// 添加额外列,用来判断整个(左)连接记录是否为空 private void AppendNullColumn(System.Reflection.MemberInfo member, string alias) { TypeRuntimeInfo typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo(member.DeclaringType); var foreignKey = typeRuntime.GetInvokerAttribute <ForeignKeyAttribute>(member.Name); string keyName = foreignKey.OuterKeys[0]; _builder.Append("CASE WHEN "); _builder.AppendMember(alias, keyName); _builder.Append(" IS NULL THEN NULL ELSE "); _builder.AppendMember(alias, keyName); _builder.Append(" END"); // 选择字段 string newName = AddColumn(_columns, Constant.NAVIGATIONSPLITONNAME); //_builder.Append(caseWhen); _builder.AppendAs(newName); _builder.Append(','); _builder.AppendNewLine(); }
// 访问导航属性 protected virtual string VisitNavMember(Expression expression, string memberName = null) { // 表达式 => b.Client.Address.AddressName Expression node = expression; Stack <KeyValuePair <string, MemberExpression> > stack = null; string alias = string.Empty; while (node != null && node.Acceptable()) { if (node.NodeType != ExpressionType.MemberAccess) { break; } if (stack == null) { stack = new Stack <KeyValuePair <string, MemberExpression> >(); } MemberExpression memberExpression = node as MemberExpression; TypeRuntimeInfo typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo(memberExpression.Expression.Type); ForeignKeyAttribute attribute = typeRuntime.GetInvokerAttribute <ForeignKeyAttribute>(memberExpression.Member.Name); if (attribute == null) { break; } string key = memberExpression.GetKeyWidthoutAnonymous(); stack.Push(new KeyValuePair <string, MemberExpression>(key, memberExpression)); node = memberExpression.Expression; if (node.NodeType == ExpressionType.Call) { node = (node as MethodCallExpression).Object; } } if (stack != null && stack.Count > 0) { while (stack != null && stack.Count > 0) { KeyValuePair <string, MemberExpression> kvp = stack.Pop(); string key = kvp.Key; MemberExpression m = kvp.Value; Type type = m.Type; if (type.IsGenericType) { type = type.GetGenericArguments()[0]; } TypeRuntimeInfo typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo(type); // 检查查询表达式是否显示指定该表关联 alias = _aliases.GetJoinTableAlias(typeRuntime.TableName); if (string.IsNullOrEmpty(alias)) { // 如果没有,则使用导航属性别名 alias = _aliases.GetNavigationTableAlias(key); if (!_navMembers.ContainsKey(kvp.Key)) { _navMembers.Add(kvp); } } // 例: a.Client.ClientId if (stack.Count == 0 && !string.IsNullOrEmpty(memberName)) { _builder.AppendMember(alias, memberName); } } } else { // => SelectMany 也会产生类似 'b.Client.Address.AddressName' 这样的表达式 alias = _aliases.GetTableAlias(expression); _builder.AppendMember(alias, memberName); } // fix issue# Join 表达式显式指定导航属性时时,alias 为空 return(alias); }
// 遍历 Include 包含的导航属性 private void VisitInclude() { if (_include == null || _include.Count == 0) { return; } foreach (var dbExpression in _include) { Expression exp = dbExpression.Expressions[0]; if (exp == null) { continue; } if (exp.NodeType == ExpressionType.Lambda) { exp = (exp as LambdaExpression).Body; } MemberExpression memberExpression = exp as MemberExpression; if (memberExpression == null) { throw new XFrameworkException("Include expression body must be 'MemberExpression'."); } // 例:Include(a => a.Client.AccountList[0].Client) // 解析导航属性链 List <Expression> chain = new List <Expression>(); while (memberExpression != null) { // a.Client 要求 <Client> 必须标明 ForeignKeyAttribute TypeRuntimeInfo typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo(memberExpression.Expression.Type); ForeignKeyAttribute attribute = typeRuntime.GetInvokerAttribute <ForeignKeyAttribute>(memberExpression.Member.Name); if (attribute == null) { throw new XFrameworkException("Include member {{{0}}} must mark 'ForeignKeyAttribute'.", memberExpression); } MemberExpression m = null; chain.Add(memberExpression); if (memberExpression.Expression.NodeType == ExpressionType.MemberAccess) { m = (MemberExpression)memberExpression.Expression; } else if (memberExpression.Expression.NodeType == ExpressionType.Call) { m = (memberExpression.Expression as MethodCallExpression).Object as MemberExpression; } //var m = memberExpression.Expression as MemberExpression; if (m == null) { chain.Add(memberExpression.Expression); } memberExpression = m; } // 生成导航属性描述信息 string keyName = string.Empty; for (int i = chain.Count - 1; i >= 0; i--) { Expression expression = chain[i]; memberExpression = expression as MemberExpression; if (memberExpression == null) { continue; } //{ // keyName = expression.Type.Name; // continue; //} //keyName = keyName + "." + memberExpression.Member.Name; keyName = memberExpression.GetKeyWidthoutAnonymous(true); if (!_navigations.ContainsKey(keyName)) { // fix issue# XC 列占一个位 Navigation descriptor = new Navigation(keyName, memberExpression.Member); descriptor.Start = i == 0 ? _columns.Count : -1; //_columns.Count; descriptor.FieldCount = i == 0 ? (GetFieldCount(exp) + 1) : -1; //i == 0 ? (GetFieldCount(exp) + 1) : 1;//-1; _navigations.Add(keyName, descriptor); } } this.VisitNavigation(memberExpression, true); } }
// {new App() {Id = p.Id}} private Expression VisitMemberInitImpl(MemberInitExpression node, bool topBinding) { // 如果有一对多的导航属性会产生嵌套的SQL,这时需要强制主表选择的列里面必须包含导航外键 // TODO #对 Bindings 进行排序,保证导航属性的赋值一定要最后面# // 未实现,在书写表达式时人工保证 ## if (node.NewExpression != null) { this.VisitNewImpl(node.NewExpression); } if (_navChainHopper.Count == 0) { _navChainHopper.Add(node.Type.Name); } for (int i = 0; i < node.Bindings.Count; i++) { MemberAssignment binding = node.Bindings[i] as MemberAssignment; if (binding == null) { throw new XFrameworkException("Only 'MemberAssignment' binding supported."); } Type propertyType = (node.Bindings[i].Member as System.Reflection.PropertyInfo).PropertyType; bool isNavigation = !TypeUtils.IsPrimitiveType(propertyType); #region 一般属性 // 非导航属性 if (!isNavigation) { if (binding.Expression.CanEvaluate()) { _builder.Append(binding.Expression.Evaluate().Value, binding.Member, node.Type); } else { base.VisitMemberBinding(binding); } // 选择字段 string newName = AddColumn(_columns, binding.Member.Name); // 添加字段别名 _builder.AppendAs(newName); _builder.Append(','); _builder.AppendNewLine(); } #endregion #region 导航属性 else { // 非显式指定的导航属性需要有 ForeignKeyAttribute if (binding.Expression.NodeType == ExpressionType.MemberAccess && binding.Expression.Acceptable()) { TypeRuntimeInfo typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo(binding.Member.DeclaringType); var attribute = typeRuntime.GetInvokerAttribute <ForeignKeyAttribute>(binding.Member.Name); if (attribute == null) { throw new XFrameworkException("Complex property {{{0}}} must mark 'ForeignKeyAttribute' ", binding.Member.Name); } } // 生成导航属性描述集合,以类名.属性名做为键值 int n = _navChainHopper.Count; string keyName = _navChainHopper.Count > 0 ? _navChainHopper[_navChainHopper.Count - 1] : string.Empty; keyName = !string.IsNullOrEmpty(keyName) ? keyName + "." + binding.Member.Name : binding.Member.Name; Navigation descriptor = new Navigation(keyName, binding.Member); if (!_navigations.ContainsKey(keyName)) { // fix issue# XC 列占一个位 descriptor.Start = _columns.Count; descriptor.FieldCount = GetFieldCount(binding.Expression) + (binding.Expression.NodeType == ExpressionType.MemberAccess && binding.Expression.Acceptable() ? 1 : 0); _navigations.Add(keyName, descriptor); _navChainHopper.Add(keyName); } // 1.不显式指定导航属性,例:a.Client.ClientList // 2.表达式里显式指定导航属性,例:b if (binding.Expression.NodeType == ExpressionType.MemberAccess) { this.VisitNavigation(binding.Expression as MemberExpression, binding.Expression.Acceptable()); } else if (binding.Expression.NodeType == ExpressionType.New) { this.VisitNewImpl(binding.Expression as NewExpression); } else if (binding.Expression.NodeType == ExpressionType.MemberInit) { this.VisitMemberInitImpl(binding.Expression as MemberInitExpression, false); } // 恢复访问链 // 在访问导航属性时可能是 Client.CloudServer,这时要恢复为 Client,以保证能访问 Client 的下一个导航属性 if (_navChainHopper.Count != n) { _navChainHopper.RemoveAt(_navChainHopper.Count - 1); } } #endregion base._visitedMember.Clear(); } return(node); }
// 添加导航属性关联 protected override void AppendNavigation() { if (this.NavMembers == null || this.NavMembers.Count == 0) { return; } // 如果有一对多的导航属性,肯定会产生嵌套查询。那么内层查询别名肯定是t0,所以需要清掉 if (this.HaveListNavigation) { _aliases = new TableAliasCache(_aliases.ObviousAlias); } //开始产生 USING 子句 ISqlBuilder jf = this.JoinFragment; int index = -1; // 未生成USING子句 if (_aliases.ObviousAlias <= 1) { jf.AppendNewLine(); jf.Append(_keywordName); } else { jf.Append(','); jf.AppendNewLine(); } foreach (var kvp in this.NavMembers) { index++; string key = kvp.Key; MemberExpression m = kvp.Value; TypeRuntimeInfo typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo(m.Expression.Type); ForeignKeyAttribute attribute = typeRuntime.GetInvokerAttribute <ForeignKeyAttribute>(m.Member.Name); string innerKey = string.Empty; string outerKey = key; string innerAlias = string.Empty; if (!m.Expression.Acceptable()) { innerKey = m.Expression.NodeType == ExpressionType.Parameter ? (m.Expression as ParameterExpression).Name : (m.Expression as MemberExpression).Member.Name; } else { MemberExpression mLeft = null; if (m.Expression.NodeType == ExpressionType.MemberAccess) { mLeft = m.Expression as MemberExpression; } else if (m.Expression.NodeType == ExpressionType.Call) { mLeft = (m.Expression as MethodCallExpression).Object as MemberExpression; } string name = TypeRuntimeInfoCache.GetRuntimeInfo(mLeft.Type).TableName; innerAlias = _aliases.GetJoinTableAlias(name); if (string.IsNullOrEmpty(innerAlias)) { string keyLeft = mLeft.GetKeyWidthoutAnonymous(); if (this.NavMembers.ContainsKey(keyLeft)) { innerKey = keyLeft; } } } string alias1 = !string.IsNullOrEmpty(innerAlias) ? innerAlias : _aliases.GetTableAlias(innerKey); string alias2 = _aliases.GetTableAlias(outerKey); // 补充与USING字符串同等间距的空白 if (_aliases.ObviousAlias > 1 || index > 0) { jf.Append(" "); } Type type = m.Type; var typeRumtime2 = TypeRuntimeInfoCache.GetRuntimeInfo(type); if (type.IsGenericType) { type = type.GetGenericArguments()[0]; } jf.Append(' '); jf.AppendMember(typeRumtime2.TableName, typeRumtime2.IsTemporary); jf.Append(' '); jf.Append(alias2); if (_onPhrase.Length > 0) { _onPhrase.Append(" AND "); } for (int i = 0; i < attribute.InnerKeys.Length; i++) { _onPhrase.Append(alias1); _onPhrase.Append('.'); _onPhrase.AppendMember(attribute.InnerKeys[i]); _onPhrase.Append(" = "); _onPhrase.Append(alias2); _onPhrase.Append('.'); _onPhrase.AppendMember(attribute.OuterKeys[i]); } if (index < this.NavMembers.Count - 1) { jf.Append(','); jf.AppendNewLine(); } } }
// 添加导航属性关联 protected virtual void AppendNavigation() { if (this._navMembers == null || this._navMembers.Count == 0) { return; } // 如果有一对多的导航属性,肯定会产生嵌套查询。那么内层查询别名肯定是t0,所以需要清掉 if (this.HaveListNavigation) { _aliases = new TableAliasCache(_aliases.ObviousAlias); } //开始产生LEFT JOIN 子句 ISqlBuilder builder = this.JoinFragment; foreach (var kvp in _navMembers) { string key = kvp.Key; MemberExpression m = kvp.Value; TypeRuntimeInfo typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo(m.Expression.Type); ForeignKeyAttribute attribute = typeRuntime.GetInvokerAttribute <ForeignKeyAttribute>(m.Member.Name); string innerKey = string.Empty; string outerKey = key; string innerAlias = string.Empty; if (!m.Expression.Acceptable()) { innerKey = m.Expression.NodeType == ExpressionType.Parameter ? (m.Expression as ParameterExpression).Name : (m.Expression as MemberExpression).Member.Name; } else { MemberExpression mLeft = null; if (m.Expression.NodeType == ExpressionType.MemberAccess) { mLeft = m.Expression as MemberExpression; } else if (m.Expression.NodeType == ExpressionType.Call) { mLeft = (m.Expression as MethodCallExpression).Object as MemberExpression; } string name = TypeRuntimeInfoCache.GetRuntimeInfo(mLeft.Type).TableName; innerAlias = _aliases.GetJoinTableAlias(name); if (string.IsNullOrEmpty(innerAlias)) { string keyLeft = mLeft.GetKeyWidthoutAnonymous(); if (_navMembers.ContainsKey(keyLeft)) { innerKey = keyLeft; } } } string alias1 = !string.IsNullOrEmpty(innerAlias) ? innerAlias : _aliases.GetTableAlias(innerKey); string alias2 = _aliases.GetTableAlias(outerKey); builder.AppendNewLine(); builder.Append("LEFT JOIN "); Type type = m.Type; if (type.IsGenericType) { type = type.GetGenericArguments()[0]; } var typeRuntime2 = TypeRuntimeInfoCache.GetRuntimeInfo(type); builder.AppendMember(typeRuntime2.TableName, !typeRuntime2.IsTemporary); builder.Append(" "); builder.Append(alias2); builder.Append(" ON "); for (int i = 0; i < attribute.InnerKeys.Length; i++) { builder.Append(alias1); builder.Append('.'); builder.AppendMember(attribute.InnerKeys[i]); builder.Append(" = "); builder.Append(alias2); builder.Append('.'); builder.AppendMember(attribute.OuterKeys[i]); if (i < attribute.InnerKeys.Length - 1) { builder.Append(" AND "); } } } }