internal override DbExpression AsCqt(DbExpression row, bool skipIsNotNull) { DbExpression cqt = null; AsCql( // negatedConstantAsCql action (negated, domainValues) => { Debug.Assert(cqt == null, "unexpected construction order - cqt must be null"); cqt = negated.AsCqt(row, domainValues, RestrictedMemberSlot.MemberPath, skipIsNotNull); }, // varInDomain action (domainValues) => { Debug.Assert(cqt == null, "unexpected construction order - cqt must be null"); Debug.Assert(domainValues.Count > 0, "domain must not be empty"); cqt = RestrictedMemberSlot.MemberPath.AsCqt(row); if (domainValues.Count == 1) { // Single value cqt = cqt.Equal(domainValues.Single().AsCqt(row, RestrictedMemberSlot.MemberPath)); } else { // Multiple values: build list of var = c1, var = c2, ..., then OR them all. var operands = domainValues.Select(c => (DbExpression)cqt.Equal(c.AsCqt(row, RestrictedMemberSlot.MemberPath))).ToList(); cqt = Helpers.BuildBalancedTreeInPlace(operands, (prev, next) => prev.Or(next)); } }, // varIsNotNull action () => { // ( ... AND var IS NOT NULL) DbExpression varIsNotNull = RestrictedMemberSlot.MemberPath.AsCqt(row).IsNull().Not(); cqt = cqt != null ? cqt.And(varIsNotNull) : varIsNotNull; }, // varIsNull action () => { // (var IS NULL OR ...) DbExpression varIsNull = RestrictedMemberSlot.MemberPath.AsCqt(row).IsNull(); cqt = cqt != null ? varIsNull.Or(cqt) : varIsNull; }, skipIsNotNull); return(cqt); }
private void MapEqualsExpression(MethodCallExpression expression) { if ((expression.Arguments == null) || (expression.Arguments.Count != 1)) { throw new ApplicationException("Did not find exactly 1 Argument to Equals function"); } DbExpression srcExpression = GetDbExpressionForExpression(expression.Object); DbExpression dbExpression; if (expression.Arguments[0] is ConstantExpression) { var constantExpression = GetDbExpressionForExpression(expression.Arguments[0]) as DbConstantExpression; if ((constantExpression == null) || (constantExpression.Value == null)) { throw new NullReferenceException("Parameter to Equals cannot be null"); } dbExpression = DbExpressionBuilder.Equal(srcExpression, constantExpression); } else { var argExpression = GetDbExpressionForExpression(expression.Arguments[0]); // Note: Can also do this using StartsWith function on srcExpression (which avoids having to hardcode the % character). // It works but generates some crazy conditions using charindex which I don't think will use indexes as well as "like"... //dbExpression = DbExpressionBuilder.Equal(DbExpressionBuilder.True, srcExpression.StartsWith(argExpression)); //dbExpression = DbExpressionBuilder.Equal(srcExpression, argExpression); dbExpression = srcExpression.Equal(argExpression); } MapExpressionToDbExpression(expression, dbExpression); }
/// <summary> /// Creates an Equal comparison of a nullable property (db column) to a nullable parameter (lambda param) /// that adds the necessary "is null" checks to support a filter like "e.TenantId = tenantId". /// Results in sql: (e.TenantID is null and @tenantID is null) or (e.TenantID is not null and e.TenantID = @tenantID) /// which will support parmeter values that are "null" or a specific value and will correctly filter on columns that /// are "null" or a specific value. /// </summary> /// <param name="propExpression"></param> /// <param name="paramExpression"></param> /// <returns></returns> private DbExpression CreateEqualComparisonOfNullablePropToNullableParam(DbExpression propExpression, DbExpression paramExpression) { var condition1 = propExpression.IsNull().And(paramExpression.IsNull()); var condition2 = propExpression.IsNull().Not().And(propExpression.Equal(paramExpression)); return(condition1.Or(condition2)); }
internal override DbExpression AsCqt(DbExpression row, bool skipIsNotNull) { DbExpression cqt = (DbExpression)null; this.AsCql((Action <NegatedConstant, IEnumerable <Constant> >)((negated, domainValues) => cqt = negated.AsCqt(row, domainValues, this.RestrictedMemberSlot.MemberPath, skipIsNotNull)), (Action <Set <Constant> >)(domainValues => { cqt = this.RestrictedMemberSlot.MemberPath.AsCqt(row); if (domainValues.Count == 1) { cqt = (DbExpression)cqt.Equal(domainValues.Single <Constant>().AsCqt(row, this.RestrictedMemberSlot.MemberPath)); } else { cqt = Helpers.BuildBalancedTreeInPlace <DbExpression>((IList <DbExpression>)domainValues.Select <Constant, DbExpression>((Func <Constant, DbExpression>)(c => (DbExpression)cqt.Equal(c.AsCqt(row, this.RestrictedMemberSlot.MemberPath)))).ToList <DbExpression>(), (Func <DbExpression, DbExpression, DbExpression>)((prev, next) => (DbExpression)prev.Or(next))); } }), (Action)(() => { DbExpression right = (DbExpression)this.RestrictedMemberSlot.MemberPath.AsCqt(row).IsNull().Not(); cqt = cqt != null ? (DbExpression)cqt.And(right) : right; }), (Action)(() => { DbExpression left = (DbExpression)this.RestrictedMemberSlot.MemberPath.AsCqt(row).IsNull(); cqt = cqt != null ? (DbExpression)left.Or(cqt) : left; }), skipIsNotNull); return(cqt); }
ComplexObjectModel GenComplexObjectModel(ComplexPropertyDescriptor navigationDescriptor, NavigationNode navigationNode, QueryModel queryModel) { TypeDescriptor navigationTypeDescriptor = EntityTypeContainer.GetDescriptor(navigationDescriptor.PropertyType); DbTable dbTable = navigationTypeDescriptor.Table; DbTableExpression tableExp = new DbTableExpression(dbTable); string alias = queryModel.GenerateUniqueTableAlias(dbTable.Name); DbTableSegment joinTableSeg = new DbTableSegment(tableExp, alias, queryModel.FromTable.Table.Lock); DbTable aliasTable = new DbTable(alias); ComplexObjectModel navigationObjectModel = navigationTypeDescriptor.GenObjectModel(aliasTable); navigationObjectModel.NullChecking = navigationObjectModel.PrimaryKey; PrimitivePropertyDescriptor foreignKeyPropertyDescriptor = navigationDescriptor.ForeignKeyProperty; DbExpression foreignKeyColumn = this.GetPrimitiveMember(foreignKeyPropertyDescriptor.Property); DbExpression joinCondition = DbExpression.Equal(foreignKeyColumn, navigationObjectModel.PrimaryKey); DbJoinTableExpression joinTableExp = new DbJoinTableExpression(foreignKeyPropertyDescriptor.IsNullable ? DbJoinType.LeftJoin : DbJoinType.InnerJoin, joinTableSeg, joinCondition); this.DependentTable.JoinTables.Add(joinTableExp); navigationObjectModel.DependentTable = joinTableExp; DbExpression condition = this.ParseCondition(navigationNode.Condition, navigationObjectModel, queryModel.ScopeTables); //AndWhere的条件放到join条件里去 joinTableExp.AppendCondition(condition); navigationObjectModel.Filter = this.ParseCondition(navigationNode.Filter, navigationObjectModel, queryModel.ScopeTables); //queryModel.Filters.Add(navigationObjectModel.Filter); return(navigationObjectModel); }
// Effects: given a "clause" in the form of a property/value pair, produces an equality expression. If the // value is null, creates an IsNull expression // Requires: all arguments are set private DbExpression GenerateEqualityExpression(DbExpressionBinding target, EdmProperty property, PropagatorResult value) { Debug.Assert(null != target && null != property && null != value); DbExpression propertyExpression = GeneratePropertyExpression(target, property); DbExpression valueExpression = GenerateValueExpression(property, value); if (valueExpression.ExpressionKind == DbExpressionKind.Null) { return(propertyExpression.IsNull()); } return(propertyExpression.Equal(valueExpression)); }
private DbExpression GeneratePredicate(StorageConditionPropertyMapping condition, DbExpression row) { Debug.Assert(condition.EdmProperty == null, "C-side conditions are not supported in function mappings."); DbExpression columnRef = GenerateColumnRef(row, condition.ColumnProperty); if (condition.IsNull.HasValue) { return(condition.IsNull.Value ? (DbExpression)columnRef.IsNull() : (DbExpression)columnRef.IsNull().Not()); } else { return(columnRef.Equal(columnRef.ResultType.Constant(condition.Value))); } }
private DbExpression GenerateEqualityExpression( DbExpressionBinding target, EdmProperty property, PropagatorResult value) { DbExpression propertyExpression = UpdateCompiler.GeneratePropertyExpression(target, property); DbExpression valueExpression = this.GenerateValueExpression(property, value); if (valueExpression.ExpressionKind == DbExpressionKind.Null) { return((DbExpression)propertyExpression.IsNull()); } return((DbExpression)propertyExpression.Equal(valueExpression)); }
private static DbExpression GeneratePredicate( ConditionPropertyMapping condition, DbExpression row) { DbExpression columnRef = FunctionImportMappingComposable.GenerateColumnRef(row, condition.Column); if (!condition.IsNull.HasValue) { return((DbExpression)columnRef.Equal((DbExpression)columnRef.ResultType.Constant(condition.Value))); } if (!condition.IsNull.Value) { return((DbExpression)columnRef.IsNull().Not()); } return((DbExpression)columnRef.IsNull()); }
ComplexObjectModel GenCollectionElementObjectModel(TypeDescriptor elementTypeDescriptor, NavigationNode navigationNode, QueryModel queryModel) { DbTable dbTable = elementTypeDescriptor.Table; DbTableExpression tableExp = new DbTableExpression(dbTable); string alias = queryModel.GenerateUniqueTableAlias(dbTable.Name); DbTableSegment joinTableSeg = new DbTableSegment(tableExp, alias, queryModel.FromTable.Table.Lock); DbTable aliasTable = new DbTable(alias); ComplexObjectModel elementObjectModel = elementTypeDescriptor.GenObjectModel(aliasTable); elementObjectModel.NullChecking = elementObjectModel.PrimaryKey; ComplexPropertyDescriptor navigationDescriptor = elementTypeDescriptor.ComplexPropertyDescriptors.Where(a => a.PropertyType == this.ObjectType).FirstOrDefault(); if (navigationDescriptor == null) { throw new ChloeException($"You have to define a navigation property which type is '{this.ObjectType.FullName}' on class '{elementTypeDescriptor.Definition.Type.FullName}'."); } DbExpression elementForeignKeyColumn = elementObjectModel.GetPrimitiveMember(navigationDescriptor.ForeignKeyProperty.Property); DbExpression joinCondition = DbExpression.Equal(this.PrimaryKey, elementForeignKeyColumn); DbJoinTableExpression joinTableExp = new DbJoinTableExpression(DbJoinType.LeftJoin, joinTableSeg, joinCondition); this.DependentTable.JoinTables.Add(joinTableExp); elementObjectModel.DependentTable = joinTableExp; var condition = this.ParseCondition(navigationNode.Condition, elementObjectModel, queryModel.ScopeTables); //AndWhere的条件放到join条件里去 joinTableExp.AppendCondition(condition); elementObjectModel.Filter = this.ParseCondition(navigationNode.Filter, elementObjectModel, queryModel.ScopeTables); bool orderByPrimaryKeyExists = queryModel.Orderings.Where(a => a.Expression == this.PrimaryKey).Any(); if (!orderByPrimaryKeyExists) { //结果集分组 DbOrdering ordering = new DbOrdering(this.PrimaryKey, DbOrderType.Asc); queryModel.Orderings.Add(ordering); } //queryModel.Filters.Add(elementObjectModel.Filter); return(elementObjectModel); }
public void Process(DbMethodCallExpression exp, SqlGenerator generator) { DbExpression e = exp.Arguments.First(); DbEqualExpression equalNullExpression = DbExpression.Equal(e, DbExpression.Constant(null, UtilConstants.TypeOfString)); DbEqualExpression equalEmptyExpression = DbExpression.Equal(e, DbExpression.Constant(string.Empty)); DbOrExpression orExpression = DbExpression.Or(equalNullExpression, equalEmptyExpression); DbCaseWhenExpression.WhenThenExpressionPair whenThenPair = new DbCaseWhenExpression.WhenThenExpressionPair(orExpression, DbConstantExpression.One); List<DbCaseWhenExpression.WhenThenExpressionPair> whenThenExps = new List<DbCaseWhenExpression.WhenThenExpressionPair>(1); whenThenExps.Add(whenThenPair); DbCaseWhenExpression caseWhenExpression = DbExpression.CaseWhen(whenThenExps, DbConstantExpression.Zero, UtilConstants.TypeOfBoolean); var eqExp = DbExpression.Equal(caseWhenExpression, DbConstantExpression.One); eqExp.Accept(generator); }
public void Process(DbMethodCallExpression exp, SqlGenerator generator) { MethodInfo method = exp.Method; if (method.DeclaringType == UtilConstants.TypeOfSql) { Method_Sql_Equals(exp, generator); return; } DbExpression right = exp.Arguments[0]; if (right.Type != exp.Object.Type) { right = DbExpression.Convert(right, exp.Object.Type); } DbExpression.Equal(exp.Object, right).Accept(generator); }
protected override Expression VisitBinary(BinaryExpression node) { var expression = base.VisitBinary(node) as BinaryExpression; DbExpression dbExpression; // Need special handling for comparisons against the null constant. If we don't translate these // using an "IsNull" expression, EF will convert it literally as "= null" which doesn't work in SQL Server. if (IsNullConstantExpression(expression.Right)) { dbExpression = MapNullComparison(expression.Left, expression.NodeType); } else if (IsNullConstantExpression(expression.Left)) { dbExpression = MapNullComparison(expression.Right, expression.NodeType); } else { DbExpression leftExpression = GetDbExpressionForExpression(expression.Left); DbExpression rightExpression = GetDbExpressionForExpression(expression.Right); switch (expression.NodeType) { case ExpressionType.Equal: // DbPropertyExpression = class property that has been mapped to a database column // DbParameterReferenceExpression = lambda parameter if (IsNullableExpressionOfType <DbPropertyExpression>(leftExpression) && IsNullableExpressionOfType <DbParameterReferenceExpression>(rightExpression)) { dbExpression = CreateEqualComparisonOfNullablePropToNullableParam(leftExpression, rightExpression); } else if (IsNullableExpressionOfType <DbPropertyExpression>(rightExpression) && IsNullableExpressionOfType <DbParameterReferenceExpression>(leftExpression)) { dbExpression = CreateEqualComparisonOfNullablePropToNullableParam(rightExpression, leftExpression); } else { dbExpression = leftExpression.Equal(rightExpression); } break; case ExpressionType.NotEqual: dbExpression = leftExpression.NotEqual(rightExpression); break; case ExpressionType.GreaterThan: dbExpression = leftExpression.GreaterThan(rightExpression); break; case ExpressionType.GreaterThanOrEqual: dbExpression = leftExpression.GreaterThanOrEqual(rightExpression); break; case ExpressionType.LessThan: dbExpression = leftExpression.LessThan(rightExpression); break; case ExpressionType.LessThanOrEqual: dbExpression = leftExpression.LessThanOrEqual(rightExpression); break; case ExpressionType.AndAlso: dbExpression = leftExpression.And(rightExpression); break; case ExpressionType.OrElse: dbExpression = leftExpression.Or(rightExpression); break; default: throw new NotImplementedException(string.Format("Unhandled NodeType of {0} in LambdaToDbExpressionVisitor.VisitBinary", expression.NodeType)); } } MapExpressionToDbExpression(expression, dbExpression); return(expression); }
private void MapContainsExpression(MethodCallExpression expression) { DbExpression argExpression = GetDbExpressionForExpression(expression.Arguments[0]); DbExpression dbExpression; var collectionObjExp = expression.Object as ParameterExpression; if (collectionObjExp != null) { // collectionObjExp is a parameter expression. This means the content of the collection is // dynamic. DbInExpression only supports a fixed size list of constant values. // So the only way to handle a dynamic collection is for us to create a single Equals expression // with a DbParameterReference. Then when we intercept that parameter, we will see that it's // for a collection and we will modify the SQL to change it from an "=" to an "in". The single // Parameter Reference is set to the first value in the collection and the rest of the values // are inserted into the SQL "in" clause. string paramName = collectionObjExp.Name; Type paramType = PrimitiveTypeForType(collectionObjExp.Type); var param = CreateParameter(paramName, paramType); dbExpression = argExpression.Equal(param); } else { var listExpression = expression.Object as ListInitExpression; if (listExpression == null) { throw new NotSupportedException(string.Format("Unsupported object type used in Contains() - type = {0}", expression.Object.GetType().Name)); } // This is a fixed size list that may contain parameter references or constant values. // This can be handled using either a DbInExpression (if all are constants) or with // a series of OR conditions. // Find all of the constant & parameter expressions. var constantExpressionList = listExpression.Initializers .Select(i => i.Arguments.FirstOrDefault() as ConstantExpression) .Where(c => (c != null) && (c.Value != null)) // null not supported - can only use DbConstant in "In" expression .Select(c => CreateConstantExpression(c.Value)) .ToList(); constantExpressionList.AddRange(listExpression.Initializers .Select(i => i.Arguments.FirstOrDefault() as UnaryExpression) .Where(c => (c != null) && (c.Operand is ConstantExpression)) .Select(c => CreateConstantExpression(((ConstantExpression)c.Operand).Value))); var parameterExpressionList = listExpression.Initializers .Select(i => i.Arguments.FirstOrDefault() as ParameterExpression) .Where(c => c != null) .Select(c => CreateParameter(c.Name, c.Type)) .ToList(); if (constantExpressionList.Count + parameterExpressionList.Count != listExpression.Initializers.Count) { throw new NotSupportedException(string.Format("Unrecognized parameters in Contains list. Null parameters not supported.")); } if (parameterExpressionList.Any()) { // Have parameters so need to build a series of OR conditions so that we can include the // DbParameterReferences. EF will optimize this into an "in" condition but with our // DbParameterReferences preserved (which is not possible with a DbInExpression). // The DbParameterReferences will be intercepted as any other parameter. dbExpression = null; var allExpressions = parameterExpressionList.Cast <DbExpression>().Union(constantExpressionList.Cast <DbExpression>()); foreach (var paramReference in allExpressions) { var equalsExpression = argExpression.Equal(paramReference); if (dbExpression == null) { dbExpression = equalsExpression; } else { dbExpression = dbExpression.Or(equalsExpression); } } } else { // All values are constants so can use DbInExpression dbExpression = argExpression.In(constantExpressionList); } } MapExpressionToDbExpression(expression, dbExpression); }
// Generate an equality expression where the values of the left and right operands are completely unknown private DbExpression ImplementEqualityUnknownArguments(DbExpression left, DbExpression right, EqualsPattern pattern) { switch (pattern) { case EqualsPattern.Store: // left EQ right return left.Equal(right); case EqualsPattern.PositiveNullEqualityNonComposable: // for Joins return left.Equal(right).Or(left.IsNull().And(right.IsNull())); case EqualsPattern.PositiveNullEqualityComposable: { var bothNotNull = left.Equal(right); var bothNull = left.IsNull().And(right.IsNull()); if (!_funcletizer.RootContext.ContextOptions.UseCSharpNullComparisonBehavior) { return bothNotNull.Or(bothNull); // same as EqualsPattern.PositiveNullEqualityNonComposable } // add more logic to avoid undefined result for true clr semantics, ensuring composability // (left EQ right AND NOT (left IS NULL OR right IS NULL)) OR (left IS NULL AND right IS NULL) var anyOneIsNull = left.IsNull().Or(right.IsNull()); return (bothNotNull.And(anyOneIsNull.Not())).Or(bothNull); } default: Debug.Fail("unexpected pattern"); return null; } }
// For comparisons, where the left and right side are nullable or not nullable, // here are the (compositionally safe) null equality predicates: // -- x NOT NULL, y NULL // x = y AND NOT (y IS NULL) // -- x NULL, y NULL // (x = y AND (NOT (x IS NULL OR y IS NULL))) OR (x IS NULL AND y IS NULL) // -- x NOT NULL, y NOT NULL // x = y // -- x NULL, y NOT NULL // x = y AND NOT (x IS NULL) private DbExpression ImplementEquality(DbExpression left, DbExpression right, EqualsPattern pattern) { switch (left.ExpressionKind) { case DbExpressionKind.Constant: switch (right.ExpressionKind) { case DbExpressionKind.Constant: // constant EQ constant return left.Equal(right); case DbExpressionKind.Null: // null EQ constant --> false return DbExpressionBuilder.False; default: return ImplementEqualityConstantAndUnknown((DbConstantExpression)left, right, pattern); } case DbExpressionKind.Null: switch (right.ExpressionKind) { case DbExpressionKind.Constant: // null EQ constant --> false return DbExpressionBuilder.False; case DbExpressionKind.Null: // null EQ null --> true return DbExpressionBuilder.True; default: // null EQ right --> right IS NULL return right.IsNull(); } default: // unknown switch (right.ExpressionKind) { case DbExpressionKind.Constant: return ImplementEqualityConstantAndUnknown((DbConstantExpression)right, left, pattern); case DbExpressionKind.Null: // left EQ null --> left IS NULL return left.IsNull(); default: return ImplementEqualityUnknownArguments(left, right, pattern); } } }
static void StringConcat(DbBinaryExpression exp, SqlGeneratorBase generator) { List <DbExpression> operands = new List <DbExpression>(); operands.Add(exp.Right); DbExpression left = exp.Left; DbAddExpression e = null; while ((e = (left as DbAddExpression)) != null && (e.Method == PublicConstants.MethodInfo_String_Concat_String_String || e.Method == PublicConstants.MethodInfo_String_Concat_Object_Object)) { operands.Add(e.Right); left = e.Left; } operands.Add(left); DbExpression whenExp = null; List <DbExpression> operandExps = new List <DbExpression>(operands.Count); for (int i = operands.Count - 1; i >= 0; i--) { DbExpression operand = operands[i]; DbExpression opBody = operand; if (opBody.Type != PublicConstants.TypeOfString) { // 需要 cast type opBody = DbExpression.Convert(opBody, PublicConstants.TypeOfString); } DbExpression equalNullExp = DbExpression.Equal(opBody, PublicConstants.DbConstant_Null_String); if (whenExp == null) { whenExp = equalNullExp; } else { whenExp = DbExpression.And(whenExp, equalNullExp); } operandExps.Add(opBody); } generator.SqlBuilder.Append("CASE", " WHEN "); whenExp.Accept(generator); generator.SqlBuilder.Append(" THEN "); DbConstantExpression.Null.Accept(generator); generator.SqlBuilder.Append(" ELSE "); generator.SqlBuilder.Append("("); for (int i = 0; i < operandExps.Count; i++) { if (i > 0) { generator.SqlBuilder.Append(" + "); } generator.SqlBuilder.Append("ISNULL("); operandExps[i].Accept(generator); generator.SqlBuilder.Append(","); DbConstantExpression.StringEmpty.Accept(generator); generator.SqlBuilder.Append(")"); } generator.SqlBuilder.Append(")"); generator.SqlBuilder.Append(" END"); }