/// <summary> /// Given a value, returns the value for its principal owner. /// </summary> internal object GetPrincipalValue(PropagatorResult result) { int currentIdentifier = result.Identifier; if (PropagatorResult.NullIdentifier == currentIdentifier) { // for non-identifiers, there is nothing to resolve return(result.GetSimpleValue()); } // find principals for this value bool first = true; object value = null; foreach (int principal in GetPrincipals(currentIdentifier)) { PropagatorResult ownerResult = _identifiers[principal].Owner; if (null != ownerResult) { if (first) { // result is taken from the first principal value = ownerResult.GetSimpleValue(); first = false; } else { // subsequent results are validated for consistency with the first if (!ByValueEqualityComparer.Default.Equals(value, ownerResult.GetSimpleValue())) { throw EntityUtil.Constraint(System.Data.Entity.Strings.Update_ReferentialConstraintIntegrityViolation); } } } } if (first) { // if there are no principals, return the current value directly value = result.GetSimpleValue(); } return(value); }
// determines equality by comparing each key component public bool Equals(CompositeKey left, CompositeKey right) { // Short circuit the comparison if we know the other reference is equivalent if (object.ReferenceEquals(left, right)) { return(true); } // If either side is null, return false order (both can't be null because of // the previous check) if (null == left || null == right) { return(false); } Debug.Assert(null != left.KeyComponents && null != right.KeyComponents, "(Update/JoinPropagator) CompositeKey must be initialized"); if (left.KeyComponents.Length != right.KeyComponents.Length) { return(false); } for (int i = 0; i < left.KeyComponents.Length; i++) { PropagatorResult leftValue = left.KeyComponents[i]; PropagatorResult rightValue = right.KeyComponents[i]; // if both side are identifiers, check if they're the same or one is constrained by the // other (if there is a dependent-principal relationship, they get fixed up to the same // value) if (leftValue.Identifier != PropagatorResult.NullIdentifier) { if (rightValue.Identifier == PropagatorResult.NullIdentifier || _manager.GetCliqueIdentifier(leftValue.Identifier) != _manager.GetCliqueIdentifier(rightValue.Identifier)) { return(false); } } else { if (rightValue.Identifier != PropagatorResult.NullIdentifier || !ByValueEqualityComparer.Default.Equals(leftValue.GetSimpleValue(), rightValue.GetSimpleValue())) { return(false); } } } return(true); }
// Gets the value to use for hash code private int GetComponentHashCode(PropagatorResult keyComponent) { if (keyComponent.Identifier == PropagatorResult.NullIdentifier) { // no identifier exists for this key component, so use the actual key // value Debug.Assert(null != keyComponent && null != keyComponent, "key value must not be null"); return(ByValueEqualityComparer.Default.GetHashCode(keyComponent.GetSimpleValue())); } else { // use ID for FK graph clique (this ensures that keys fixed up to the same // value based on a constraint will have the same hash code) return(_manager.GetCliqueIdentifier(keyComponent.Identifier).GetHashCode()); } }
/// <summary> /// Determines column/value used to set values for a row. /// </summary> /// <remarks> /// The following columns are not included in the result: /// <list> /// <item>Keys in non-insert operations (keys are only set for inserts).</item> /// <item>Values flagged 'preserve' (these are values the propagator claims are untouched).</item> /// <item>Server generated values.</item> /// </list> /// </remarks> /// <param name="target">Expression binding representing the table.</param> /// <param name="row">Row containing values to set.</param> /// <param name="processor">Context for table.</param> /// <param name="insertMode">Determines whether key columns and 'preserve' columns are /// omitted from the list.</param> /// <param name="outputIdentifiers">Dictionary listing server generated identifiers.</param> /// <param name="returning">DbExpression describing result projection for server generated values.</param> /// <param name="rowMustBeTouched">Indicates whether the row must be touched /// because it produces a value (e.g. computed)</param> /// <returns>Column value pairs.</returns> private IEnumerable <DbModificationClause> BuildSetClauses(DbExpressionBinding target, PropagatorResult row, PropagatorResult originalRow, TableChangeProcessor processor, bool insertMode, out Dictionary <int, string> outputIdentifiers, out DbExpression returning, ref bool rowMustBeTouched) { Dictionary <EdmProperty, PropagatorResult> setClauses = new Dictionary <EdmProperty, PropagatorResult>(); List <KeyValuePair <string, DbExpression> > returningArguments = new List <KeyValuePair <string, DbExpression> >(); outputIdentifiers = new Dictionary <int, string>(); // Determine which flags indicate a property should be omitted from the set list. PropagatorFlags omitMask = insertMode ? PropagatorFlags.NoFlags : PropagatorFlags.Preserve | PropagatorFlags.Unknown; for (int propertyOrdinal = 0; propertyOrdinal < processor.Table.ElementType.Properties.Count; propertyOrdinal++) { EdmProperty property = processor.Table.ElementType.Properties[propertyOrdinal]; // Type members and result values are ordinally aligned PropagatorResult propertyResult = row.GetMemberValue(propertyOrdinal); if (PropagatorResult.NullIdentifier != propertyResult.Identifier) { // retrieve principal value propertyResult = propertyResult.ReplicateResultWithNewValue( m_translator.KeyManager.GetPrincipalValue(propertyResult)); } bool omitFromSetList = false; Debug.Assert(propertyResult.IsSimple); // Determine if this is a key value bool isKey = false; for (int i = 0; i < processor.KeyOrdinals.Length; i++) { if (processor.KeyOrdinals[i] == propertyOrdinal) { isKey = true; break; } } // check if this value should be omitted PropagatorFlags flags = PropagatorFlags.NoFlags; if (!insertMode && isKey) { // Keys are only set for inserts omitFromSetList = true; } else { // See if this value has been marked up with some context. If so, add the flag information // from the markup. Markup includes information about whether the property is a concurrency value, // whether it is known (it may be a property that is preserved across an update for instance) flags |= propertyResult.PropagatorFlags; } // Determine if this value is server-generated StoreGeneratedPattern genPattern = MetadataHelper.GetStoreGeneratedPattern(property); bool isServerGen = genPattern == StoreGeneratedPattern.Computed || (insertMode && genPattern == StoreGeneratedPattern.Identity); if (isServerGen) { DbPropertyExpression propertyExpression = target.Variable.Property(property); returningArguments.Add(new KeyValuePair <string, DbExpression>(property.Name, propertyExpression)); // check if this is a server generated identifier int identifier = propertyResult.Identifier; if (PropagatorResult.NullIdentifier != identifier) { if (m_translator.KeyManager.HasPrincipals(identifier)) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_GeneratedDependent(property.Name)); } outputIdentifiers.Add(identifier, property.Name); // If this property maps an identifier (in the update pipeline) it may // also be a store key. If so, the pattern had better be "Identity" // since otherwise we're dealing with a mutable key. if (genPattern != StoreGeneratedPattern.Identity && processor.IsKeyProperty(propertyOrdinal)) { throw EntityUtil.NotSupported(System.Data.Entity.Strings.Update_NotSupportedComputedKeyColumn( EdmProviderManifest.StoreGeneratedPatternFacetName, XmlConstants.Computed, XmlConstants.Identity, property.Name, property.DeclaringType.FullName)); } } } if (PropagatorFlags.NoFlags != (flags & (omitMask))) { // column value matches "omit" pattern, therefore should not be set omitFromSetList = true; } else if (isServerGen) { // column value does not match "omit" pattern, but it is server generated // so it cannot be set omitFromSetList = true; // if the row has a modified value overridden by server gen, // it must still be touched in order to retrieve the value rowMustBeTouched = true; } // make the user is not updating an identity value if (!omitFromSetList && !insertMode && genPattern == StoreGeneratedPattern.Identity) { //throw the error only if the value actually changed Debug.Assert(originalRow != null, "Updated records should have a original row"); PropagatorResult originalPropertyResult = originalRow.GetMemberValue(propertyOrdinal); Debug.Assert(originalPropertyResult.IsSimple, "Server Gen property that is not primitive?"); Debug.Assert(propertyResult.IsSimple, "Server Gen property that is not primitive?"); if (!ByValueEqualityComparer.Default.Equals(originalPropertyResult.GetSimpleValue(), propertyResult.GetSimpleValue())) { throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.Update_ModifyingIdentityColumn( XmlConstants.Identity, property.Name, property.DeclaringType.FullName)); } else { omitFromSetList = true; } } if (!omitFromSetList) { setClauses.Add(property, propertyResult); } } // Construct returning projection if (0 < returningArguments.Count) { returning = DbExpressionBuilder.NewRow(returningArguments); } else { returning = null; } // Construct clauses corresponding to the set clauses List <DbModificationClause> result = new List <DbModificationClause>(setClauses.Count); foreach (KeyValuePair <EdmProperty, PropagatorResult> setClause in setClauses) { EdmProperty property = setClause.Key; result.Add(new DbSetClause( GeneratePropertyExpression(target, setClause.Key), GenerateValueExpression(setClause.Key, setClause.Value))); } return(result); }