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. The heart of this new feature is the conventions interfaces that you use to implement your own custom conventions. These interfaces fall into two main categories: Configuration-based and Model-based.
An easier way to define configuration based conventions is by using Lightweight conventions.
Lightweight conventions
Lightweight conventions are a way to define a convention inside your OnModelCreating event 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 Lightweight Conventions this can be achieved with the following piece of code:
protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Add( entities => entities.Properties() .Where(property => property.PropertyType == typeof(decimal)) .Configure(config => config.Precision = 10)); }
This can be conceptualized as the “entities” containing a list of all entities in your model. You then use the properties method to say “for all properties on all entities”. The where 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 entities and 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 do nothing. This means that if you tried to configure all integers to have a precision of 10 then it would be ignored.
Another example of Lightweight 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.
protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Add( entities => entities.Properties() .Where(prop => prop.Name.EndsWith("Key")) .Configure(config => config.IsKey())); }
Configuration-based conventions
The first type of convention is based on reflection and configuration objects and can be created using IConfigurationConvention. The configuration objects used by this type of convention are similar to the Fluent API objects that are typically used when overriding DbContext.OnModelCreating().class DefaultDateTimeColumnTypeConvention : IConfigurationConvention<PropertyInfo, DateTimePropertyConfiguration> { publicvoid Apply( PropertyInfo propertyInfo, Func<DateTimePropertyConfiguration> configuration) { if (configuration().ColumnType == null) { configuration().ColumnType = "datetime2"; } } }
- Type
- PropertyInfo
- ModelConfiguration
- PropertyConfiguration
- NavigationPropertyConfiguration
- PrimitivePropertyConfiguration
- DateTimePropertyConfiguration
- DecimalPropertyConfiguration
- LengthPropertyConfiguration
- BinaryPropertyConfiguration
- StringPropertyConfiguration
- StructuralTypeConfiguration
- ComplexTypeConfiguration
- EntityTypeConfiguration
Model-based conventions
The second type of convention is based on the underlying model metadata. The interfaces used to create these conventions are IEdmConvention, IDbConvention & IDbMappingConvention. This type of convention gives you more control than configuration-based conventions because it allow you to directly manipulate the model metadata that gets used by Entity Framework.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.
class ModelBasedConvention : IEdmConvention <EdmProperty> { publicvoid Apply(EdmProperty property, EdmModel model) { if (property.PrimitiveType.PrimitiveTypeKind == PrimitiveTypeKind.Decimal && property.Scale == null) { property.Scale = 4; } } }
- IEdmConvention
- IDbConvention
- IDbMappingConvention
Adding conventions
To enable a custom convention to be applied during model creation, call Add() on DbModelBuilder.Conventions.protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Add<DefaultDateTimeColumnTypeConvention>(); }
modelBuilder.Conventions.AddBefore<DecimalPropertyConvention>(
new DefaultDecimalScaleConvention());