Udostępnij za pośrednictwem


Conventions for Code First

 

The latest preview of Code First allows you to describe a model using C# or VB.Net classes. The basic shape of the model is detected by convention and then a fluent API can be used to further refine your model.

We recently posted about our plans to support Data Annotations as another way to further describe your model. We are now looking at extending and improving the conventions that initially infer the shape of the model. This post describes the conventions we are planning to include.

Conventions are designed to provide a starting point for a model. Data Annotations or the fluent API can then be used to further describe the model or change what was detected by convention. Precedence is given to configuration via the fluent API followed by Data Annotations then convention.

Primary Key

Previously Code First would infer that a property is a primary key if the property is called ‘Id’ or ‘<class name>Id’. The only change to this convention is that once primary key properties are detected if their type is ‘int’, ‘long’ or ‘short’, they are registered as identity columns in the database by default. Primary key detection is not case sensitive.

Relationship Inverses

When defining a relationship between two types it is common to include a navigation property on both types, such as in the following example:

public class Product

{

public int ProductId { get; set; }

public string Name { get; set; }

public Category Category { get; set; }

}

public class Category

{

public int CategoryId { get; set; }

public string Name { get; set; }

public ICollection<Product> Products { get; set; }

}

Previously Code First would create two separate relationships between Product and Category but will now infer that Product.Category and Category.Products represent different ends of the same relationship. Inverse detection will occur when both types involved in the relationship define one and only one navigation property (reference to or collection) to each other. If one of the types in a relationship defines two or more navigation properties that reference the other type then inverse detection will not occur and the relationships will need to be manually configured using Data Annotations or the fluent API.

Foreign Keys

Building on the previous convention it is also common to include a foreign key property on the dependent end of a relationship, in this case BookReview.SubjectISBN:

 

 

public class BookReview

{

    public int Id { get; set; }

    public Book Subject { get; set; }

    public string SubjectISBN { get; set; }

}

public class Book

{

    [Key]

    public string ISBN { get; set; }

    public string Name { get; set; }

    public ICollection<BookReview> Reviews { get; set; }

}

Code First will now infer that any property named ‘<navigation property name><primary key property name>’ (i.e. SubjectISBN), ‘<principal class name><primary key property name>’ (i.e. BookISBN) or ‘<primary key property name>’ (i.e. ISBN), with the same data type as the primary key, represents a foreign key for the relationship. If multiple matches are found then precedence is given in the order listed above. Foreign key detection will not be case sensitive.

When a foreign key property is detected Code First will also infer the multiplicity of the relationship based on the nullability of the foreign key, if the property is nullable then the relationship is registered as optional, otherwise the relationship is registered as required, and cascade delete is turned on. The multiplicity and cascade delete behavior detected by convention can be overridden using the fluent API.

Type Discovery

Previously Code First would only include types that were declared in object sets on your derived context or manually registered through the fluent API.  Given the following example Product would have been included in your model, but Category would not:

 

 

public class ProductContext : ObjectContext

{

    public ProductContext(EntityConnection connection)

        : base(connection)

    { }

    public ObjectSet<Product> Products

    {

        get { return base.CreateObjectSet<Product>(); }

    }

}

public class Product

{

    public int Id { get; set; }

    public string Name { get; set; }

    public Category Category { get; set; }

}

public class Category

{

    public int Id { get; set; }

    public string Name { get; set; }

    public ICollection<Product> Products { get; set; }

}

Code First will now recognize that Product has a property that references Category and automatically include Category in your model. Reachability is recursive, so if Category then referenced another unregistered type, this would also be included in the model.  Reachability will also follow references to types defined in another assembly. The convention may include types that do not belong in the model; these can be removed using the StoreIgnore Data Annotation or via the fluent API as follows:

 

 

var builder = new ContextBuilder<ProductContext>();

builder.Ignore<Category>();

Complex Type Discovery

Building on the reachability convention, if Code First discovers a class definition where a primary key cannot be inferred, and no primary key is registered through Data Annotations or the fluent API, then the type will be automatically registered as a complex type. Complex type detection also requires that the type does not have properties that reference entity types and is not referenced from a collection property on another type. Given the following class definitions Code First would infer that Name is a complex type because it has no primary key:

 

 

public class Person

{

    public int PersonId { get; set; }

