Custom Code First Conventions

Code First includes a set of simple, model-wide behaviors that provide sensible configuration defaults for the parts of your model that have not been explicitly configured using Data Annotations or the Fluent API. These default behaviors are referred to as Conventions. One commonly requested feature is the ability to add your own conventions.

The Custom Conventions feature will let you define your own conventions to provide custom configuration defaults for your model. Ther are two main types of Conventions, Configuration Conventions and Model Conventions.

Design Meeting Notes:

Here are links to various design meeting notes where conventions discussions took place.

https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20September%2020%2c%202012
https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20October%2011%2c%202012
https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20October%2025%2c%202012
https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20November%208%2c%202012
https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20November%2015%2c%202012
https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20December%2013%2c%202012
https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20March%2013%2c%202013
https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20April%2011%2c%202013
https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20May%2016%2c%202013
https://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20-%20June%206%2c%202013

Configuration Conventions

Configuration Conventions are a way to configure a set of objects, without overriding the explicit configuration provided in the Fluent API. You can define a convention inside your OnModelCreating event or in a Convention Class in a similar way to how you would define normal entity mappings with the Fluent API.

To start with we will show a commonly requested example. Some people want to configure EF to set the precision of all decimal properties in their model. With Configuration Conventions this can be achieved with the following piece of code:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Properties<decimal>()
        .Configure(config => config.HasPrecision(10, 2));
}
This code creates a convention that tells EF that all properties on all entities should be configured to have a precision of 10 and a scale of 2.

This can be conceptualized as the properties method returning a list of all properties in your model. The generic type parameter then filters all properties to only be those that are of type decimal, and finally you call configure.

Configure is where you decide what to do with all the properties that you haven’t filtered out in the preceding method calls.

Note: The configuration that is passed to configure is a flattened representation of all configuration options. If you configure a property that doesn’t make sense for something that you haven’t filtered out then it will throw an exception. This means that if you tried to configure all integers to have a precision of 10 then you will get an error.

Another example of Configuration Conventions is when you want to define a different primary key convention, in this case we want all properties that end with “Key” to be a primary key.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Properties()
        .Where(prop => prop.Name.EndsWith("Key"))
        .Configure(config => config.IsKey());
}

Convention Classes

Another way of using the Configuration Convention API is to create a class that inherits from the Convention class in the System.Data.Entity.ModelConfiguration.Conventions namespace.


public class CustomKeyConvention : Convention
{
    public CustomKeyConvention()
    {
        Properties()
            .Where(prop => prop.Name.EndsWith("Key"))
            .Configure(config => config.IsKey());
    }
}


To enable a Convention Class, call Add() on DbModelBuilder.Conventions.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Add<CustomKeyConvention>();
}

Model Conventions

The last type of convention is based on the underlying model metadata. There are two types of model conventions, Conceptual (C-Space) and Store (S-Space). This distinction indicates where in the pipeline a convention is executed. A C-Space convention is applied to the model that the application builds, whereas an S-Space convention is applied to the version of the model.

Creating a model convention will require creating a class that implements either IConceptualModelConvention or IStoreModelConvention. These interfaces both accept a generic that can be of type MetadataItem which is used to filter the type data type that the convention applies to.

Note: Additional work is also being done to consolidate and improve the model metadata API used by these conventions. For more information, see Work Item 555.

public class ModelBasedConvention : IStoreModelConvention<EdmProperty>
{
    public void Apply(EdmProperty property, System.Data.Entity.Infrastructure.DbModel model)
    {
        if (property.PrimitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Decimal
            && property.Scale == null)
        {
            property.Scale = 4;
        }
    }
}
These conventions are always applied after the configuration-based conventions.

They are applied in order according to their interface type:
  1. IConceptualModelConvention
  2. IStoreModelConvention

Model Conventions are added to the conventions collection on modelBuilder the same as Convention Classes.

Convention Order

The order in which conventions get applied can be important, and there are some differences between Configuration Conventions and Model Conventions. Most of the time you will not need to worry about these orderings, just adding a convention to the collection will work most of the time. But if you run into a situation where two conventions want to change the same thing on the same object then you can run into problems. This usually happens when you are trying to configure something that a built-in convention has already configured.

Configuration Conventions

The first thing to understand is that the fluent API will take precedence over any Configuration Convention. So if you explicitly configure a property to have a precision of 5, and have a Configuration Convention that sets precision to 10 more generally, then the explicit configuration will win.

The next important consideration is that, just like the fluent API, if you write two conventions that configure the same property on the same type then the last one added will take effect. This is important if you have conventions that overlap, in that two conventions could configure the same property of the same type, and you can arrange the order that you declare the conventions in OnModelCreating to control which runs last.

If the convention that you are overlapping with is not one of your own, because it is a built-in convention for example, then you will need to use the AddBefore and AddAfter methods to control which runs last.

modelBuilder.Conventions.AddBefore<DecimalPropertyConvention>(
    new DefaultDecimalScaleConvention());

You can also use the Remove method of the Conventions collection to Remove a convention from the list if you do not need it and it is conflicting with other conventions. This is probably a good idea if you are completely replacing one of the built-in conventions.

Model Conventions

Model Conventions also work in a last wins manner and can override anything that has been set by conventions, attributes, or Fluent API. However, they can be written so that they don't override previous configuration if you want.

You can stop a Model Convention from overriding configuration by checking that a property is null. If a property has already been configured before a Model Convention executes then it will be null. So you can create a Model Convention that only changes things that have not already been configured by checking for null before-hand. Which you can see in the Model Convention example we showed above.

Last edited Sep 16, 2013 at 6:26 PM by glennc, version 15

Comments

glennc Sep 16, 2013 at 10:41 PM 
@mrutting, yes. IStoreConvention would be the place to do what you are asking. There is a comment with an example of how to do something similar to what you are asking here: https://entityframework.codeplex.com/workitem/1226. We should also have some examples in the Model Conventions documentation when we get it finished.

mrutting Aug 23, 2013 at 8:20 AM 
If I wanted to remove the underscore that is generated in the database column names for foreign keys that are not explicitly present in my pocos (f. ex. MyForeignKeyId in stead of MyForeignKey_Id). Would IStoreModelConvention<EdmProperty> be the correct place to do that?

johncrim Jul 24, 2013 at 1:30 AM 
Note that this document is not current as of 6.0.0-rc1 - eg IEdmConvention is gone, and IConfigurationConvention<TMemberInfo, TConfiguration>is internal.

BriceLambson Dec 19, 2012 at 5:44 PM 
@ignatandrei, Code First builds up an in-memory Entity Data Model based on your code, custom configuration, and these Code First Conventions. The model/database-first workflow loads it from an edmx file. In other words, this metadata is available regardless of which workflow you are using. However, the conventions described in this feature spec, will only influence models created using Code First.

ignatandrei Dec 14, 2012 at 9:15 AM 
How will CodeFirst know about EdmProperty or about property.PrimitiveType.PrimitiveTypeKind ? That means including edmx is mandatory, right? And the connection string should be the EDMX one, not code first, right?

BriceLambson Oct 11, 2012 at 7:46 PM 
@interscape, this feature is currently only for models that are created using Code First and does not apply to models created using the designer.

interscape Oct 11, 2012 at 1:56 AM 
Will we be able to apply default conventions to EDMX-based models?