private DbExpression BuildPredicate( DbExpressionBinding target, PropagatorResult referenceRow, PropagatorResult current, TableChangeProcessor processor, ref bool rowMustBeTouched) { Dictionary <EdmProperty, PropagatorResult> dictionary = new Dictionary <EdmProperty, PropagatorResult>(); int ordinal = 0; foreach (EdmProperty property in processor.Table.ElementType.Properties) { PropagatorResult memberValue = referenceRow.GetMemberValue(ordinal); PropagatorResult input = current == null ? (PropagatorResult)null : current.GetMemberValue(ordinal); if (!rowMustBeTouched && (UpdateCompiler.HasFlag(memberValue, PropagatorFlags.ConcurrencyValue) || UpdateCompiler.HasFlag(input, PropagatorFlags.ConcurrencyValue))) { rowMustBeTouched = true; } if (!dictionary.ContainsKey(property) && (UpdateCompiler.HasFlag(memberValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key) || UpdateCompiler.HasFlag(input, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key))) { dictionary.Add(property, memberValue); } ++ordinal; } DbExpression left = (DbExpression)null; foreach (KeyValuePair <EdmProperty, PropagatorResult> keyValuePair in dictionary) { DbExpression equalityExpression = this.GenerateEqualityExpression(target, keyValuePair.Key, keyValuePair.Value); left = left != null ? (DbExpression)left.And(equalityExpression) : equalityExpression; } return(left); }
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); }
internal DbExpression AsCqt(DbExpression row, IEnumerable <Constant> constants, MemberPath outputMember, bool skipIsNotNull) { DbExpression cqt = null; AsCql( // trueLiteral action () => cqt = DbExpressionBuilder.True, // varIsNotNull action () => cqt = outputMember.AsCqt(row).IsNull().Not(), // varNotEqualsTo action (constant) => { DbExpression notEqualsExpr = outputMember.AsCqt(row).NotEqual(constant.AsCqt(row, outputMember)); if (cqt != null) { cqt = cqt.And(notEqualsExpr); } else { cqt = notEqualsExpr; } }, constants, outputMember, skipIsNotNull); return(cqt); }
public static DbExpression And(this DbExpression left, List <DbExpression> right) { for (int i = 0; i < right.Count; i++) { left = left.And(right[i]); } return(left); }
/// <summary> /// Determines predicate used to identify a row in a table. /// </summary> /// <remarks> /// Columns are included in the list when: /// <list> /// <item>They are keys for the table</item> /// <item>They are concurrency values</item> /// </list> /// </remarks> /// <param name="target"> Expression binding representing the table containing the row </param> /// <param name="referenceRow"> Values for the row being located. </param> /// <param name="current"> Values being updated (may be null). </param> /// <param name="processor"> Context for the table containing the row. </param> /// <param name="rowMustBeTouched"> Output parameter indicating whether a row must be touched (whether it's being modified or not) because it contains a concurrency value </param> /// <returns> Column/value pairs. </returns> private DbExpression BuildPredicate( DbExpressionBinding target, PropagatorResult referenceRow, PropagatorResult current, TableChangeProcessor processor, ref bool rowMustBeTouched) { var whereClauses = new Dictionary <EdmProperty, PropagatorResult>(); // add all concurrency tokens (note that keys are always concurrency tokens as well) var propertyOrdinal = 0; foreach (var member in processor.Table.ElementType.Properties) { // members and result values are ordinally aligned var expectedValue = referenceRow.GetMemberValue(propertyOrdinal); var newValue = null == current ? null : current.GetMemberValue(propertyOrdinal); // check if the rowMustBeTouched value should be set to true (if it isn't already // true and we've come across a concurrency value) if (!rowMustBeTouched && (HasFlag(expectedValue, PropagatorFlags.ConcurrencyValue) || HasFlag(newValue, PropagatorFlags.ConcurrencyValue))) { rowMustBeTouched = true; } // determine if this is a concurrency value if (!whereClauses.ContainsKey(member) && // don't add to the set clause twice (HasFlag(expectedValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key) || HasFlag(newValue, PropagatorFlags.ConcurrencyValue | PropagatorFlags.Key))) // tagged as concurrency value { whereClauses.Add(member, expectedValue); } propertyOrdinal++; } // Build a binary AND expression tree from the clauses DbExpression predicate = null; foreach (var clause in whereClauses) { var clauseExpression = GenerateEqualityExpression(target, clause.Key, clause.Value); if (null == predicate) { predicate = clauseExpression; } else { predicate = predicate.And(clauseExpression); } } Debug.Assert(null != predicate, "some predicate term must exist"); return(predicate); }
/// <summary> /// Generates CQT representation of the slot. /// </summary> internal DbExpression AsCqt(DbExpression row) { DbExpression cqt = m_slotValue.AsCqt(row, m_outputMember); if (m_enforceNotNull) { cqt = cqt.And(cqt.IsNull().Not()); } return(cqt); }
internal DbExpression AsCqt(DbExpression row) { DbExpression left = this.m_slotValue.AsCqt(row, this.m_outputMember); if (this.m_enforceNotNull) { left = (DbExpression)left.And((DbExpression)left.IsNull().Not()); } return(left); }
internal DbExpression AsCqt(DbExpression leftRow, DbExpression rightRow) { DbExpression left = this.m_singleClauses[0].AsCqt(leftRow, rightRow); for (int index = 1; index < this.m_singleClauses.Count; ++index) { left = (DbExpression)left.And(this.m_singleClauses[index].AsCqt(leftRow, rightRow)); } return(left); }
/// <summary> /// Generates CQT of the form "LeftSlot1 = RightSlot1 AND LeftSlot2 = RightSlot2 AND ... /// </summary> internal DbExpression AsCqt(DbExpression leftRow, DbExpression rightRow) { DbExpression cqt = m_singleClauses[0].AsCqt(leftRow, rightRow); for (int i = 1; i < m_singleClauses.Count; ++i) { cqt = cqt.And(m_singleClauses[i].AsCqt(leftRow, rightRow)); } return(cqt); }
public override DbExpression Visit(DbEqualExpression exp) { DbExpression left = exp.Left; DbExpression right = exp.Right; left = DbExpressionExtension.StripInvalidConvert(left); right = DbExpressionExtension.StripInvalidConvert(right); MethodInfo method_Sql_Equals = PublicConstants.MethodInfo_Sql_Equals.MakeGenericMethod(left.Type); /* Sql.Equals(left, right) */ DbMethodCallExpression left_equals_right = DbExpression.MethodCall(null, method_Sql_Equals, new List <DbExpression>(2) { left, right }); if (right.NodeType == DbExpressionType.Parameter || right.NodeType == DbExpressionType.Constant || left.NodeType == DbExpressionType.Parameter || left.NodeType == DbExpressionType.Constant || right.NodeType == DbExpressionType.SubQuery || left.NodeType == DbExpressionType.SubQuery || !left.Type.CanNull() || !right.Type.CanNull()) { /* * a.Name == name --> a.Name == name * a.Id == (select top 1 T.Id from T) --> a.Id == (select top 1 T.Id from T) * 对于上述查询,我们不考虑 null */ left_equals_right.Accept(this); return(exp); } /* * a.Name == a.XName --> a.Name == a.XName or (a.Name is null and a.XName is null) */ /* Sql.Equals(left, null) */ var left_is_null = DbExpression.MethodCall(null, method_Sql_Equals, new List <DbExpression>(2) { left, DbExpression.Constant(null, left.Type) }); /* Sql.Equals(right, null) */ var right_is_null = DbExpression.MethodCall(null, method_Sql_Equals, new List <DbExpression>(2) { right, DbExpression.Constant(null, right.Type) }); /* Sql.Equals(left, null) && Sql.Equals(right, null) */ var left_is_null_and_right_is_null = DbExpression.And(left_is_null, right_is_null); /* Sql.Equals(left, right) || (Sql.Equals(left, null) && Sql.Equals(right, null)) */ var left_equals_right_or_left_is_null_and_right_is_null = DbExpression.Or(left_equals_right, left_is_null_and_right_is_null); left_equals_right_or_left_is_null_and_right_is_null.Accept(this); return(exp); }
public override DbExpression Visit(DbAndExpression expression) { DbExpression left = this.VisitExpression(expression.Left); DbExpression right = this.VisitExpression(expression.Right); if (left != null && right != null) { return((DbExpression)left.And(right)); } return(left ?? right); }
static void AppendLimitCondition(DbSqlQueryExpression sqlQuery, int limitCount) { DbLessThanExpression lessThanExp = DbExpression.LessThan(OracleSemantics.DbMemberExpression_ROWNUM, DbExpression.Constant(limitCount + 1)); DbExpression condition = lessThanExp; if (sqlQuery.Condition != null) { condition = DbExpression.And(sqlQuery.Condition, condition); } sqlQuery.Condition = condition; }
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); }
public static DbExpression MakeCondition(PairList <PrimitivePropertyDescriptor, object> propertyValuePairs, DbTable dbTable) { DbExpression conditionExp = null; foreach (var pair in propertyValuePairs) { PrimitivePropertyDescriptor propertyDescriptor = pair.Item1; object val = pair.Item2; DbExpression left = new DbColumnAccessExpression(dbTable, propertyDescriptor.Column); DbExpression right = DbExpression.Parameter(val, propertyDescriptor.PropertyType, propertyDescriptor.Column.DbType); DbExpression equalExp = new DbEqualExpression(left, right); conditionExp = conditionExp.And(equalExp); } return(conditionExp); }
internal DbExpression AsCqt( DbExpression row, IEnumerable <Constant> constants, MemberPath outputMember, bool skipIsNotNull) { DbExpression cqt = (DbExpression)null; this.AsCql((Action)(() => cqt = (DbExpression)DbExpressionBuilder.True), (Action)(() => cqt = (DbExpression)outputMember.AsCqt(row).IsNull().Not()), (Action <Constant>)(constant => { DbExpression right = (DbExpression)outputMember.AsCqt(row).NotEqual(constant.AsCqt(row, outputMember)); if (cqt != null) { cqt = (DbExpression)cqt.And(right); } else { cqt = right; } }), constants, outputMember, skipIsNotNull); return(cqt); }
static DbExpression MakeCondition(Dictionary <PropertyDescriptor, object> keyValueMap, DbTable dbTable) { DbExpression conditionExp = null; foreach (var kv in keyValueMap) { PropertyDescriptor keyPropertyDescriptor = kv.Key; object keyVal = kv.Value; if (keyVal == null) { throw new ArgumentException(string.Format("The primary key '{0}' could not be null.", keyPropertyDescriptor.Property.Name)); } DbExpression left = new DbColumnAccessExpression(dbTable, keyPropertyDescriptor.Column); DbExpression right = DbExpression.Parameter(keyVal, keyPropertyDescriptor.PropertyType, keyPropertyDescriptor.Column.DbType); DbExpression equalExp = new DbEqualExpression(left, right); conditionExp = conditionExp == null ? equalExp : DbExpression.And(conditionExp, equalExp); } return(conditionExp); }
/// <summary> /// Helper method creating the correct filter expression based on the supplied parameters /// </summary> private DbFilterExpression BuildFilterExpression(DbExpressionBinding binding, DbExpression predicate, string column) { _injectedDynamicFilter = true; var variableReference = binding.VariableType.Variable(binding.VariableName); // Create the property based on the variable in order to apply the equality var tenantProperty = variableReference.Property(column); // Create the parameter which is an object representation of a sql parameter. // We have to create a parameter and not perform a direct comparison with Equal function for example // as this logic is cached per query and called only once var tenantParameter = tenantProperty.Property.TypeUsage.Parameter(MultitenancyConstants.TenantIdFilterParameterName); // Apply the equality between property and parameter. DbExpression newPredicate = tenantProperty.Equal(tenantParameter); // If an existing predicate exists (normally when called from DbFilterExpression) execute a logical AND to get the result if (predicate != null) { newPredicate = newPredicate.And(predicate); } return(binding.Filter(newPredicate)); }
private DbExpression TransformIntersectOrExcept( DbExpression left, DbExpression right, DbExpressionKind expressionKind, IList <DbPropertyExpression> sortExpressionsOverLeft, string sortExpressionsBindingVariableName) { bool flag1 = expressionKind == DbExpressionKind.Except || expressionKind == DbExpressionKind.Skip; bool flag2 = expressionKind == DbExpressionKind.Except || expressionKind == DbExpressionKind.Intersect; DbExpressionBinding input = left.Bind(); DbExpressionBinding expressionBinding = right.Bind(); IList <DbPropertyExpression> propertyExpressionList1 = (IList <DbPropertyExpression>) new List <DbPropertyExpression>(); IList <DbPropertyExpression> propertyExpressionList2 = (IList <DbPropertyExpression>) new List <DbPropertyExpression>(); this.FlattenProperties((DbExpression)input.Variable, propertyExpressionList1); this.FlattenProperties((DbExpression)expressionBinding.Variable, propertyExpressionList2); if (expressionKind == DbExpressionKind.Skip && Sql8ExpressionRewriter.RemoveNonSortProperties(propertyExpressionList1, propertyExpressionList2, sortExpressionsOverLeft, input.VariableName, sortExpressionsBindingVariableName)) { expressionBinding = Sql8ExpressionRewriter.CapWithProject(expressionBinding, propertyExpressionList2); } DbExpression dbExpression1 = (DbExpression)null; for (int index = 0; index < propertyExpressionList1.Count; ++index) { DbExpression right1 = (DbExpression)propertyExpressionList1[index].Equal((DbExpression)propertyExpressionList2[index]).Or((DbExpression)propertyExpressionList1[index].IsNull().And((DbExpression)propertyExpressionList2[index].IsNull())); dbExpression1 = index != 0 ? (DbExpression)dbExpression1.And(right1) : right1; } DbExpression dbExpression2 = (DbExpression)expressionBinding.Any(dbExpression1); DbExpression predicate = !flag1 ? dbExpression2 : (DbExpression)dbExpression2.Not(); DbExpression dbExpression3 = (DbExpression)input.Filter(predicate); if (flag2) { dbExpression3 = (DbExpression)dbExpression3.Distinct(); } return(dbExpression3); }
/// <summary> /// Returns the update or query view for an Extent as a /// string. /// There are a series of steps that we go through for discovering a view for an extent. /// To start with we assume that we are working with Generated Views. To find out the /// generated view we go to the ObjectItemCollection and see if it is not-null. If the ObjectItemCollection /// is non-null, we get the view generation assemblies that it might have cached during the /// Object metadata discovery.If there are no view generation assemblies we switch to the /// runtime view generation strategy. If there are view generation assemblies, we get the list and /// go through them and see if there are any assemblies that are there from which we have not already loaded /// the views. We collect the views from assemblies that we have not already collected from earlier. /// If the ObjectItemCollection is null and we are in the view generation mode, that means that /// the query or update is issued from the Value layer and this is the first time view has been asked for. /// The compile time view gen for value layer queries will work for very simple scenarios. /// If the users wants to get the performance benefit, they should call MetadataWorkspace.LoadFromAssembly. /// At this point we go through the referenced assemblies of the entry assembly( this wont work for Asp.net /// or if the viewgen assembly was not referenced by the executing application). /// and try to see if there were any view gen assemblies. If there are, we collect the views for all extents. /// Once we have all the generated views gathered, we try to get the view for the extent passed in. /// If we find one we will return it. If we can't find one an exception will be thrown. /// If there were no view gen assemblies either in the ObjectItemCollection or in the list of referenced /// assemblies of calling assembly, we change the mode to runtime view generation and will continue to /// be in that mode for the rest of the lifetime of the mapping item collection. /// </summary> internal GeneratedView GetGeneratedView(EntitySetBase extent, MetadataWorkspace workspace, StorageMappingItemCollection storageMappingItemCollection) { //First check if we have collected a view from user-defined query views //Dont need to worry whether to generate Query view or update viw, because that is relative to the extent. GeneratedView view; if (TryGetUserDefinedQueryView(extent, out view)) { return(view); } //If this is a foreign key association, manufacture a view on the fly. if (extent.BuiltInTypeKind == BuiltInTypeKind.AssociationSet) { AssociationSet aSet = (AssociationSet)extent; if (aSet.ElementType.IsForeignKey) { if (m_config.IsViewTracing) { Helpers.StringTraceLine(String.Empty); Helpers.StringTraceLine(String.Empty); Helpers.FormatTraceLine("================= Generating FK Query View for: {0} =================", aSet.Name); Helpers.StringTraceLine(String.Empty); Helpers.StringTraceLine(String.Empty); } // Although we expose a collection of constraints in the API, there is only ever one constraint. Debug.Assert(aSet.ElementType.ReferentialConstraints.Count == 1, "aSet.ElementType.ReferentialConstraints.Count == 1"); ReferentialConstraint rc = aSet.ElementType.ReferentialConstraints.Single(); EntitySet dependentSet = aSet.AssociationSetEnds[rc.ToRole.Name].EntitySet; EntitySet principalSet = aSet.AssociationSetEnds[rc.FromRole.Name].EntitySet; DbExpression qView = dependentSet.Scan(); // Introduce an OfType view if the dependent end is a subtype of the entity set EntityType dependentType = MetadataHelper.GetEntityTypeForEnd((AssociationEndMember)rc.ToRole); EntityType principalType = MetadataHelper.GetEntityTypeForEnd((AssociationEndMember)rc.FromRole); if (dependentSet.ElementType.IsBaseTypeOf(dependentType)) { qView = qView.OfType(TypeUsage.Create(dependentType)); } if (rc.FromRole.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne) { // Filter out instances with existing relationships. qView = qView.Where(e => { DbExpression filter = null; foreach (EdmProperty fkProp in rc.ToProperties) { DbExpression notIsNull = e.Property(fkProp).IsNull().Not(); filter = null == filter ? notIsNull : filter.And(notIsNull); } return(filter); }); } qView = qView.Select(e => { List <DbExpression> ends = new List <DbExpression>(); foreach (AssociationEndMember end in aSet.ElementType.AssociationEndMembers) { if (end.Name == rc.ToRole.Name) { var keyValues = new List <KeyValuePair <string, DbExpression> >(); foreach (EdmMember keyMember in dependentSet.ElementType.KeyMembers) { keyValues.Add(e.Property((EdmProperty)keyMember)); } ends.Add(dependentSet.RefFromKey(DbExpressionBuilder.NewRow(keyValues), dependentType)); } else { // Manufacture a key using key values. var keyValues = new List <KeyValuePair <string, DbExpression> >(); foreach (EdmMember keyMember in principalSet.ElementType.KeyMembers) { int offset = rc.FromProperties.IndexOf((EdmProperty)keyMember); keyValues.Add(e.Property(rc.ToProperties[offset])); } ends.Add(principalSet.RefFromKey(DbExpressionBuilder.NewRow(keyValues), principalType)); } } return(TypeUsage.Create(aSet.ElementType).New(ends)); }); return(GeneratedView.CreateGeneratedViewForFKAssociationSet(aSet, aSet.ElementType, new DbQueryCommandTree(workspace, DataSpace.SSpace, qView), storageMappingItemCollection, m_config)); } } // If no User-defined QV is found, call memoized View Generation procedure. Dictionary <EntitySetBase, GeneratedView> generatedViews = m_generatedViewsMemoizer.Evaluate(extent.EntityContainer); if (!generatedViews.TryGetValue(extent, out view)) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Mapping_Views_For_Extent_Not_Generated( (extent.EntityContainer.DataSpace == DataSpace.SSpace)?"Table":"EntitySet", extent.Name)); } return(view); }
public override DbExpression Visit(DbNotEqualExpression exp) { DbExpression left = exp.Left; DbExpression right = exp.Right; left = DbExpressionExtension.StripInvalidConvert(left); right = DbExpressionExtension.StripInvalidConvert(right); MethodInfo method_Sql_NotEquals = PublicConstants.MethodInfo_Sql_NotEquals.MakeGenericMethod(left.Type); /* Sql.NotEquals(left, right) */ DbMethodCallExpression left_not_equals_right = DbExpression.MethodCall(null, method_Sql_NotEquals, new List <DbExpression>(2) { left, right }); //明确 left right 其中一边一定为 null if (DbExpressionExtension.AffirmExpressionRetValueIsNull(right) || DbExpressionExtension.AffirmExpressionRetValueIsNull(left)) { /* * a.Name != null --> a.Name != null */ left_not_equals_right.Accept(this); return(exp); } if (right.NodeType == DbExpressionType.SubQuery || left.NodeType == DbExpressionType.SubQuery) { /* * a.Id != (select top 1 T.Id from T) --> a.Id <> (select top 1 T.Id from T),对于这种查询,我们不考虑 null */ left_not_equals_right.Accept(this); return(exp); } MethodInfo method_Sql_Equals = PublicConstants.MethodInfo_Sql_Equals.MakeGenericMethod(left.Type); if (left.NodeType == DbExpressionType.Parameter || left.NodeType == DbExpressionType.Constant) { var t = right; right = left; left = t; } if (right.NodeType == DbExpressionType.Parameter || right.NodeType == DbExpressionType.Constant) { /* * 走到这说明 name 不可能为 null * a.Name != name --> a.Name <> name or a.Name is null */ if (left.NodeType != DbExpressionType.Parameter && left.NodeType != DbExpressionType.Constant) { /* * a.Name != name --> a.Name <> name or a.Name is null */ /* Sql.Equals(left, null) */ var left_is_null1 = DbExpression.MethodCall(null, method_Sql_Equals, new List <DbExpression>(2) { left, DbExpression.Constant(null, left.Type) }); /* Sql.NotEquals(left, right) || Sql.Equals(left, null) */ var left_not_equals_right_or_left_is_null = DbExpression.Or(left_not_equals_right, left_is_null1); left_not_equals_right_or_left_is_null.Accept(this); } else { /* * name != name1 --> name <> name,其中 name 和 name1 都为变量且都不可能为 null */ left_not_equals_right.Accept(this); } return(exp); } /* * a.Name != a.XName --> a.Name <> a.XName or (a.Name is null and a.XName is not null) or (a.Name is not null and a.XName is null) * ## a.Name != a.XName 不能翻译成:not (a.Name == a.XName or (a.Name is null and a.XName is null)),因为数据库里的 not 有时候并非真正意义上的“取反”! * 当 a.Name 或者 a.XName 其中一个字段有为 NULL,另一个字段有值时,会查不出此条数据 ## */ DbConstantExpression null_Constant = DbExpression.Constant(null, left.Type); /* Sql.Equals(left, null) */ var left_is_null = DbExpression.MethodCall(null, method_Sql_Equals, new List <DbExpression>(2) { left, null_Constant }); /* Sql.NotEquals(left, null) */ var left_is_not_null = DbExpression.MethodCall(null, method_Sql_NotEquals, new List <DbExpression>(2) { left, null_Constant }); /* Sql.Equals(right, null) */ var right_is_null = DbExpression.MethodCall(null, method_Sql_Equals, new List <DbExpression>(2) { right, null_Constant }); /* Sql.NotEquals(right, null) */ var right_is_not_null = DbExpression.MethodCall(null, method_Sql_NotEquals, new List <DbExpression>(2) { right, null_Constant }); /* Sql.Equals(left, null) && Sql.NotEquals(right, null) */ var left_is_null_and_right_is_not_null = DbExpression.And(left_is_null, right_is_not_null); /* Sql.NotEquals(left, null) && Sql.Equals(right, null) */ var left_is_not_null_and_right_is_null = DbExpression.And(left_is_not_null, right_is_null); /* (Sql.Equals(left, null) && Sql.NotEquals(right, null)) || (Sql.NotEquals(left, null) && Sql.Equals(right, null)) */ var left_is_null_and_right_is_not_null_or_left_is_not_null_and_right_is_null = DbExpression.Or(left_is_null_and_right_is_not_null, left_is_not_null_and_right_is_null); /* Sql.NotEquals(left, right) || (Sql.Equals(left, null) && Sql.NotEquals(right, null)) || (Sql.NotEquals(left, null) && Sql.Equals(right, null)) */ var e = DbExpression.Or(left_not_equals_right, left_is_null_and_right_is_not_null_or_left_is_not_null_and_right_is_null); e.Accept(this); return(exp); }
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); }
// <summary> // This method is used for translating <see cref="DbIntersectExpression" /> and <see cref="DbExceptExpression" />, // and for translating the "Except" part of <see cref="DbSkipExpression" />. // into the follwoing expression: // A INTERSECT B, A EXCEPT B // (DISTINCT) // | // FILTER // | // | - Input: A // | - Predicate:(NOT) // | // ANY // | // | - Input: B // | - Predicate: (B.b1 = A.a1 or (B.b1 is null and A.a1 is null)) // AND (B.b2 = A.a2 or (B.b2 is null and A.a2 is null)) // AND ... // AND (B.bn = A.an or (B.bn is null and A.an is null))) // Here, A corresponds to right and B to left. // (NOT) is present when transforming Except // for the purpose of translating <see cref="DbExceptExpression" /> or <see cref="DbSkipExpression" />. // (DISTINCT) is present when transforming for the purpose of translating // <see cref="DbExceptExpression" /> or <see cref="DbIntersectExpression" />. // For <see cref="DbSkipExpression" />, the input to ANY is caped with project which projects out only // the columns represented in the sortExpressionsOverLeft list and only these are used in the predicate. // This is because we want to support skip over input with non-equal comarable columns and we have no way to recognize these. // </summary> // <param name="sortExpressionsOverLeft"> note that this list gets destroyed by this method </param> private DbExpression TransformIntersectOrExcept( DbExpression left, DbExpression right, DbExpressionKind expressionKind, IList <DbPropertyExpression> sortExpressionsOverLeft, string sortExpressionsBindingVariableName) { var negate = (expressionKind == DbExpressionKind.Except) || (expressionKind == DbExpressionKind.Skip); var distinct = (expressionKind == DbExpressionKind.Except) || (expressionKind == DbExpressionKind.Intersect); var leftInputBinding = left.Bind(); var rightInputBinding = right.Bind(); IList <DbPropertyExpression> leftFlattenedProperties = new List <DbPropertyExpression>(); IList <DbPropertyExpression> rightFlattenedProperties = new List <DbPropertyExpression>(); FlattenProperties(leftInputBinding.Variable, leftFlattenedProperties); FlattenProperties(rightInputBinding.Variable, rightFlattenedProperties); //For Skip, we need to ignore any columns that are not in the original sort list. We can recognize these by comparing the left flattened properties and // the properties in the list sortExpressionsOverLeft // If any such columns exist, we need to add an additional project, to keep the rest of the columns from being projected, as if any among these // are non equal comparable, SQL Server 2000 throws. if (expressionKind == DbExpressionKind.Skip) { if (RemoveNonSortProperties( leftFlattenedProperties, rightFlattenedProperties, sortExpressionsOverLeft, leftInputBinding.VariableName, sortExpressionsBindingVariableName)) { rightInputBinding = CapWithProject(rightInputBinding, rightFlattenedProperties); } } Debug.Assert( leftFlattenedProperties.Count == rightFlattenedProperties.Count, "The left and the right input to INTERSECT or EXCEPT have a different number of properties"); Debug.Assert(leftFlattenedProperties.Count != 0, "The inputs to INTERSECT or EXCEPT have no properties"); //Build the predicate for the quantifier: // (B.b1 = A.a1 or (B.b1 is null and A.a1 is null)) // AND (B.b2 = A.a2 or (B.b2 is null and A.a2 is null)) // AND ... // AND (B.bn = A.an or (B.bn is null and A.an is null))) DbExpression existsPredicate = null; for (var i = 0; i < leftFlattenedProperties.Count; i++) { //A.ai == B.bi DbExpression equalsExpression = leftFlattenedProperties[i].Equal(rightFlattenedProperties[i]); //A.ai is null AND B.bi is null DbExpression leftIsNullExpression = leftFlattenedProperties[i].IsNull(); DbExpression rightIsNullExpression = rightFlattenedProperties[i].IsNull(); DbExpression bothNullExpression = leftIsNullExpression.And(rightIsNullExpression); DbExpression orExpression = equalsExpression.Or(bothNullExpression); if (i == 0) { existsPredicate = orExpression; } else { existsPredicate = existsPredicate.And(orExpression); } } //Build the quantifier DbExpression quantifierExpression = rightInputBinding.Any(existsPredicate); DbExpression filterPredicate; //Negate if needed if (negate) { filterPredicate = quantifierExpression.Not(); } else { filterPredicate = quantifierExpression; } //Build the filter DbExpression result = leftInputBinding.Filter(filterPredicate); //Apply distinct in needed if (distinct) { result = result.Distinct(); } return(result); }
// This is called for any navigation property reference so we can apply filters for those entities here. // That includes any navigation properties referenced in functions (.Where() clauses) and also any // child entities that are .Include()'d. public override DbExpression Visit(DbPropertyExpression expression) { #if DEBUG_VISITS System.Diagnostics.Debug.Print("Visit(DbPropertyExpression): EdmType.Name={0}", expression.ResultType.ModelTypeUsage.EdmType.Name); #endif var baseResult = base.Visit(expression); var basePropertyResult = baseResult as DbPropertyExpression; if (basePropertyResult == null) { return(baseResult); // base.Visit changed type! } var navProp = basePropertyResult.Property as NavigationProperty; if (navProp != null) { var targetEntityType = navProp.ToEndMember.GetEntityType(); var containers = _ObjectContext.MetadataWorkspace.GetItems <EntityContainer>(DataSpace.CSpace).First(); var filterList = FindFiltersForEntitySet(targetEntityType.MetadataProperties); if (filterList.Any()) { // If the expression contains a collection (i.e. the child property is an IEnumerable), we can bind directly to it. // Otherwise, we have to create a DbScanExpression over the ResultType in order to bind. if (baseResult.ResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.CollectionType) { var binding = DbExpressionBuilder.Bind(baseResult); var newFilterExpression = BuildFilterExpressionWithDynamicFilters(filterList, binding, null); if (newFilterExpression != null) { // If not null, a new DbFilterExpression has been created with our dynamic filters. return(newFilterExpression); } } else if (baseResult.ResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType) { if (DoesNotSupportElementMethod(_DbContext)) { // Oracle and MySQL do not support the "newFilterExpression.Element()" method that we need to call // at the end of this block. Oracle *MAY* support it in a newer release but not sure // (see https://community.oracle.com/message/10168766#10168766). // But users may not have the option of upgrading their database so decided to try to support it. // If we find it is supported by newer versions, can detect those versions and allow the normal handling. // To apply any necessary filters to these entities, we're going to have to do it using SSpace. // These entities will be visited via the DbScan visit method so we will apply filters there. // If one of those filters then references a child property, the filter will fail. return(baseResult); } DbExpression scanExpr; var entitySet = containers.EntitySets.FirstOrDefault(e => e.ElementType.Name == baseResult.ResultType.EdmType.Name); if (entitySet == null) { if (baseResult.ResultType.EdmType.BaseType == null) { throw new ApplicationException(string.Format("EntitySet not found for {0}", baseResult.ResultType.EdmType.Name)); } // Did not find the entity set for the property but it has a base type. // This means the entity set of the property is a derived class of a TPT base class. // Find the entity set of the parent and then map it to the type we need. // Then we can bind against that expression. entitySet = containers.EntitySets.FirstOrDefault(e => e.ElementType.Name == baseResult.ResultType.EdmType.BaseType.Name); if (entitySet == null) // hope we don't need to do this recursively... { throw new ApplicationException(string.Format("EntitySet not found for {0} or BaseType {1}", baseResult.ResultType.EdmType.Name, baseResult.ResultType.EdmType.BaseType.Name)); } var parentScanExpr = DbExpressionBuilder.Scan(entitySet); scanExpr = DbExpressionBuilder.OfType(parentScanExpr, baseResult.ResultType); } else { scanExpr = DbExpressionBuilder.Scan(entitySet); } var binding = DbExpressionBuilder.Bind(scanExpr); // Build the join conditions that are needed to join from the source object (basePropertyResult.Instance) // to the child object (the scan expression we just creating the binding for). // These conditions will be and'd with the filter conditions. var associationType = navProp.RelationshipType as AssociationType; if (associationType == null) { throw new ApplicationException(string.Format("Unable to find AssociationType on navigation property of single child property {0} in type {1}", navProp.Name, navProp.DeclaringType.FullName)); } if (associationType.Constraint == null) { // KNOWN_ISSUE: // If this happens, the model does not contain the foreign key (the "id" property). EF will automatically generate // it based on naming rules when generating the SSpace/database models but does not expose the Constraint here in the // AssociationType. In order for us to be able to generate the conditions correctly, those Foreign Keys need to be // specified on the model. To fix/handle this, we would need to examine the SSpace Association Sets (which do have // these relations!!) to try to map that information back to CSpace to figure out the correct properties of the FK conditions. // or...the models just need to contain the necessary "ID" properties for the FK relations so that they are available here // (in CSpace) for us to generate the necessary join conditions. throw new ApplicationException(string.Format("FK Constriant not found for association '{0}' - must directly specify foreign keys on model to be able to apply this filter", associationType.FullName)); } // Figure out if the "baseResults" are the from side or to side of the constraint so we can create the properties correctly // Note that this navigation property may be the same type as parent entity (so both association types // will be the same type). In that case, we need to figure out which side of the association matches the // PKs of the main entity. var fromEdmType = ((AssociationEndMember)associationType.Constraint.FromRole).GetEntityType(); var toEdmType = ((AssociationEndMember)associationType.Constraint.ToRole).GetEntityType(); bool baseResultIsFromRole; if (fromEdmType != toEdmType) { baseResultIsFromRole = (basePropertyResult.Instance.ResultType.EdmType == fromEdmType); } else { // When same, basePropertyResult is the child property and binding is the main entity. // Fixes issue #85. baseResultIsFromRole = false; } DbExpression joinCondition = null; for (int i = 0; i < associationType.Constraint.FromProperties.Count; i++) { var prop1 = DbExpressionBuilder.Property(basePropertyResult.Instance, baseResultIsFromRole ? associationType.Constraint.FromProperties[i] : associationType.Constraint.ToProperties[i]); var prop2 = DbExpressionBuilder.Property(binding.Variable, baseResultIsFromRole ? associationType.Constraint.ToProperties[i] : associationType.Constraint.FromProperties[i]); var condition = prop1.Equal(prop2) as DbExpression; joinCondition = (joinCondition == null) ? condition : joinCondition.And(condition); } // Translate the filter predicate into a DbExpression bound to the Scan expression of the target entity set. // Those conditions are then and'd with the join conditions necessary to join the target table with the source table. var newFilterExpression = BuildFilterExpressionWithDynamicFilters(filterList, binding, joinCondition); if (newFilterExpression != null) { // Converts the collection results into a single row. The expected output is a single item so EF will // then populate the results of that query into the property in the model. // The resulting SQL will be a normal "left outer join" just as it would normally be except that our // filter predicate conditions will be included with the normal join conditions. // MySQL needs this Limit() applied here or it throws an error saying: // Unable to cast object of type 'MySql.Data.Entity.SelectStatement' to type 'MySql.Data.Entity.LiteralFragment'. // But don't do that unless necessary because it produces extra "outer apply" sub queries in MS SQL. // This trick does not work for Oracle... if (_DbContext.IsMySql()) { return(newFilterExpression.Limit(DbConstantExpression.FromInt32(1)).Element()); } return(newFilterExpression.Element()); } } } } return(baseResult); }
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"); }