    public Name Name { get; set; }

}

public class Name

{

    public string Title { get; set; }

    public string FirstName { get; set; }

    public string LastName { get; set; }

}

Summary

Code First will provide an extended set of default conventions to determine the shape of a model. These conventions can be overridden using Data Annotations which in turn can be overridden via the fluent API.

We’d like to hear any feedback you have on the set of conventions as well as the rules associated with each convention.

 

 

Rowan Miller
Program Manager
ADO.Net Entity Framework Team

Comments

  • Anonymous
    June 01, 2010
    The comment has been removed

  • Anonymous
    June 01, 2010
    I like it, I have used EF, nhibernate and JPA, and I liked a lot the last one. It is a really nice way for mapping. You are doing a really good job with EF

  • Anonymous
    June 01, 2010
    I'm pretty excited about this stuff, but we can't even touch it if it doesn't have a strategy for updates built in.  Seems like things are developing really smartly though.  

  • Anonymous
    June 02, 2010
    I'm looking forward to the day when my code is my model... Will CTP3 work with the RTM of EF 4?

  • Anonymous
    June 02, 2010
    @lynn eriksen Our team is working through a road map at the moment, we don't have anything definite but we are pushing to get to an RTM as early as possible.

  • Anonymous
    June 02, 2010
    @thepaulpage Thank you for the feedback! Can you elaborate more on what you are after for an update strategy? Feel free to drop me an email; rowmil 'at' microsoft 'dot' com.

  • Anonymous
    June 02, 2010
    @Peter Stephens Yes, CTP3 of Code First works on top to EF4

  • Anonymous
    June 02, 2010
    Looks good but I hope that the next CTP fixes some existing issues as well as introducing new features. Top of my list are:

  1. Composite primary keys consisting of foreign keys without needing to introduce FKRs. I want to be able to write <JobApplication>.HasKey(u => new { u.Candidate.Id, u.Job.Id });
  2. Many-to-many relationships without needing a relationship on both entities: <Job>.Relationship<Candidate>(job => job.Applicants).FromEntity<Candidate>() maybe?
  3. Enum support!
  4. A way to specifically exclude a property without having to manuall map the entire entity. Note that I wouldn't expect to be using 1 and 2 in combination.
  • Anonymous
    June 02, 2010
    Looks good but I hope that the next CTP fixes some existing issues as well as introducing new features. Top of my list are:
  1. Composite primary keys consisting of foreign keys without needing to introduce FKRs. I want to be able to write <JobApplication>.HasKey(u => new { u.Candidate.Id, u.Job.Id });
  2. Many-to-many relationships without needing a relationship on both entities: <Job>.Relationship<Candidate>(job => job.Applicants).FromEntity<Candidate>() maybe?
  3. Enum support!
  4. A way to specifically exclude a property without having to manuall map the entire entity. Note that I wouldn't expect to be using 1 and 2 in combination.
  • Anonymous
    June 03, 2010
    ...Program Manager...

  • Anonymous
    June 04, 2010
    This is exactly the sort of thing we want to see:  convention first, annotation second, coding third.  In fact I want to avoid using the fluent interface at all  -  to my mind writing all that code undermines the value of Code First as an idea.

  • Anonymous
    June 04, 2010
    @Daniel Skinner

  1. This will be supported in the next release of Code First
  2. We are working to improve the fluent API for specifying relationships and the next release will support this pattern
  3. Enum support for the Entity Framework in general is a common ask and is something our team is looking into at the moment
  4. You will be able to ignore properties in the next Code First release, either through the fluent API of using the StoreIgnore Data Annotation
  • Anonymous
    June 10, 2010
    Specify relationships with IEnumerable<> ( not ICollection ) public class Product {    public int Id { get; set; }    public string Name { get; set; }    public Category Category { get; set; } } public class Category {    public int Id { get; set; }    public string Name { get; set; }    public IEnumerable<Product> Products { get; set; } }

  • Anonymous
    June 10, 2010
    @Geert Entity Framework in general currently relies on being able to add and remove items from collection navigation properties, which is why we require ICollection. Our team is looking at allowing more flexibility in how you define your types in the future though.

  • Anonymous
    June 13, 2010
    My experience with EF 4 and Code First is that I abandoned the new() operator and ended up with an anemic domain model as a result (one sentence summary of a long journey). Hopefully I'm just approaching the problem wrong.  I have written up a summary of the problem on StackOverflow and wonder if someone could comment here and/or there? stackoverflow.com/.../entity-framework-4-code-first-and-the-new-operator

  • Anonymous
    June 13, 2010
    Dear Rowan, currently I'm really missing the possibility, to ignore single Properties regarding to auto-mapping. It helpf to set the property as internal, but unfortunately WPF-Binding does not work with non-public-properties :( It seems that in CTP 4 this feature (StoreIgnoreAttribute) will be included. Do you have any date of release of CTP4? Or may it be possible to get something like a "weekly build" or an SVN-Link?

  • Anonymous
    June 15, 2010
    Possibility to exclude entities from DDL scripts. This will be usefull when a larger project needs to be split up into different modules/models, but some of the entities are part of 2 or more modules/models ( for query purposes)

  • Anonymous
    June 16, 2010
    Hello, I've been struggling with use of EF on enterprise class projects for some time now and I see this as a very positive step in the right direction. One of the biggest limiters to EF adoption on large projects is the difficulty in dicing up large schemas into "modules" that developers can work on independently without jumping through too many hoops. One thing I'm confused about with the current direction is how the original "fluent" bindings allowed you to create a class that inherits from EntityConfiguration and put the mappings in a separate file. This is a great thing from a separation of concerns and parallel development standpoint, which has served NHibernate well in the past. The newer posts I've seen from you guys (Microsoft) with regards to this show using the "builder" to call an entity method passing the generic and then chaining mappings. This is nice from an API standpoint, but it also requires developers to come up with their own mechanism for passing the builder into separate class libraries, if they want mappings to be contained in other parts of the code base from the builder configuration. I'd like to see some guidance or standardization in the API around allowing entity mappings to be discovered at load time through reflection or convention. My devs should be able to create classes named "ExistingClassName"Map or "ExistingClassName"Configuration and the code only stuff should just add it to the builder automatically. This allows developers to check out the smallest units of code possible from source control (individual mappings) instead of containing all mapping code in one class that has access to the builder, or again - coming up with their own way of passing the builder around. I am a big fan of the standardization in Ruby on Rails with regards to location of code and conventions - I'd like to see Entity Framework move in that direction, especially when hosted in MVC projects.

  • Anonymous
    June 17, 2010
    @Eric J I'll follow up on the question on StackOverflow. @Jens Yes StoreIgnore will be there in the next release, in the future we're also planning to include the ability to ignore properties using the fluent API. We don't have a definite date for the next release, we're working to wrap up a few features we want to get feedback on before we do another preview. We hope to have something out in the next couple of months. @Geert Similar to ignoring properties via the fluent API we are also planning to add the ability to ignore entities, meaning they would be excluded from the EF model and ultimately the store.

  • Anonymous
    June 17, 2010
    @Jayme Edwards We absolutely still support specifying configuration in derived EntityConfiguration<T> classes, in fact the Entity<T>() method internally creates an EntityConfiguration<T>, registers it and then returns it. Entity<T>() is just intended as shortcut for people who don’t need to create separate configuration classes. Automatic discovery of configuration classes is something our team has on the list to consider adding in the future.

  • Anonymous
    June 17, 2010
    Thanks so much for your timely response, it's really appreciated. One thing I ran into while using the inherited EntityConfiguration class is that the default convention for relationships seems to look for ColumnName_Id (with an underscore in it) and I was trying to figure out how to either change this convention across the board (none of the schemas' FKs have keys with an underscore) or override the columns looked for on a per entity basis. I saw an example of how to do this through the Entity<T> method, but not via the EntityConfiguration class. I also wanted to recommend that you consider renaming EntityConfiguration to EntityMap, since you are really mapping an entity to the persistent store - configuration makes me think of settings. This is just an opinion of course. I'm especially interested in the work you guys are doing around this space as I have a client that we are about to implement a data access layer for that has over 300 tables, and total implementation of the project will take probably close to a year leaving them with a significant investment in .NET assets that need to be maintainable going forward for quite a few years. Our current dilemma was NHibernate vs. EF with EF strongly preferred as we are a MS Gold Partner. However the current "stable" approach to modularization with separate designers (no way we are going to maintain that many tables on one designer) via the using statement, which breaks designer support, and the elevation of transactions across data contexts that use separate metadata but the same DB in their connection strings to distributed transactions is currently unacceptable. It's a frustrating situation and my colleagues really want me to avoid NHibernate (even though I've had great success with it on past projects) as it's not a good long term technology investment with this stuff potentially releasing in a timeframe that would allow us to use it instead. It's hard to tell with how much is changing in the code only stuff's API what a release schedule would look like however. Thanks again, Jayme

  • Anonymous
    June 18, 2010
    Was going to write them but Daniel Skinner took the words out of my mouth. Robert thanks for the reply.

  • Anonymous
    June 18, 2010
    Was going to write them but Daniel Skinner took the words out of my mouth. Robert thanks for the reply.

  • Anonymous
    June 22, 2010
    @Jayme Edwards I think you are referring to MapSingleType, using the canonical Product and Category you could do this: modelBuilder.Entity<Product>().MapSingleType(p => new {    p.ProductId,    p.Name,    CategoryId = p.Category.CategoryId }); You can call MapSingleType in a derived configuration class as well, builder.Entity<T>() is simply creating a derived configuration class so the same methods are always available. Pluggable conventions, where you can add or replace our default conventions, is something we have on the list to consider in the future. Hearing about cases such as yours where you want to change the fk convention helps us build an understanding of the requirements for this feature, so thank you for the input! The naming of EntityConfiguration vs Map is interesting, our core scenario for Code First (and why it was originally called Code Only) was to write code and have Code First generate the schema for you. We are however seeing quite a few people using Code First to map to an existing database, which it sounds like you are doing as well. Large model support is a common pain point and is something our team is actively working to improve at the moment. Rowan

  • Anonymous
    July 06, 2010
    Will there be support for enumerations? I believe it is a very important feature for EF that separates it from normal ORMs.

  • Anonymous
    July 15, 2010
    It looks like ContextBuilder no longer exists in this release. Is there a way to build a generic ObjectContext?

  • Anonymous
    July 15, 2010
    @Alexey Enum support is one of the most common asks for EF in general (not just Code First), we’re working on it at the moment. @Keith ContextBuilder has been split into ModelBuilder and DbModel The rationale behind this is explained in more detail in section 4 of the Code First walkthrough; blogs.msdn.com/.../ctp4codefirstwalkthrough.aspx But the short answer is yes you can still build an ObjectContext. If you have a derived context then the code is: var builder = new ModelBuilder(); builder.DiscoverEntitiesFromContext(typeof(MyContext)); var model = builder.CreateModel(); var context = model.CreateObjectContext<MyContext>(new SqlConnection("...")); And if you just want a non-derived context the code is: var builder = new ModelBuilder(); // Configure the model via Fluent API var model = builder.CreateModel(); var context = model.CreateObjectContext<ObjectContext>(new SqlConnection("...")); Let me know if this doesn’t answer your question

  • Anonymous
    July 16, 2010
    Is there a way to plug in your own set of conventions for table name, column name and primary id naming conventions? For example when working with existing databases we often come across custom database naming conventions, such as prefixing 'tb' for tables names (tbProduct, tbSupplier) and column names such as 'supplier_code', 'category_code' etc. It would be nice to have this configuration defined in one place as opposed to writing individual mapping classes for each entity. This would enable us to apply DRY patterns to our code. Any tips on implementing this in EF4?

  • Anonymous
    July 19, 2010
    Hi, That's very cool and sophisticated feature for EF4, I want create project with this feature, But i don't like define Identity field for Primary Key that has Integral number, Can use any Data Annotation attribute?

  • Anonymous
    July 19, 2010
    @Bikal Currently the conventions are internal to Code First but we are looking at making them public in the future. Getting feedback on scenarios that you would use custom conventions in is great and will help us shape the feature so thank you for the feedback! In the current preview (CTP4) you could implement some form of convention that scans the object model and uses the Fluent API to override what was detected by convention, there is an Entity<T>() method on ModelBuilder to avoid needing to create an actual derived configuration class. This would be a bit messy though because the reflection code needed to use generic methods is a bit clumsy. @Amir Sebt If you want to stop your integer primary keys being identity columns then you can use the StoreGenerated annotation: public class Blog {    [StoreGenerated(StoreGeneratedPattern.None)]    public int BlogId { get; set; }    ... } Or alternatively use the Fluent API as follows: modelBuilder.Entity<Blog>()    .Property(b => b.BlogId)    .StoreGeneratedPattern = System.Data.Metadata.Edm.StoreGeneratedPattern.None;

  • Anonymous
    July 22, 2010
    Hi Rowan, this is really nice news. Only thing I see missing is support for string primary keys: public class Product {    public string Key { get; set; }    /* since 'key' can be reserved word in some DBs,  maybe just this other case then */    public string ProductKey { get; set; }    public string Name { get; set; }    public Category Category { get; set; } }

  • Anonymous
    July 29, 2010
    Would be outstanding if the source was out on codeplex so we could help you drive the api design by trying some things on real projects. That being said FluentNHibernate has a lot right when it comes to specifying conventions. BIG deals would be: Controlling how the primary key column is named (ID, TableID, Table_ID) etc And all column names in general, all lowercase, camel case, underscore I know you mostly care about SQL Server, but in the past I used nhibernate to support both SQL Server and PSQL and the ability to say all columns should now be lowercase was HUGE.

  • Anonymous
    July 30, 2010
    @alemarko Code First does support string keys, if the property is named Id or <ClassName>Id it will automatically detect that it is a key. If the property is named something else then you need to add the Key data annotation or use the Fluent API to configure it as a key. @Chris Thanks for the feedback! We are looking at making the conventions pluggable to allow you to customize them. We don’t have firm plans on what this would look like or what release it might be in yet though.

  • Anonymous
    August 06, 2010
    Currently trying to retrofit CTP 4 over CTP 3, implementing Type Discovery, which, by the way, is excellent.  However, Type Discovery is not registering classes that should be inferred as ComplexTypes.  It completely ignores a property on an entity that ties to a class that has no key. Can you refer me to some additional guidance on Type Discover of complex types. Thanks!

  • Anonymous
    August 10, 2010
    @rob vettor The Type Discovery convention isn’t implemented in CTP4 so you still need to explicitly register each type with the model builder (note that if you are using DbContext then just exposing a DbSet is enough to register the type) For complex types you need to use the ComplexType method on ModelBuilder: modelBuilder.ComplexType<Address>();

  • Anonymous
    August 31, 2010
    +1 for @Bikal and @Chris regarding customized conventions.  This is really good stuff.  Since you asked for examples of the kinds of things that would be helpful, here is my wish list: ;)

  • Add to list of forms which are primary keys.  e.g.: "Syskey" is an identity PK.
  • Add to list of forms which are recognized as foreign keys.  e.g. Order.CustomerKey -> Customer.Syskey.
  • Ability to define columns with a given name as having a specific type.  e.g.: "LastUpdate" POCO field is always timestamp in the database.
  • Options for mapping POCO field names to DBMS column names.  The current convention is 1:1. The force upper/lower/camel/title case idea has been mentioned, as has hiding/expecting prefixes like "tbl".  I would add substituting underscores for upper case.  e.g. POCO field "CurrencyCode" maps to table column "currency_code" and POCO class "BillingPeriod" maps to table "BILLING_PERIOD". Obviously, the idea would be to turn any of these, as well as any other options that you decide to provide on or off as part of some kind of .config file or initialization method maybe?  Anything that allows us to stay DRY while maintaining reasonable naming conventions in our code is going to be a real boon!
  • Anonymous
    September 02, 2010
    I'm confused about how lookup tables might be configured into these conventions. I noticed that if I generate a model using regular EF, the lookup tables don't show up in the designer diagram. I am trying to use this diagram as a model for code first classes. For example, I have two tables, Events and FlaggedDates, that are connected by a lookup table DateEvents. DateEvents does not show up in the diagram. In the diagram, Event has a navigation property, FlaggedDates. If you click on it, the Mapping Details shows that it maps to DateEvents, even though DateEvents is not present in the model diagram, and even though it is named FlaggedDates, not DateEvents. For my code first project, should I include DateEvents? Should I define navigation properties for DateEvents so that EF can map the relationships between those two tables? And should the navigation property for Event be ICollection<DateEvent> instead of ICollection<FlaggedDate>? Hope this makes sense .. it just seems confusing.

  • Anonymous
    September 03, 2010
    I know that in terms of conventions you can never satisify all people. In this article you say that matching is case insensitive which make ProductId and productId equivalent. But what about product_id. removing special characters when matching will make your convention usefull to a larger public. at least: one more adept! :-) actually what will be better is to allow the model builder to take some functions/expressions that abstract out the naming conventions: for instance if a silly person want to make all the ids of it's application called ThisIsMyIdentifier, he may overrides the convention by providing the model builder the string it should look for. This is still much simpler than specifiying that for every class in his model... Otherwise, thanks guy you are going in the right direction...

  • Anonymous
    September 03, 2010
    I know that in terms of conventions you can never satisify all people. In this article you say that matching is case insensitive which make ProductId and productId equivalent. But what about product_id. removing special characters when matching will make your convention usefull to a larger public. at least: one more adept! :-) actually what will be better is to allow the model builder to take some functions/expressions that abstract out the naming conventions: for instance if a silly person want to make all the ids of it's application called ThisIsMyIdentifier, he may overrides the convention by providing the model builder the string it should look for. This is still much simpler than specifiying that for every class in his model... Otherwise, thanks guy you are going in the right direction...

  • Anonymous
    September 07, 2010
    @Joel Brown Thanks for the detailed feedback! These are a pretty consistent set of scenarios we have been hearing. @Cynthia This is known as a “join table” in EF and because the table only contains a mapping between Events and Dates it can be represented as a collection on either side of the relationship. You can still configure this using Code First, the mapping would look something like:    modelBuilder.Entity<FlaggedDate>().MapSingleType().ToTable("FlaggedDates");    modelBuilder.Entity<Event>().MapSingleType().ToTable("Events");    modelBuilder.Entity<Event>()        .HasMany(e => e.FlaggedDates)        .WithMany(d => d.Events)        .Map("DateEvents", (e, d) => new { EventId = e.Id, DateId = d.Id }); @Hicham B. We will most likely modify the existing convention to allow underscores, we’ve had a few people point this out. We are also looking at making the conventions customizable but it’s not clear exactly when we will add this feature.

  • Anonymous
    September 07, 2010
    Maybe it is a good idea to have one more convention for foreign keys. Sometimes we have more than one relation between two entities. For example we have Account class / AccountId field and Transaction class / SourceAccountId and DestinationAccountId fields. Could you detect this? You have the following conventions for primary key: ‘Id’ and ‘<class name>Id’. Could you add ‘Id<class name>’? It is important for field names similar to SourceAccountId. In Polish language we have to write IdKontoZrodlowe (IdAccountSource). Primary key will be IdKonto (IdAccount). If you add this convention you would be able to do partial name matching for English and Polish language (and probably for a few more).

  • Anonymous
    September 17, 2010
    @Anwu Yes, we support the foreign key convention you mentioned, this is “<navigation property name><primary key property name>” Thank you for the feedback on ordering of “Id” too, we will look at adding this convention. This is also another case where customizable conventions would be helpful as we aren’t going to be able to support every convention by default, it’s not clear yet when we will deliver customizable conventions but they are on our backlog.

  • Anonymous
    September 18, 2010
    The comment has been removed

  • Anonymous
    September 18, 2010
    Sorry for spaces in code, you`d better delete them to make it more readable :)

  • Anonymous
    September 21, 2010
    @Alexey Veselovskiy You are seeing the results of a bug with the parameterless overload of MapSingleType, your original code that doesn’t specify any column mapping will work in the next release. In fact we are looking at providing a shortcut so you can miss the Map call altogether and just call ; builder.Entity<Student>().ToTable(“Educ.Students”);

  • Anonymous
    December 08, 2010
    I would like to use UDFs with code first, but it seems that annotating with EdmFunctionAttribute is not enough ("cannot be translated" exception). Is it possible to auto-configure (or just configure anyhow) UDFs or configure them manually with the builder?

  • Anonymous
    November 15, 2011
    What in case i have Composite Primary Keys?? and what if my table has no primary key??

  • Anonymous
    March 21, 2012
    I think what is really missing now is the ability to auto-generate the “fluent API” code that would generate the current model as it is. In other words, run the conventions and show me exactly what the results are, expressed in code that I can choose to keep, remove, or modify.