Pre-generated mapping views
Before the Entity Framework can execute a query or save changes to the data source, it must generate a set of mapping views to access the database. These mapping views are a set of Entity SQL statement that represent the database in an abstract way, and are part of the metadata which is cached per application domain. If you create multiple instances of the same context in the same application domain, they will reuse mapping views from the cached metadata rather than regenerating them. Because mapping view generation is a significant part of the overall cost of executing the first query, the Entity Framework enables you to pre-generate mapping views and include them in the compiled project. For more information, see Performance Considerations (Entity Framework).
Generating Mapping Views with the EF Power Tools Community Edition
The easiest way to pre-generate views is to use the EF Power Tools Community Edition. Once you have the Power Tools installed you will have a menu option to Generate Views, as below.
- For Code First models right-click on the code file that contains your DbContext class.
- For EF Designer models right-click on your EDMX file.
Once the process is finished you will have a class similar to the following generated
Now when you run your application EF will use this class to load views as required. If your model changes and you do not re-generate this class then EF will throw an exception.
Generating Mapping Views from Code - EF6 Onwards
The other way to generate views is to use the APIs that EF provides. When using this method you have the freedom to serialize the views however you like, but you also need to load the views yourself.
Note
EF6 Onwards Only - The APIs shown in this section were introduced in Entity Framework 6. If you are using an earlier version this information does not apply.
Generating Views
The APIs to generate views are on the System.Data.Entity.Core.Mapping.StorageMappingItemCollection class. You can retrieve a StorageMappingCollection for a Context by using the MetadataWorkspace of an ObjectContext. If you are using the newer DbContext API then you can access this by using the IObjectContextAdapter like below, in this code we have an instance of your derived DbContext called dbContext:
var objectContext = ((IObjectContextAdapter) dbContext).ObjectContext;
var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace
.GetItemCollection(DataSpace.CSSpace);
Once you have the StorageMappingItemCollection then you can get access to the GenerateViews and ComputeMappingHashValue methods.
public Dictionary<EntitySetBase, DbMappingView> GenerateViews(IList<EdmSchemaError> errors)
public string ComputeMappingHashValue()
The first method creates a dictionary with an entry for each view in the container mapping. The second method computes a hash value for the single container mapping and is used at runtime to validate that the model has not changed since the views were pre-generated. Overrides of the two methods are provided for complex scenarios involving multiple container mappings.
When generating views you will call the GenerateViews method and then write out the resulting EntitySetBase and DbMappingView. You will also need to store the hash generated by the ComputeMappingHashValue method.
Loading Views
In order to load the views generated by the GenerateViews method, you can provide EF with a class that inherits from the DbMappingViewCache abstract class. DbMappingViewCache specifies two methods that you must implement:
public abstract string MappingHashValue { get; }
public abstract DbMappingView GetView(EntitySetBase extent);
The MappingHashValue property must return the hash generated by the ComputeMappingHashValue method. When EF is going to ask for views it will first generate and compare the hash value of the model with the hash returned by this property. If they do not match then EF will throw an EntityCommandCompilationException exception.
The GetView method will accept an EntitySetBase and you need to return a DbMappingVIew containing the EntitySql that was generated for that was associated with the given EntitySetBase in the dictionary generated by the GenerateViews method. If EF asks for a view that you do not have then GetView should return null.
The following is an extract from the DbMappingViewCache that is generated with the Power Tools as described above, in it we see one way to store and retrieve the EntitySql required.
public override string MappingHashValue
{
get { return "a0b843f03dd29abee99789e190a6fb70ce8e93dc97945d437d9a58fb8e2afd2e"; }
}
public override DbMappingView GetView(EntitySetBase extent)
{
if (extent == null)
{
throw new ArgumentNullException("extent");
}
var extentName = extent.EntityContainer.Name + "." + extent.Name;
if (extentName == "BlogContext.Blogs")
{
return GetView2();
}
if (extentName == "BlogContext.Posts")
{
return GetView3();
}
return null;
}
private static DbMappingView GetView2()
{
return new DbMappingView(@"
SELECT VALUE -- Constructing Blogs
[BlogApp.Models.Blog](T1.Blog_BlogId, T1.Blog_Test, T1.Blog_title, T1.Blog_Active, T1.Blog_SomeDecimal)
FROM (
SELECT
T.BlogId AS Blog_BlogId,
T.Test AS Blog_Test,
T.title AS Blog_title,
T.Active AS Blog_Active,
T.SomeDecimal AS Blog_SomeDecimal,
True AS _from0
FROM CodeFirstDatabase.Blog AS T
) AS T1");
}
To have EF use your DbMappingViewCache you add use the DbMappingViewCacheTypeAttribute, specifying the context that it was created for. In the code below we associate the BlogContext with the MyMappingViewCache class.
[assembly: DbMappingViewCacheType(typeof(BlogContext), typeof(MyMappingViewCache))]
For more complex scenarios, mapping view cache instances can be provided by specifying a mapping view cache factory. This can be done by implementing the abstract class System.Data.Entity.Infrastructure.MappingViews.DbMappingViewCacheFactory. The instance of the mapping view cache factory that is used can be retrieved or set using the StorageMappingItemCollection.MappingViewCacheFactoryproperty.