Mercurial > pub > bltoolkit
view Source/Data/Linq/Builder/TableBuilder.cs @ 9:1e85f66cf767 default tip
update bltoolkit
author | nickolay |
---|---|
date | Thu, 05 Apr 2018 20:53:26 +0300 |
parents | 8f65451dc28f |
children |
line wrap: on
line source
using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; using System.Linq.Expressions; using System.Reflection; namespace BLToolkit.Data.Linq.Builder { using BLToolkit.Linq; using Data.Sql; using Mapping; using Reflection; using Reflection.Extension; class TableBuilder : ISequenceBuilder { #region TableBuilder int ISequenceBuilder.BuildCounter { get; set; } static T Find<T>(ExpressionBuilder builder, BuildInfo buildInfo, Func<int,IBuildContext,T> action) { var expression = buildInfo.Expression; switch (expression.NodeType) { case ExpressionType.Constant: { var c = (ConstantExpression)expression; if (c.Value is IQueryable) return action(1, null); break; } case ExpressionType.Call: { var mc = (MethodCallExpression)expression; if (mc.Method.Name == "GetTable") if (expression.Type.IsGenericType && expression.Type.GetGenericTypeDefinition() == typeof(Table<>)) return action(2, null); var attr = builder.GetTableFunctionAttribute(mc.Method); if (attr != null) return action(5, null); break; } case ExpressionType.MemberAccess: if (expression.Type.IsGenericType && expression.Type.GetGenericTypeDefinition() == typeof(Table<>)) return action(3, null); // Looking for association. // if (buildInfo.IsSubQuery && buildInfo.SqlQuery.From.Tables.Count == 0) { var ctx = builder.GetContext(buildInfo.Parent, expression); if (ctx != null) return action(4, ctx); } break; case ExpressionType.Parameter: { if (buildInfo.IsSubQuery && buildInfo.SqlQuery.From.Tables.Count == 0) { var ctx = builder.GetContext(buildInfo.Parent, expression); if (ctx != null) return action(4, ctx); } break; } } return action(0, null); } public bool CanBuild(ExpressionBuilder builder, BuildInfo buildInfo) { return Find(builder, buildInfo, (n,_) => n > 0); } public IBuildContext BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) { return Find(builder, buildInfo, (n,ctx) => { switch (n) { case 0 : return null; case 1 : return new TableContext(builder, buildInfo, ((IQueryable)((ConstantExpression)buildInfo.Expression).Value).ElementType); case 2 : case 3 : return new TableContext(builder, buildInfo, buildInfo.Expression.Type.GetGenericArguments()[0]); case 4 : return ctx.GetContext(buildInfo.Expression, 0, buildInfo); case 5 : return new TableContext(builder, buildInfo); } throw new InvalidOperationException(); }); } public SequenceConvertInfo Convert(ExpressionBuilder builder, BuildInfo buildInfo, ParameterExpression param) { return null; } public bool IsSequence(ExpressionBuilder builder, BuildInfo buildInfo) { return true; } #endregion #region TableContext public class TableContext : IBuildContext { #region Properties #if DEBUG public string _sqlQueryText { get { return SqlQuery == null ? "" : SqlQuery.SqlText; } } #endif public ExpressionBuilder Builder { get; private set; } public Expression Expression { get; private set; } public SqlQuery SqlQuery { get; set; } public virtual IBuildContext Parent { get; set; } public Type OriginalType; public Type ObjectType; public ObjectMapper ObjectMapper; public SqlTable SqlTable; #endregion #region Init public TableContext(ExpressionBuilder builder, BuildInfo buildInfo, Type originalType) { Builder = builder; Parent = buildInfo.Parent; Expression = buildInfo.Expression; SqlQuery = buildInfo.SqlQuery; OriginalType = originalType; ObjectType = GetObjectType(); SqlTable = new SqlTable(builder.MappingSchema, ObjectType); ObjectMapper = Builder.MappingSchema.GetObjectMapper(ObjectType); SqlQuery.From.Table(SqlTable); Init(); } protected TableContext(ExpressionBuilder builder, SqlQuery sqlQuery) { Builder = builder; SqlQuery = sqlQuery; } public TableContext(ExpressionBuilder builder, BuildInfo buildInfo) { Builder = builder; Parent = buildInfo.Parent; Expression = buildInfo.Expression; SqlQuery = buildInfo.SqlQuery; var mc = (MethodCallExpression)Expression; var attr = builder.GetTableFunctionAttribute(mc.Method); if (!mc.Method.ReturnType.IsGenericType || mc.Method.ReturnType.GetGenericTypeDefinition() != typeof(Table<>)) throw new LinqException("Table function has to return Table<T>."); OriginalType = mc.Method.ReturnType.GetGenericArguments()[0]; ObjectType = GetObjectType(); SqlTable = new SqlTable(builder.MappingSchema, ObjectType); ObjectMapper = Builder.MappingSchema.GetObjectMapper(ObjectType); SqlQuery.From.Table(SqlTable); var args = mc.Arguments.Select(a => builder.ConvertToSql(this, a, false)); attr.SetTable(SqlTable, mc.Method, mc.Arguments, args); Init(); } protected Type GetObjectType() { for (var type = OriginalType.BaseType; type != null && type != typeof(object); type = type.BaseType) { var extension = TypeExtension.GetTypeExtension(type, Builder.MappingSchema.Extensions); var mapping = Builder.MappingSchema.MetadataProvider.GetInheritanceMapping(type, extension); if (mapping.Length > 0) return type; } return OriginalType; } public List<InheritanceMappingAttribute> InheritanceMapping; public List<string> InheritanceDiscriminators; protected void Init() { Builder.Contexts.Add(this); InheritanceMapping = ObjectMapper.InheritanceMapping; if (InheritanceMapping.Count > 0) InheritanceDiscriminators = GetInheritanceDiscriminators(Builder, SqlTable, ObjectType, InheritanceMapping); // Original table is a parent. // if (ObjectType != OriginalType) { var predicate = Builder.MakeIsPredicate(this, OriginalType); if (predicate.GetType() != typeof(SqlQuery.Predicate.Expr)) SqlQuery.Where.SearchCondition.Conditions.Add(new SqlQuery.Condition(false, predicate)); } } internal static List<string> GetInheritanceDiscriminators( ExpressionBuilder builder, SqlTable sqlTable, Type objectType, List<InheritanceMappingAttribute> inheritanceMapping) { var inheritanceDiscriminators = new List<string>(inheritanceMapping.Count); foreach (var mapping in inheritanceMapping) { string discriminator = null; foreach (MemberMapper mm in builder.MappingSchema.GetObjectMapper(mapping.Type)) { if (mm.MapMemberInfo.SqlIgnore == false && !sqlTable.Fields.Any(f => f.Value.Name == mm.MemberName)) { var field = new SqlField(mm.Type, mm.MemberName, mm.Name, mm.MapMemberInfo.Nullable, int.MinValue, null, mm); sqlTable.Fields.Add(field); } if (mm.MapMemberInfo.IsInheritanceDiscriminator) discriminator = mm.MapMemberInfo.MemberName; } inheritanceDiscriminators.Add(discriminator); } var dname = inheritanceDiscriminators.FirstOrDefault(s => s != null); if (dname == null) throw new LinqException("Inheritance Discriminator is not defined for the '{0}' hierarchy.", objectType); for (var i = 0; i < inheritanceDiscriminators.Count; i++) if (inheritanceDiscriminators[i] == null) inheritanceDiscriminators[i] = dname; return inheritanceDiscriminators; } #endregion #region BuildQuery class MappingData { public MappingSchema MappingSchema; public ObjectMapper ObjectMapper; public int[] Index; public IValueMapper[] ValueMappers; } static object MapDataReaderToObject1(IDataReader dataReader, MappingData data) { var source = data.MappingSchema.CreateDataReaderMapper(dataReader); var destObject = data.ObjectMapper.CreateInstance(); if (data.ValueMappers == null) { var mappers = new IValueMapper[data.Index.Length]; for (var i = 0; i < data.Index.Length; i++) { var n = data.Index[i]; if (n < 0) continue; if (!data.ObjectMapper.SupportsTypedValues(i)) { mappers[i] = data.MappingSchema.DefaultValueMapper; continue; } var sourceType = source. GetFieldType(n) ?? typeof(object); var destType = data.ObjectMapper.GetFieldType(i) ?? typeof(object); IValueMapper t; if (sourceType == destType) { lock (data.MappingSchema.SameTypeMappers) if (!data.MappingSchema.SameTypeMappers.TryGetValue(sourceType, out t)) data.MappingSchema.SameTypeMappers.Add(sourceType, t = data.MappingSchema.GetValueMapper(sourceType, destType)); } else { var key = new KeyValuePair<Type,Type>(sourceType, destType); lock (data.MappingSchema.DifferentTypeMappers) if (!data.MappingSchema.DifferentTypeMappers.TryGetValue(key, out t)) data.MappingSchema.DifferentTypeMappers.Add(key, t = data.MappingSchema.GetValueMapper(sourceType, destType)); } mappers[i] = t; } data.ValueMappers = mappers; } var dest = data.ObjectMapper; var idx = data.Index; var ms = data.ValueMappers; for (var i = 0; i < idx.Length; i++) { var n = idx[i]; if (n >= 0) ms[i].Map(source, dataReader, n, dest, destObject, i); } return destObject; } static object MapDataReaderToObject2(IDataReader dataReader, MappingData data) { var source = data.MappingSchema.CreateDataReaderMapper(dataReader); var initContext = new InitContext { MappingSchema = data.MappingSchema, DataSource = source, SourceObject = dataReader, ObjectMapper = data.ObjectMapper, MappingIndex = data.Index }; var destObject = data.ObjectMapper.CreateInstance(initContext); if (initContext.StopMapping) return destObject; var smDest = destObject as ISupportMapping; if (smDest != null) { smDest.BeginMapping(initContext); if (initContext.StopMapping) return destObject; } if (data.ValueMappers == null) { var mappers = new IValueMapper[data.Index.Length]; for (var i = 0; i < data.Index.Length; i++) { var n = data.Index[i]; if (n < 0) continue; if (!data.ObjectMapper.SupportsTypedValues(i)) { mappers[i] = data.MappingSchema.DefaultValueMapper; continue; } var sourceType = source. GetFieldType(n) ?? typeof(object); var destType = data.ObjectMapper.GetFieldType(i) ?? typeof(object); IValueMapper t; if (sourceType == destType) { lock (data.MappingSchema.SameTypeMappers) if (!data.MappingSchema.SameTypeMappers.TryGetValue(sourceType, out t)) data.MappingSchema.SameTypeMappers.Add(sourceType, t = data.MappingSchema.GetValueMapper(sourceType, destType)); } else { var key = new KeyValuePair<Type,Type>(sourceType, destType); lock (data.MappingSchema.DifferentTypeMappers) if (!data.MappingSchema.DifferentTypeMappers.TryGetValue(key, out t)) data.MappingSchema.DifferentTypeMappers.Add(key, t = data.MappingSchema.GetValueMapper(sourceType, destType)); } mappers[i] = t; } data.ValueMappers = mappers; } var dest = data.ObjectMapper; var idx = data.Index; var ms = data.ValueMappers; for (var i = 0; i < idx.Length; i++) { var n = idx[i]; if (n >= 0) ms[i].Map(source, dataReader, n, dest, destObject, i); } if (smDest != null) smDest.EndMapping(initContext); return destObject; } static object DefaultInheritanceMappingException(object value, Type type) { throw new LinqException("Inheritance mapping is not defined for discriminator value '{0}' in the '{1}' hierarchy.", value, type); } static readonly MethodInfo _mapperMethod1 = ReflectionHelper.Expressor<object>.MethodExpressor(_ => MapDataReaderToObject1(null, null)); static readonly MethodInfo _mapperMethod2 = ReflectionHelper.Expressor<object>.MethodExpressor(_ => MapDataReaderToObject2(null, null)); #if FW4 || SILVERLIGHT ParameterExpression _variable; static int _varIndex; #endif Expression BuildTableExpression(bool buildBlock, Type objectType, int[] index) { #if FW4 || SILVERLIGHT if (buildBlock && _variable != null) return _variable; #endif var data = new MappingData { MappingSchema = Builder.MappingSchema, ObjectMapper = Builder.MappingSchema.GetObjectMapper(objectType), Index = index }; Expression expr; if (Builder.DataContextInfo.DataContext == null || TypeHelper.IsSameOrParent(typeof(ISupportMapping), objectType) || TypeHelper.GetFirstAttribute(objectType, typeof(ObjectFactoryAttribute)) != null) { expr = Expression.Convert( Expression.Call(null, _mapperMethod2, ExpressionBuilder.DataReaderParam, Expression.Constant(data)), objectType); } else { expr = Expression.Convert( Expression.Call(null, _mapperMethod1, ExpressionBuilder.DataReaderParam, Expression.Constant(data)), objectType); } expr = ProcessExpression(expr); #if FW4 || SILVERLIGHT if (!buildBlock) return expr; Builder.BlockVariables. Add(_variable = Expression.Variable(expr.Type, expr.Type.Name + _varIndex++)); Builder.BlockExpressions.Add(Expression.Assign(_variable, expr)); return _variable; #else return expr; #endif } protected virtual Expression ProcessExpression(Expression expression) { return expression; } int[] BuildIndex(int[] index, Type objectType) { var names = new Dictionary<string,int>(); var n = 0; foreach (MemberMapper mm in Builder.MappingSchema.GetObjectMapper(objectType)) if (mm.MapMemberInfo.SqlIgnore == false) names.Add(mm.MemberName, n++); var q = from r in SqlTable.Fields.Values.Select((f,i) => new { f, i }) where names.ContainsKey(r.f.Name) orderby names[r.f.Name] select index[r.i]; return q.ToArray(); } Expression BuildQuery(Type tableType) { SqlInfo[] info; if (ObjectType == tableType) { info = ConvertToIndex(null, 0, ConvertFlags.All); } else { info = ConvertToSql(null, 0, ConvertFlags.All); var table = new SqlTable(Builder.MappingSchema, tableType); var q = from fld1 in table.Fields.Values.Select((f,i) => new { f, i }) join fld2 in info on fld1.f.Name equals ((SqlField)fld2.Sql).Name orderby fld1.i select GetIndex(fld2); info = q.ToArray(); } var index = info.Select(idx => ConvertToParentIndex(idx.Index, null)).ToArray(); if (ObjectType != tableType || InheritanceMapping.Count == 0) return BuildTableExpression(!Builder.IsBlockDisable, tableType, index); Expression expr; var defaultMapping = InheritanceMapping.SingleOrDefault(m => m.IsDefault); if (defaultMapping != null) { expr = Expression.Convert( BuildTableExpression(false, defaultMapping.Type, BuildIndex(index, defaultMapping.Type)), ObjectType); } else { var exceptionMethod = ReflectionHelper.Expressor<object>.MethodExpressor(_ => DefaultInheritanceMappingException(null, null)); var dindex = ( from f in SqlTable.Fields.Values where f.Name == InheritanceDiscriminators[0] select ConvertToParentIndex(_indexes[f].Index, null) ).First(); expr = Expression.Convert( Expression.Call(null, exceptionMethod, Expression.Call( ExpressionBuilder.DataReaderParam, ReflectionHelper.DataReader.GetValue, Expression.Constant(dindex)), Expression.Constant(ObjectType)), ObjectType); } foreach (var mapping in InheritanceMapping.Select((m,i) => new { m, i }).Where(m => m.m != defaultMapping)) { var dindex = ( from f in SqlTable.Fields.Values where f.Name == InheritanceDiscriminators[mapping.i] select ConvertToParentIndex(_indexes[f].Index, null) ).First(); Expression testExpr; if (mapping.m.Code == null) { testExpr = Expression.Call( ExpressionBuilder.DataReaderParam, ReflectionHelper.DataReader.IsDBNull, Expression.Constant(dindex)); } else { var codeType = mapping.m.Code.GetType(); testExpr = Expression.Equal( Expression.Constant(mapping.m.Code), Builder.BuildSql(codeType, dindex)); } expr = Expression.Condition( testExpr, Expression.Convert(BuildTableExpression(false, mapping.m.Type, BuildIndex(index, mapping.m.Type)), ObjectType), expr); } return expr; } public void BuildQuery<T>(Query<T> query, ParameterExpression queryParameter) { var expr = BuildQuery(typeof(T)); var mapper = Builder.BuildMapper<T>(expr); query.SetQuery(mapper.Compile()); } #endregion #region BuildExpression public Expression BuildExpression(Expression expression, int level) { // Build table. // var table = FindTable(expression, level, false); if (table == null) { if (expression is MemberExpression) { var memberExpression = (MemberExpression)expression; if (ObjectMapper != null && ObjectMapper.TypeAccessor.OriginalType == memberExpression.Member.DeclaringType) { throw new LinqException("Member '{0}.{1}' is not a table column.", memberExpression.Member.Name, memberExpression.Member.Name); } } throw new InvalidOperationException(); } if (table.Field == null) return table.Table.BuildQuery(table.Table.OriginalType); // Build field. // var info = ConvertToIndex(expression, level, ConvertFlags.Field).Single(); var idx = ConvertToParentIndex(info.Index, null); if (expression is MemberExpression) { var me = (MemberExpression)expression; var memberAccessor = TypeAccessor.GetAccessor(me.Member.DeclaringType)[me.Member.Name]; return Builder.BuildSql(memberAccessor, idx); } else { return Builder.BuildSql(expression.Type, idx); } } #endregion #region ConvertToSql public SqlInfo[] ConvertToSql(Expression expression, int level, ConvertFlags flags) { switch (flags) { case ConvertFlags.All : { var table = FindTable(expression, level, false); if (table.Field == null) return table.Table.SqlTable.Fields.Values .Select(_ => new SqlInfo(_.MemberMapper.MemberAccessor.MemberInfo) { Sql = _ }) .ToArray(); break; } case ConvertFlags.Key : { var table = FindTable(expression, level, false); if (table.Field == null) { var q = from f in table.Table.SqlTable.Fields.Values where f.IsPrimaryKey orderby f.PrimaryKeyOrder select new SqlInfo(f.MemberMapper.MemberAccessor.MemberInfo) { Sql = f }; var key = q.ToArray(); return key.Length != 0 ? key : ConvertToSql(expression, level, ConvertFlags.All); } break; } case ConvertFlags.Field : { var table = FindTable(expression, level, true); if (table.Field != null) return new[] { new SqlInfo(table.Field.MemberMapper.MemberAccessor.MemberInfo) { Sql = table.Field } }; break; } } throw new InvalidOperationException(); } #endregion #region ConvertToIndex readonly Dictionary<ISqlExpression,SqlInfo> _indexes = new Dictionary<ISqlExpression,SqlInfo>(); protected SqlInfo GetIndex(SqlInfo expr) { SqlInfo n; if (_indexes.TryGetValue(expr.Sql, out n)) return n; if (expr.Sql is SqlField) { var field = (SqlField)expr.Sql; expr.Index = SqlQuery.Select.Add(field, field.Alias); } else { expr.Index = SqlQuery.Select.Add(expr.Sql); } expr.Query = SqlQuery; _indexes.Add(expr.Sql, expr); return expr; } public SqlInfo[] ConvertToIndex(Expression expression, int level, ConvertFlags flags) { switch (flags) { case ConvertFlags.Field : case ConvertFlags.Key : case ConvertFlags.All : var info = ConvertToSql(expression, level, flags); for (var i = 0; i < info.Length; i++) info[i] = GetIndex(info[i]); return info; } throw new InvalidOperationException(); } #endregion #region IsExpression public IsExpressionResult IsExpression(Expression expression, int level, RequestFor requestFor) { switch (requestFor) { case RequestFor.Field : { var table = FindTable(expression, level, false); return new IsExpressionResult(table != null && table.Field != null); } case RequestFor.Table : case RequestFor.Object : { var table = FindTable(expression, level, false); var isTable = table != null && table.Field == null && (expression == null || expression.GetLevelExpression(table.Level) == expression); return new IsExpressionResult(isTable, isTable ? table.Table : null); } case RequestFor.Expression : { if (expression == null) return IsExpressionResult.False; var levelExpression = expression.GetLevelExpression(level); switch (levelExpression.NodeType) { case ExpressionType.MemberAccess : case ExpressionType.Parameter : case ExpressionType.Call : var table = FindTable(expression, level, false); return new IsExpressionResult(table == null); } return IsExpressionResult.True; } case RequestFor.Association : { if (ObjectMapper.Associations.Count > 0) { var table = FindTable(expression, level, false); var isat = table != null && table.Table is AssociatedTableContext && table.Field == null && (expression == null || expression.GetLevelExpression(table.Level) == expression); return new IsExpressionResult(isat, isat ? table.Table : null); } return IsExpressionResult.False; } } return IsExpressionResult.False; } #endregion #region GetContext interface IAssociationHelper { Expression GetExpression(Expression parent, AssociatedTableContext association); } class AssociationHelper<T> : IAssociationHelper where T : class { public Expression GetExpression(Expression parent, AssociatedTableContext association) { Expression expr = null; var param = Expression.Parameter(typeof(T), "c"); foreach (var cond in (association).ParentAssociationJoin.Condition.Conditions) { var p = (SqlQuery.Predicate.ExprExpr)cond.Predicate; var e1 = Expression.MakeMemberAccess(parent, ((SqlField)p.Expr1).MemberMapper.MemberAccessor.MemberInfo); var e2 = Expression.MakeMemberAccess(param, ((SqlField)p.Expr2).MemberMapper.MemberAccessor.MemberInfo) as Expression; while (e1.Type != e2.Type) { if (TypeHelper.IsNullableType(e1.Type)) { e1 = Expression.PropertyOrField(e1, "Value"); continue; } if (TypeHelper.IsNullableType(e2.Type)) { e2 = Expression.PropertyOrField(e2, "Value"); continue; } e2 = Expression.Convert(e2, e1.Type); } var ex = Expression.Equal(e1, e2); expr = expr == null ? ex : Expression.AndAlso(expr, ex); } var predicate = Expression.Lambda<Func<T,bool>>(expr, param); return association.Builder.DataContextInfo.DataContext.GetTable<T>().Where(predicate).Expression; } } public IBuildContext GetContext(Expression expression, int level, BuildInfo buildInfo) { if (expression == null) { if (buildInfo != null && buildInfo.IsSubQuery) { var table = new TableContext( Builder, new BuildInfo(Parent is SelectManyBuilder.SelectManyContext ? this : Parent, Expression, buildInfo.SqlQuery), SqlTable.ObjectType); return table; } return this; } if (ObjectMapper.Associations.Count > 0) { var levelExpression = expression.GetLevelExpression(level); if (buildInfo != null && buildInfo.IsSubQuery) { if (levelExpression == expression && expression.NodeType == ExpressionType.MemberAccess) { var tableLevel = GetAssociation(expression, level); var association = (AssociatedTableContext)tableLevel.Table; if (association.IsList) { var ma = (MemberExpression)buildInfo.Expression; var atype = typeof(AssociationHelper<>).MakeGenericType(association.ObjectType); var helper = (IAssociationHelper)Activator.CreateInstance(atype); var expr = helper.GetExpression(ma.Expression, association); buildInfo.IsAssociationBuilt = true; if (tableLevel.IsNew || buildInfo.CopyTable) association.ParentAssociationJoin.IsWeak = true; return Builder.BuildSequence(new BuildInfo(buildInfo, expr)); } } else { var association = GetAssociation(levelExpression, level); var paj = ((AssociatedTableContext)association.Table).ParentAssociationJoin; paj.IsWeak = paj.IsWeak && buildInfo.CopyTable; return association.Table.GetContext(expression, level + 1, buildInfo); } } } throw new InvalidOperationException(); } #endregion #region ConvertToParentIndex public int ConvertToParentIndex(int index, IBuildContext context) { return Parent == null ? index : Parent.ConvertToParentIndex(index, this); } #endregion #region SetAlias public void SetAlias(string alias) { if (alias.Contains('<')) return; if (SqlTable.Alias == null) SqlTable.Alias = alias; } #endregion #region GetSubQuery public ISqlExpression GetSubQuery(IBuildContext context) { return null; } #endregion #region Helpers SqlField GetField(Expression expression, int level, bool throwException) { if (expression.NodeType == ExpressionType.MemberAccess) { var memberExpression = (MemberExpression)expression; var levelExpression = expression.GetLevelExpression(level); if (levelExpression.NodeType == ExpressionType.MemberAccess) { if (levelExpression != expression) { var levelMember = (MemberExpression)levelExpression; if (TypeHelper.IsNullableValueMember(memberExpression.Member) && memberExpression.Expression == levelExpression) memberExpression = levelMember; else { var sameType = levelMember.Member.ReflectedType == SqlTable.ObjectType || levelMember.Member.DeclaringType == SqlTable.ObjectType; if (!sameType) { var mi = SqlTable.ObjectType.GetMember(levelMember.Member.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); sameType = mi.Any(_ => _.DeclaringType == levelMember.Member.DeclaringType); } if (sameType || InheritanceMapping.Count > 0) { foreach (var field in SqlTable.Fields.Values) { if (field.MemberMapper is MemberMapper.ComplexMapper) { var name = levelMember.Member.Name; for (var ex = (MemberExpression)expression; ex != levelMember; ex = (MemberExpression)ex.Expression) name += "." + ex.Member.Name; if (field.MemberMapper.MemberName == name) return field; } } } } } if (levelExpression == memberExpression) { foreach (var field in SqlTable.Fields.Values) { if (TypeHelper.Equals(field.MemberMapper.MapMemberInfo.MemberAccessor.MemberInfo, memberExpression.Member, SqlTable.ObjectType)) { if (field.MemberMapper is MemberMapper.ComplexMapper && field.MemberMapper.MemberName.IndexOf('.') > 0) { var name = memberExpression.Member.Name; var me = memberExpression; if (!(me.Expression is MemberExpression)) return null; while (me.Expression is MemberExpression) { me = (MemberExpression)me.Expression; name = me.Member.Name + '.' + name; } return SqlTable.Fields.Values.FirstOrDefault(f => f.MemberMapper.MemberName == name); } return field; } if (InheritanceMapping.Count > 0 && field.Name == memberExpression.Member.Name) foreach (var mapping in InheritanceMapping) foreach (MemberMapper mm in Builder.MappingSchema.GetObjectMapper(mapping.Type)) if (TypeHelper.Equals(mm.MapMemberInfo.MemberAccessor.MemberInfo, memberExpression.Member)) return field; } if (throwException && ObjectMapper != null && ObjectMapper.TypeAccessor.OriginalType == memberExpression.Member.DeclaringType) { throw new LinqException("Member '{0}.{1}' is not a table column.", memberExpression.Member.DeclaringType.Name, memberExpression.Member.Name); } } } } return null; } [JetBrains.Annotations.NotNull] readonly Dictionary<MemberInfo,AssociatedTableContext> _associations = new Dictionary<MemberInfo,AssociatedTableContext>(new MemberInfoComparer()); class TableLevel { public TableContext Table; public SqlField Field; public int Level; public bool IsNew; } TableLevel FindTable(Expression expression, int level, bool throwException) { if (expression == null) return new TableLevel { Table = this }; var levelExpression = expression.GetLevelExpression(level); switch (levelExpression.NodeType) { case ExpressionType.MemberAccess : case ExpressionType.Parameter : { var field = GetField(expression, level, throwException); if (field != null || (level == 0 && levelExpression == expression)) return new TableLevel { Table = this, Field = field, Level = level }; return GetAssociation(expression, level); } } return null; } TableLevel GetAssociation(Expression expression, int level) { var objectMapper = ObjectMapper; var levelExpression = expression.GetLevelExpression(level); var inheritance = ( from m in InheritanceMapping let om = Builder.MappingSchema.GetObjectMapper(m.Type) where om.Associations.Count > 0 select om ).ToList(); if (objectMapper.Associations.Count > 0 || inheritance.Count > 0) { if (levelExpression.NodeType == ExpressionType.MemberAccess) { var memberExpression = (MemberExpression)levelExpression; var isNew = false; AssociatedTableContext tableAssociation; if (!_associations.TryGetValue(memberExpression.Member, out tableAssociation)) { var q = from a in objectMapper.Associations.Concat(inheritance.SelectMany(om => om.Associations)) where TypeHelper.Equals(a.MemberAccessor.MemberInfo, memberExpression.Member) select new AssociatedTableContext(Builder, this, a) { Parent = Parent }; tableAssociation = q.FirstOrDefault(); isNew = true; _associations.Add(memberExpression.Member, tableAssociation); } if (tableAssociation != null) { if (levelExpression == expression) return new TableLevel { Table = tableAssociation, Level = level, IsNew = isNew }; var al = tableAssociation.GetAssociation(expression, level + 1); if (al != null) return al; var field = tableAssociation.GetField(expression, level + 1, false); return new TableLevel { Table = tableAssociation, Field = field, Level = field == null ? level : level + 1, IsNew = isNew }; } } } return null; } #endregion } #endregion #region AssociatedTableContext public class AssociatedTableContext : TableContext { public readonly TableContext ParentAssociation; public readonly SqlQuery.JoinedTable ParentAssociationJoin; public readonly Association Association; public readonly bool IsList; public override IBuildContext Parent { get { return ParentAssociation.Parent; } set { } } public AssociatedTableContext(ExpressionBuilder builder, TableContext parent, Association association) : base(builder, parent.SqlQuery) { var type = TypeHelper.GetMemberType(association.MemberAccessor.MemberInfo); var left = association.CanBeNull; if (TypeHelper.IsSameOrParent(typeof(IEnumerable), type)) { var etypes = TypeHelper.GetGenericArguments(type, typeof(IEnumerable)); type = etypes != null && etypes.Length > 0 ? etypes[0] : TypeHelper.GetListItemType(type); IsList = true; } OriginalType = type; ObjectType = GetObjectType(); ObjectMapper = Builder.MappingSchema.GetObjectMapper(ObjectType); SqlTable = new SqlTable(builder.MappingSchema, ObjectType); var psrc = parent.SqlQuery.From[parent.SqlTable]; var join = left ? SqlTable.WeakLeftJoin() : IsList ? SqlTable.InnerJoin() : SqlTable.WeakInnerJoin(); Association = association; ParentAssociation = parent; ParentAssociationJoin = join.JoinedTable; psrc.Joins.Add(join.JoinedTable); for (var i = 0; i < association.ThisKey.Length; i++) { SqlField field1; SqlField field2; if (!parent.SqlTable.Fields.TryGetValue(association.ThisKey[i], out field1)) throw new LinqException("Association key '{0}' not found for type '{1}.", association.ThisKey[i], parent.ObjectType); if (!SqlTable.Fields.TryGetValue(association.OtherKey[i], out field2)) throw new LinqException("Association key '{0}' not found for type '{1}.", association.OtherKey[i], ObjectType); join.Field(field1).Equal.Field(field2); } Init(); } protected override Expression ProcessExpression(Expression expression) { var isLeft = false; for ( var association = this; isLeft == false && association != null; association = association.ParentAssociation as AssociatedTableContext) { isLeft = association.ParentAssociationJoin.JoinType == SqlQuery.JoinType.Left || association.ParentAssociationJoin.JoinType == SqlQuery.JoinType.OuterApply; } if (isLeft) { Expression cond = null; var keys = ConvertToIndex(null, 0, ConvertFlags.Key); foreach (var key in keys) { var index2 = ConvertToParentIndex(key.Index, null); Expression e = Expression.Call( ExpressionBuilder.DataReaderParam, ReflectionHelper.DataReader.IsDBNull, Expression.Constant(index2)); cond = cond == null ? e : Expression.AndAlso(cond, e); } expression = Expression.Condition(cond, Expression.Constant(null, expression.Type), expression); } return expression; } } #endregion } }