/// <summary> /// Extension method used to attach the specified entity as modified, /// with the specified original state. /// </summary> /// <typeparam name="T">The entity Type</typeparam> /// <param name="objectSet">The ObjectSet to attach to</param> /// <param name="current">The current entity state</param> /// <param name="original">The original entity state</param> public static void AttachAsModified <T>(this ObjectSet <T> objectSet, T current, T original) where T : class { if (objectSet == null) { throw new ArgumentNullException(nameof(objectSet)); } if (current == null) { throw new ArgumentNullException(nameof(current)); } if (original == null) { throw new ArgumentNullException(nameof(original)); } // Attach the entity if it is not already attached, or if it is already // attached, transition to Modified EntityState currState = ObjectContextUtilities.GetEntityState(objectSet.Context, current); if (currState == EntityState.Detached) { objectSet.Attach(current); } else { objectSet.Context.ObjectStateManager.ChangeObjectState(current, EntityState.Modified); } ObjectStateEntry stateEntry = ObjectContextUtilities.AttachAsModifiedInternal(current, original, objectSet.Context); if (stateEntry.State != EntityState.Modified) { // Ensure that when we leave this method, the entity is in a // Modified state. For example, if current and original are the // same, we still need to force the state transition objectSet.Context.ObjectStateManager.ChangeObjectState(current, EntityState.Modified); } }
/// <summary> /// Extension method used to attach the specified entity as modified. This overload /// can be used in cases where the entity has a Timestamp member. /// </summary> /// <typeparam name="T">The entity Type</typeparam> /// <param name="objectSet">The ObjectSet to attach to</param> /// <param name="entity">The current entity state</param> public static void AttachAsModified <T>(this ObjectSet <T> objectSet, T entity) where T : class { if (objectSet == null) { throw new ArgumentNullException(nameof(objectSet)); } if (entity == null) { throw new ArgumentNullException(nameof(entity)); } ObjectContext context = objectSet.Context; EntityState currState = ObjectContextUtilities.GetEntityState(context, entity); if (currState == EntityState.Detached) { // attach the entity objectSet.Attach(entity); } // transition the entity to the modified state context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified); }
/// <summary> /// Returns a collection of all the <see cref="Attribute"/>s we infer from the metadata associated /// with the metadata member corresponding to the given property descriptor /// </summary> /// <param name="pd">A <see cref="PropertyDescriptor"/> to examine</param> /// <returns>A collection of attributes inferred from the metadata in the given descriptor.</returns> protected override IEnumerable <Attribute> GetMemberAttributes(PropertyDescriptor pd) { List <Attribute> attributes = new List <Attribute>(); // Exclude any EntityState, EntityReference, etc. members if (ShouldExcludeEntityMember(pd)) { // for these members, we don't want to do any further // attribute inference attributes.Add(new ExcludeAttribute()); return(attributes.ToArray()); } EditableAttribute editableAttribute = null; bool inferRoundtripOriginalAttribute = false; bool hasKeyAttribute = (pd.Attributes[typeof(KeyAttribute)] != null); bool isEntity = this.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType; if (isEntity) { EntityType entityType = (EntityType)this.EdmType; EdmMember keyMember = entityType.KeyMembers.SingleOrDefault(k => k.Name == pd.Name); if (keyMember != null && !hasKeyAttribute) { attributes.Add(new KeyAttribute()); hasKeyAttribute = true; } } EdmProperty member = this.EdmType.Members.SingleOrDefault(p => p.Name == pd.Name) as EdmProperty; if (member != null) { if (hasKeyAttribute) { // key members must always be roundtripped inferRoundtripOriginalAttribute = true; // key members that aren't also FK members are non-editable (but allow an initial value) if (!this._keyIsEditable) { editableAttribute = new EditableAttribute(false) { AllowInitialValue = true }; } } // Check if the member is DB generated and add the DatabaseGeneratedAttribute to it if not already present. if (pd.Attributes[typeof(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedAttribute)] == null) { MetadataProperty md = ObjectContextUtilities.GetStoreGeneratedPattern(member); if (md != null) { if ((string)md.Value == "Computed") { attributes.Add(new System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedAttribute(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed)); } else if ((string)md.Value == "Identity") { attributes.Add(new System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedAttribute(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)); } } } // Add implicit ConcurrencyCheck attribute to metadata if ConcurrencyMode is anything other than ConcurrencyMode.None Facet facet = member.TypeUsage.Facets.SingleOrDefault(p => p.Name == "ConcurrencyMode"); if (facet != null && facet.Value != null && (ConcurrencyMode)facet.Value != ConcurrencyMode.None && pd.Attributes[typeof(ConcurrencyCheckAttribute)] == null) { attributes.Add(new ConcurrencyCheckAttribute()); inferRoundtripOriginalAttribute = true; } bool isStringType = pd.PropertyType == typeof(string) || pd.PropertyType == typeof(char[]); // Add Required attribute to metadata if the member cannot be null and it is either a reference type or a Nullable<T> // unless it is a database generated field if (!member.Nullable && (!pd.PropertyType.IsValueType || IsNullableType(pd.PropertyType)) && !pd.Attributes.OfType <DatabaseGeneratedAttribute>().Any(dga => dga.DatabaseGeneratedOption != DatabaseGeneratedOption.None) && pd.Attributes[typeof(RequiredAttribute)] == null) { attributes.Add(new RequiredAttribute()); } if (isStringType && pd.Attributes[typeof(StringLengthAttribute)] == null) { facet = member.TypeUsage.Facets.SingleOrDefault(p => p.Name == "MaxLength"); if (facet != null && facet.Value != null && facet.Value.GetType() == typeof(int)) { // need to test for Type int, since the value can also be of type // System.Data.Metadata.Edm.EdmConstants.Unbounded int maxLength = (int)facet.Value; attributes.Add(new StringLengthAttribute(maxLength)); } } bool hasTimestampAttribute = (pd.Attributes[typeof(TimestampAttribute)] != null); if (this._timestampMember == member && !hasTimestampAttribute) { attributes.Add(new TimestampAttribute()); hasTimestampAttribute = true; } // All members marked with TimestampAttribute (inferred or explicit) need to // have [Editable(false)] and [RoundtripOriginal] applied if (hasTimestampAttribute) { inferRoundtripOriginalAttribute = true; if (editableAttribute == null) { editableAttribute = new EditableAttribute(false); } } // Add RTO to this member if required. If this type has a timestamp // member that member should be the ONLY member we apply RTO to. // Dont apply RTO if it is an association member. bool isForeignKeyMember = this._foreignKeyMembers != null && this._foreignKeyMembers.Contains(member); if ((this._timestampMember == null || this._timestampMember == member) && (inferRoundtripOriginalAttribute || isForeignKeyMember) && pd.Attributes[typeof(AssociationAttribute)] == null) { if (pd.Attributes[typeof(RoundtripOriginalAttribute)] == null) { attributes.Add(new RoundtripOriginalAttribute()); } } } // Add the Editable attribute if required if (editableAttribute != null && pd.Attributes[typeof(EditableAttribute)] == null) { attributes.Add(editableAttribute); } if (isEntity) { this.AddAssociationAttributes(pd, attributes); } return(attributes.ToArray()); }
/// <summary> /// Returns the <see cref="StructuralType"/> that corresponds to the given CLR type /// </summary> /// <param name="clrType">The CLR type</param> /// <returns>The StructuralType that corresponds to the given CLR type</returns> public StructuralType GetEdmType(Type clrType) { return(ObjectContextUtilities.GetEdmType(this.MetadataWorkspace, clrType)); }