/// <summary> /// Determines if the specified EdmMember is a concurrency timestamp. /// </summary> /// <remarks>Since EF doesn't expose "timestamp" as a first class /// concept, we use the below criteria to infer this for ourselves. /// </remarks> /// <param name="member">The member to check.</param> /// <returns>True or false.</returns> public static bool IsConcurrencyTimestamp(EdmMember member) { Facet facet = member.TypeUsage.Facets.SingleOrDefault(p => p.Name == "ConcurrencyMode"); if (facet == null || facet.Value == null || (ConcurrencyMode)facet.Value != ConcurrencyMode.Fixed) { return(false); } facet = member.TypeUsage.Facets.SingleOrDefault(p => p.Name == "FixedLength"); if (facet == null || facet.Value == null || !((bool)facet.Value)) { return(false); } facet = member.TypeUsage.Facets.SingleOrDefault(p => p.Name == "MaxLength"); if (facet == null || facet.Value == null || (int)facet.Value != 8) { return(false); } MetadataProperty md = ObjectContextUtilities.GetStoreGeneratedPattern(member); if (md == null || facet.Value == null || (string)md.Value != "Computed") { return(false); } return(true); }
/// <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()); }