Introduction
Entity Framework generates a set of mapping views to access the database before executing any queries or save changes to the data source. These mapping views are a set of E-SQL (Entity SQL) statements that represent the database in an abstract manner. Mapping views are cached per application domain. It means that if we create multiple instances of the same context in the same application domain, EF reuses the mapping views from the cached metadata. Entity Framework generates the mapping view per application domain when executing the first query and it will take a significant part of time to execute. Entity Framework enables us to pre-generate mapping views and include them in a compiled project.
Generating Mapping Views using EF Power Tool
Entity Framework Power Tool is one of the easiest ways to pre-generate a view. Once we have installed the EF Power Tool, we may be able to see the menu option to generate the view on a right-click of the DbContext class (Code First) / Edmx file (Database First).
Entity Framework Power Tools download link: Entity Framework Power Tools Beta 4.
Once the process is finished, we will have the following class generated.
From now on when the application is run, Entity Framework will use this pre-generated whatever to load the views that are required. This class must be re-generated whenever any changes are made to the model otherwise Ef will throw an exception.
Generating Mapping Views from Code (EF6 or later version)
Entity Framework version 6 has introduced an API to generate a view. When we use this method we have the freedom to serialize the view but we need to load the views by ourself.
The System.Data.Entity.Core.Mapping.StorageMappingItemCollection class represents a collection of items in Storage Mapping. The ObjectContext has all the information about the storage mapping collection and we can retrieve it using the metadata workspace of ObjectContext. Generally the developer uses the DbContext class to access the database and the DbContext class implements the IObjectContextAdapter interface to provide the access to the underlying ObjectContext.
By using the following code we can get a storage mapping collection.
- var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
- var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace .GetItemCollection(DataSpace.CSSpace);
Once we have the Storage Mapping Item collection, we can access the methods of GenerateViews and ComputeMappingHashValue of the StorageMappingItemCollection class.
The GenerateViews method creates a dictionary of entities for each view in the container mapping and the ComputeMappingHashValue method computes the hash value for a single container mapping and it is very useful at runtime to identify whetehr there is no change in the model after generating views.
The DbMappingViewCache class has one property, MappingHashValue, and one method, GetView. MappingHashValue must returned as generated by the ComputeMappingHashValue method. When the Entity Framework loads the view, it will first compare the hash value of the model and the hash returned by this property. If they do not match then the Entity Framework throws an exception type of EntityCommandCompilationException.
The GetView method accepts an EntitySetBase and returns a DbMappingVIew containing the Entity SQL that was generated for that that was associated with the given EntitySetBase in the dictionary generated by the GenerateViews method. The GetView method returns null when we do not have the required view.
- public class MyMappingViewCache : DbMappingViewCache
- {
- public override string MappingHashValue
- {
- get { return "df5a56995eb89ee1d0f7cd1c0bd989a32f6f6c500f524f64aba8f87706ab51ab"; }
- }
-
- public override DbMappingView GetView(EntitySetBase extent)
- {
- if (extent == null)
- {
- throw new ArgumentNullException("extent");
- }
-
- var extentName = extent.EntityContainer.Name + "." + extent.Name;
-
- if (extentName == "CodeFirstDatabase.Employee")
- {
- return GetView0();
- }
-
- if (extentName == "Model.Employees")
- {
- return GetView1();
- }
-
- return null;
- }
-
- private static DbMappingView GetView0()
- {
- return new DbMappingView(@" SELECT VALUE --Constructing Employee [CodeFirstDatabaseSchema.Employee]
- T1.Employee_Id, T1.Employee_Code, T1.Employee_Name)
- FROM (
- SELECT
- T.Id AS Employee_Id,
- T.Code AS Employee_Code,
- T.Name AS Employee_Name,
- True AS _from0
- FROM Model.Employees AS T
- ) AS T1");
- }
-
- private static DbMappingView GetView1()
- {
- return new DbMappingView(@" SELECT VALUE -- Constructing Employees [PreGeneratedView.Model.Employee]
- (T1.Employee_Id, T1.Employee_Code, T1.Employee_Name)
- FROM (
- SELECT
- T.Id AS Employee_Id,
- T.Code AS Employee_Code,
- T.Name AS Employee_Name,
- True AS _from0
- FROM CodeFirstDatabase.Employee AS T
- ) AS T1");
- }
- }
Summary
Using the method described previously we can create a pre-generated view and using the pre-generated view we can improve the performance of the Entity Framework (first time query execution).