Поделиться через


Data Annotations in the Entity Framework and Code First

Data annotation attributes were introduced in .NET 3.5 as a way to add validation to classes used by ASP.NET applications. Since that time, RIA Services has begun using data annotations and they are now part of Silverlight.  Code First allows you to build an EDM Entity Framework model using code (C# or VB.NET) and is now in its third CTP.

Feedback we’ve received from these CTPs shows demand for Code First to read data annotation attributes from class and property definitions to configure aspects such as maximum string length, which properties are keys, or the table name in which to store a type of entity.

This post outlines which existing data annotation attributes we’ll adopt and which new attributes we are proposing.

Using Data Annotations

When building a model using Code First, you start by writing a set of entity classes. For example:

public class Book
{
    public string ISBN { get; set; }
    public string Title { get; set; }
    public string AuthorSSN { get; set; }
    public Person Author { get; set; }
}

public class Person
{
    public string SSN { get; set; }
    public string Name { get; set; }
    public ICollection<Book> Books { get; set; }
}

When using this class model with Code First in CTP3, you would need to use the fluent API to describe which properties are keys, which relationships exist, and the length of each string property if you wanted these to be constrained. These are very common tasks and we’d like users of Code First to be able to make these kinds of configuration without having to use the fluent API to describe the model. Using data annotation attributes, the above model characteristics can be made declaratively as part of the class definition:

public class Book
{
[Key]
    public string ISBN { get; set; }

    [StringLength(256)]
    public string Title { get; set; }

public string AuthorSSN { get; set; }

    [RelatedTo(RelatedProperty=“Books”, Key=”AuthorSSN”, RelatedKey=”SSN”)]
public Person Author { get; set; }
}

public class Person
{
    [Key]
    public string SSN { get; set; }

    [StringLength(512)]
    public string Name { get; set; }

    [RelatedTo(RelatedProperty=”Author”)]
    public ICollection<Book> Books { get; set; }
}

Existing Data Annotation Attributes

There are several existing data annotation attributes in the System.ComponentModel.DataAnnotations namespace that can be used by Code First. These are:

KeyAttribute

KeyAttribute is used to specify that a property/column is part of the primary key of the entity and applies to scalar properties only.

StringLengthAttribute

StringLengthAttribute is used to specify the maximum length of a string. As there is no minimum length in Code First or EF, the “MinimumLength” property on the attribute would be ignored. This attribute applies only to properties that are of type string.

In .NET 4.0, a negative MaximumValue is not allowed. but in the next version of the framework the recommendation is to change this behavior so that a value of ‘-1’ means “max”. This isn’t ideal, so another idea we are considering is to make a MaxStringLengthAttribute that derives from StringLengthAttribute. The MaxStringLengthAttribute wouldn’t take parameters, but would just assume the maximum allowable value.

ConcurrencyCheckAttribute

ConcurrencyCheckAttribute is used to specify that a property/column has a concurrency mode of “fixed” in the EDM model. A fixed concurrency mode means that this property is part of the concurrency check of the entity during save operations and applies to scalar properties only.

RequiredAttribute

RequiredAttribute is used to specify that a property/column is non-nullable and applies to scalar, complex, and navigation properties:

  • Scalar properties: means this property is non-nullable (even if the CLR type says it is nullable).
  • Complex properties: the attribute is not allowed here so we would throw an exception
  • Navigation properties
    • Collection property: the attribute is not allowed here so we would throw an exception
    • Reference property: this means the backing FK property is non-null, and that the end of the association is cardinality “1” (instead of 0..1).

TimestampAttribute

The TimestampAttribute is used to specify that a byte[] property/column has a concurrency mode of “fixed” in the model and that it should be treated as a timestamp column on the store model (non-nullable byte[] in the CLR type). This attribute applies to scalar properties of type byte[] only. and only one TimestampAttribute can be present on an entity as this is what is allowed on most database platforms.

DataMemberAttribute

The DataMemberAttribute, while not strictly part of the data annotation collection, can be used to help specify the ordinal of primary keys. This is important on entities where there is a composite primary key and you want to make an assertion about the order the keys are specified on the database or are used in your application. This attribute applies only to scalar properties only that are part of the key. Ordinals do not need to be consecutive, they only specify the relative order of the key properties in metadata.

Ideally we’d like to be able to specify the key order as part of the KeyAttribute via an “Ordinal” property, but we can’t make changes to KeyAttribute until the next release of the .NET framework.

New Data Annotation Attributes

While the existing data annotation attributes provide many useful options for defining your model, there are still several common cases where we’d like to be able to make model changes with annotation attributes. Our goal with data annotation support in Code First is not to provide another way to completely specify a model, but to provide a mechanism that addresses the most common kinds of configurations that developers need to influence their model.

This is an area where it would be great to get early feedback so we can incorporate your ideas into our data annotation plan - below are descriptions of the annotation attributes we are proposing for Code First.

RelatedToAttribute

The RelatedToAttribute is used for two things:

  • To assign bi-directionality to certain relationships where the built-in convention doesn’t detect it
  • To associate a navigation property with its foreign key(s)

The RelatedToAttribute is placed on navigation properties and can be present on zero, one, or both ends of a relationship. There are a few parameters on the RelatedToAttribute:

  • A property to optionally specify the RelatedProperty on the target type.
    This is to support bi directionality in relationships (Book.Author is related to Person.Books, for example).
  • A property to optionally specify the keys on this entity that constrain the relationship.
    This can be either the primary key(s) if the attribute is on a navigation property on the principal end of a relationship, or the foreign key(s) if the attribute is on a navigation property on the dependent end of a relationship.
  • A property to optionally specify the keys on the related entity that constrain the relationship.
    This can be either the primary key(s) if the attribute is on a navigation property on the dependent end of a relationship, or the foreign key(s) if the attribute is on a navigation property on the principal end of a relationship.
  • A property to optionally specify cascade delete behavior.
    When this property is set to true and the principal entity is deleted, the dependent end of the relationship will also be deleted.

In the example at the beginning of the blog post, the RelatedToAttribute was used to related the Book.Author and Person.Books navigation properties so that all of Book.AuthorSSN, Book.Author, and Person.Books represent the same relationship in the model.

We also considered using the existing AssociationAttribute to specify relationships. however this attribute assumes that there already is a backing model and requires a relationship name and required key properties which are not needed when describing your model with Code First.

LengthAttribute

LengthAttribute can be used to specify the maximum length of an array type, such as a byte[] and applies only to properties. A value of ‘-1’ means “max” and all other negative values are not allowed. There is overlap here with the StringLengthAttribute: when the LengthAttribute is specified on a property of type string, the semantics will be the same as StringLengthAttribute.

StoreGeneratedAttribute

The StoreGeneratedAttribute can be used to specify that a property’s value is generated on the database store and the only store generated pattern supported in the first version is “identity”. This attribute applies to properties only and has a single optional nullable boolean property Identity. A null value for Identity means that no decision should be made for Identity: the convention or an explicit API call will be made to turn the identity pattern on or off.

StoreNameAttribute

The StoreNameAttribute can be used to specify a name for a store/database item. This attribute has different meanings depending on the kind of thing this attribute applies to:

  • If the attribute is on a context class, it means “database name”.
  • If the attribute is on an entity class, it means “table name”.
  • If the attribute is on a property in an entity class, it means “column name”.

StoreIgnoreAttribute

StoreIgnoreAttribute can be used to specify that a CLR type or property should not be included when reading type or property metadata from a CLR type. This attribute can apply to classes, which means they should not be considered as an EntityType or ComplexType object but can also apply to properties, which means that property should not be part of the EntityType or ComplexType. We haven’t finalized the name of this attribute yet, so if you have suggestions, please let us know.

StoreInlineAttribute

StoreInlineAttribute can be used to specify that a CLR type should be considered as a ComplexType when reading type metadata from CLR types. Complex types can be thought of as a class without an identity/keys, or a logic grouping of properties (such as an Address or PhoneNumber) and applies only to class definitions. ComplexTypes are stored inline with the entity. We may choose not to include this attribute as the convention we are thinking of adding to Code First should detect most cases where a complex type is being used.

Other Possibilities

There may be other kinds of model definition that you’d like to be able to do using data annotation attributes. Some other candidates are:

  • Being able to specify whether a string is unicode or not
  • Being able to specify whether a type is variable or fixed length
  • Having data annotation attributes put CSDL annotations in your model that other systems (such as OData) can read. An example of this is the RangeAttribute.

Let us know if any of these options sound interesting or if there is something else you’d like us to consider.

Summary

Code First will offer a few different ways of configuring a model: purely by built-in conventions, using data annotation attributes to make certain kinds of model changes, or by using a fluent API to fully customize the model. To provide more flexibility over the existing data annotation attributes, we are adding new attributes that Code First can use.

We’d like to hear your feedback on the set of attributes that are available and how Code First will use them.

Jeff Derstadt
Entity Framework Team

Comments

  • Anonymous
    March 30, 2010
    Hi, nice work, but it could go further. I would like a full tag modeling, as JPA or nhibernate, where you declare all: the mapped table and so on. I would be nice to have the option of avoiding the edmx Nice work!.

  • Anonymous
    March 30, 2010
    Hello People, until now I've not thought about configure model using annotations. I think it is a great idea, You're going so well with Fluent API but I'l like to use annotations too. Are you going do implement it or just thinking?

  • Anonymous
    March 30, 2010
    @Higor It is our plan to implement this set of Data Annotations and incorporate any feedback we receive on naming or additional useful attributes. This should be available in the next CTP of Code First. Thanks, Jeff

  • Anonymous
    March 30, 2010
    @Pablo We have thought about the idea of offering a full declarative syntax for describing the mapping. Right now we don't have plans to do this for the first release, but we'll take it into consideration for the future. You will be able to express full mapping with the fluent API. Jeff

  • Anonymous
    April 02, 2010
    I would prefer a StoreTypeAttribute over a TimestampAttribute. This way, the store type can be specified. The reason is that you may need to specify other data types as well. An example that comes to mind is decimal. By default, generating an SQL Server db from code first will make all decimal fields decimal(18, 0). But you may want them saved as money. When the StoreGeneratedAttribute has value of Identity, it would be important to somehow specify whether this should apply to the EDM only or both the EDM and the data store. This is because it is possible to have more than one column with the StoreGeneratedPattern property set to Identity in the EDM. A good thing. However RDBMS, specifically SQL server will not allow you to have more than one Identity column in a table. So, trying to create database from model or Code-first with multiple fields’ StoreGeneratedPattern property set to Identity will not succeed and will raise the error: “Multiple identity columns specified for table ‘TableName'. Only one identity column per table is allowed.”

  • Anonymous
    April 02, 2010
    IsUniqueAttribute?

  • Anonymous
    April 05, 2010
    How about an attribute to mark something as transient?   Perhaps I am missing something, but I have been searching for the last hour on how to make one of my POCO classes' attributes not created in the database when i do a context.CreateDatabase(). Does anyone know how to specify this in fluent syntax?

  • Anonymous
    April 06, 2010
    In CodeFirst CTP3 there isn't a way to "exclude" a property from an entity, but this is something we are adding to the fluent API and the set of annotations. The attribute called "StoreIgnoreAttribute" that is described above is the annotation. We considered calling this StoreTransientAttribute, but we aren't sure if that is globally recognized since "Ignore" is more common in .NET. Jeff

  • Anonymous
    April 06, 2010
    @Tommy In CodeFirst CTP3 there isn't a way to "exclude" a property from an entity, but this is something we are adding to the fluent API and the set of annotations. The attribute called "StoreIgnoreAttribute" that is described above is the annotation. We considered calling this StoreTransientAttribute, but we aren't sure if that is globally recognized since "Ignore" is more common in .NET. Jeff

  • Anonymous
    April 06, 2010
    @Ben Uniqueness: right not the EF doesn't support unique constraints, but this is something we are likely to add in the next release after VS 2010. At that time we'll update the Code First API and annotations to support this constraint. Store type: We'll support TimestampAttribute since it is already present as part of the existing data annotation attributes. As for adding a StoreTypeAttribute, we've considered this but found that there are only a limited number of cases where you would need this (such as specifying decimal scale and precision). There would be complicated interactions between attributes such as StringLengthAttribute and StoreTypeAttribute as to which should take precedence. However, it is still on the table and we appreciate your feedback. Jeff

  • Anonymous
    April 07, 2010
    The comment has been removed

  • Anonymous
    April 07, 2010
    there is not a way to drop one table or drop all table but not drop the database

  • Anonymous
    April 08, 2010
    When might we see a Code First beta?

  • Anonymous
    April 08, 2010
    StringLength Attribute should have a minimal property And IndexAttribute is important too Range Attribute should be seperate as MinAttribute MaxAtribute AllowNegative AllowDecimal etc.

  • Anonymous
    April 08, 2010
    [Exclude] [DbKey(IsDbGenerate=false), Length(50)] [DbTable("User")] StringColumn(IsUnicode=true [Index("Name_Age", ASC = false, UNIQUE = true)] [HasOne(OrderBy = "Id DESC")] [RelateTo, DbColumn("Person_Id")]

  • Anonymous
    April 17, 2010
    I'd like to know if is there a way to modify the existing POCO template so taht in the generated entities I could have a some 'validation' attribues set such as [Key] or [StringLength] for (some of) the properties? - based on existing database schema... Is it feasible?

  • Anonymous
    May 04, 2010
    Could you put in a "Category" string property to the base ValidationAttribute so validation messages could be categorised, say into Errors, Warnings, Informational ?

  • Anonymous
    June 09, 2010
    Hi Jeff, I consider the Data-Annotations with the full declarative mapping possibilities as a "must" to be usable in an enterprise architecture. Right now, the EDMX is a handy toy for very little one-shot apps. The "fluent API", avoiding the funny wizard/EDMX toys, is surely the right way for handling databases with n-1000 tables. But nobody wants to call the API to get the mappings, it's good enough for a few tables, but for a large db it's a pain. It definitely needs to be expressed declarative, much like L2S, NHibernate etc.. Right now, no serious architect considers EF as an valid option for larger databases, which is a pity. Thank you, Andrej

  • Anonymous
    June 25, 2010
    Hi, I have just discovered that post. I have already started a small project to do the same thing... argh, I thought I am the only one ;-) My solution looks the following (it is heavily influenced by JPA and (N)HIbernate) because I am using these in customer projects). I am currently only able to map simple types because of too less time to implement more. But perhaps this could also give you some ideas: [Entity (Name="DB_TableName)] public class IntegrationTestEntity {   [Id]   [Column(Name = "TEST_ID")]   public long Id { get; set; }   [Column(Length=20, Nullable=false)]   public string Name { get; set; }   [Column(Name = "TEST_VORNAME", Nullable=true)]   public string Firstname { get; set; }   [Transient]  public int donotStoreInDB { get; set;} } Most of the Annotations are optional like the Column Attribute because I would like to have convention over configuratio. So if I don't specify any thing my framework uses common sense defaults (e.g. if name in Column Attribute is missing the property name is used as column name). Kind reagrds, Thomas

  • Anonymous
    July 15, 2010
    I noticed in Scott Gu's blog post that the table columns were alphabetical instead of the same order in the class. Can you please not sort the properties by name and then insert the columns into the table? At a minimum, offer an option to sort or not sort. Thanks! Karl

  • Anonymous
    July 16, 2010
    Is there any way currently to specify the database name that should be used for a given DbContext?  (like what the proposed DataStoreName when used on a DbContext will do?)

  • Anonymous
    July 23, 2010
    consider a MVVM pattern, in some scenarios model is not sufficient so you need a viewmodel wich usually has a few members that are model classes or some members of model classes. in such a case, you would have way too many classes that are very similar, often two times more than if you could use mvc pattern. now, if you could specify that certan property is a column in one table and another property that is a column in other table than you could map tables (models) directly into your viewmodels.

  • Anonymous
    July 28, 2010
    Is the RequiredAttribute working for FKs? The reason I ask is that I if I add a property like:        [Required]        public virtual Venue Venue { get; set; } I would have expected the resulting FK column to be NOT nullable, but it is actually nullable. I can workaround it (by adding another VenueID property of type int), but I'd rather not if possible...

  • Anonymous
    July 29, 2010
    It looks like the StoreNameAttribute and StoreIgnoreAttribute did,t make it to the CTP, or I am missing something?

  • Anonymous
    July 30, 2010
    Is it possible to use these annotations in another language, e.g. F#?

  • Anonymous
    August 05, 2010
    +1 Nufri Where are the StoreNameAttribute and StoreIgnoreAttribute?

  • Anonymous
    August 12, 2010
    @Matthew Wills I've run into the same issue re: the [Required] attribute on a foreign key property still resulting in a nullable DB column. If you override OnModelCreating in your context class and use the fluent API to specify that a Venue is required, the resulting foreign key column is indeed not nullable. modelBuilder.Entity<Dinner>.HasRequired(d => d.Venue).WithMany(v => v.Dinners); This makes a separate Dinner.VenueId property unnecessary, although having to define column attributes using both annotations and fluent API is obviously less than ideal.

  • Anonymous
    August 14, 2010
    I would definitely want to have the [Length] attribute.

  • Anonymous
    August 16, 2010
    Using the MapSingleType method it seems that you have to remap the whole object. This is a pain if you just want to remap a single property. I would like to use data annotation to do this but the current CTP4 doesn’t seem to support it, or I cannot find the correct attibute to use. For example, with Data.Linq you can use the Column attribute with the Name property but there doesn’t seem to be an equivalent in EF. A lot of the attributes you mention above seem to be missing from the CTP4 - is that correct or have I missed something?

  • Anonymous
    September 01, 2010
    How would you specify different data types for string ? Specially when you want to use text or ntext ? Cheers Sunny

  • Anonymous
    October 05, 2010
    Looking forward to StoreNameAttribute

  • Anonymous
    October 07, 2010
    Defining the string type will be a blessing!

  • Anonymous
    October 19, 2010
    What about supporting DisplayAttribute? It's very important for localization. BTW what done for localization in EF4? In EF 3.5.1 we created own wrap of EntityModelCodeGenerator which translates LongDiscription from edmx metadata to DisplayName attribute of ComponentModel. And why localization attributes don't collected in one namespace and used all frameworks? ASP uses Display via MetadataType, WinForms uses DisplayName directly, RIA uses Display via MetadataType on server and directly on client. It's pain to support this especially if EF doesn't care about it. Thanks!

  • Anonymous
    October 22, 2010
    With respect to the proposed naming convention for [StoreInline] and [StoreIgnore], it appears most existing .NET attribute names conform to the convention "PropertyName is/has AttributeName". So we get "ID is Key" or "Name has Length(200)".  The current StoreXXX naming convention doesn't really apply this convention.   I would suggest attribute names such as [NotPersisted] and [PersistedInline], so the convention is maintained:  "InMemoryProperty is NotPersisted" or "MyComplexType is PersistedInline".

  • Anonymous
    October 31, 2010
    Hi, The last option: "Having data annotation attributes put CSDL annotations in your model that other systems (such as OData) can read." sounds grate to me. This can be leveraged by T4 templates to generate annotations with the self tracing entities and "close a circle" for development with Silverlight RIA. Generally, it would help in facilitating data annotation for non-code first scenarios (kind of looking on it from the other direction). Is it really in the pipeline or is it just a thought? Best, Asher

  • Anonymous
    November 06, 2010
    The comment has been removed

  • Anonymous
    December 02, 2010
    Don't mean to be rude but why would any right minded developer make it so the db columns are alphabetically sorted BY DEFAULT? Have you ever heard of the principle of least surprise? And the fact this BUG is still in here from CTP3 to CTP4 is astounding. Someone went out of their way to add a .OrderBy() clause somewhere in the code that is completely uncalled for. Judging by the EF teams responsive (or lack of) to the enum debacle I get the feeling this bug will be in here post release and maybe many years into the future.

  • Anonymous
    December 03, 2010
    Hi, I am still I getting the "unable to infer a primary key" error on any of the tables I try to access. I am using the [Key] attribute as you indicated, but it seems to have no effect. I also tried declaring the key in OnModelCreating in the DbContext. This at least gives me a different error: "Key expression is not valid." Has anyone else run into this? I'm finding very few examples of this error online. I'm using .NET 4, VisualStudio 2010, etc.

  • Anonymous
    December 06, 2010
    Is there a way to add composite keys? Example: [Key] orderNumber, orderDate

  • Anonymous
    January 20, 2011
    Hi, is it possible to mark a base class as something like "single inheritance" or "Ignore as own entity" . We have a layered szenario, in which baseclasses are the core-database-classes and they resides in a separate assembly.   The enhanced and derived classes resides in the main application. These concrete classes should be handled as single tables in the database. There is no further inheritance on the baseclasses (only one derived class). The best approach would be "TPC (Table per concrete Class)" , but it has configuration overhead and the concrete classes are hidden in the dbcontext. (only dbset with baseclass) . It seems to me, that it must be easy to implement, because there is only a single inheritance, so the classes can handled as classes without inheritance.     greetings, Dirk

  • Anonymous
    February 13, 2011
    To give my 2 cents, I would love to see Default Value Annotation. I would also like a data annotation for setting the data column type for example a text column instead of varchar.

  • Anonymous
    February 25, 2011
    i love if EF can support uniqueidentifier PK (Guid), StoreGeneratedAttribute support store generated pattern like newsecuencialid or newid values

  • Anonymous
    March 16, 2011
    Yes, I'd like to be able to specify a string as non-unicode to map to varchar. I'm currently hitting an issue where SQL fails to compile a query using a unicode query parameter that EF is generating.

  • Anonymous
    May 23, 2011
    Please help me: social.msdn.microsoft.com/.../94a856d3-cf18-459e-9adb-310b58c0a2e3

  • Anonymous
    June 21, 2011
    It would be nice to mark a column as "optionally existing" to enable flexibility with legacy databases

  • Anonymous
    October 30, 2012
    I would really like the unicode attribute. We have a big project about to start, and the entire project will require unicode support. Something like a unicode data annotation would go a long way.

  • Anonymous
    December 30, 2012
    MinPrecision, MaxPrecision, Precision (to specify both or that they are the same) would be useful DefaultValue would be good

  • Anonymous
    February 23, 2013
    Hi, what  is the namespace we need refer in order to add relateto[] attibute

  • Anonymous
    June 04, 2013
    Visit www.entityframeworktutorial.net to learn entity framework

  • Anonymous
    September 14, 2014
    And the key attribute does not work for me.