预生成的映射视图

在实体框架可以执行查询或将更改保存到数据源之前,它必须生成一组本地查询视图才能访问数据库。 这些映射视图是一组实体 SQL 语句,它们以抽象的方式表示数据库,是按应用程序域缓存的元数据的一部分。 如果在同一个应用程序域中创建了多个相同上下文的实例,则这些实例将重用缓存元数据中的映射视图,而不是重新生成视图。 由于映射视图生成是执行第一个查询的总体成本的重要部分,因此实体框架允许预先生成映射视图,并将它们包含在已编译的项目中。 有关详细信息,请参阅性能注意事项(实体框架)

使用 EF Power Tools 社区版生成映射视图

预生成视图的最简单方法是使用 EF Power Tools 社区版。 安装 Power Tools 后,会看到一个用于生成视图的菜单选项,如下所示。

  • 对于 Code First 模型,右键单击包含 DbContext 类的代码文件
  • 对于 EF 设计器模型,右键单击 EDMX 文件。

generate Views

完成此过程后,会生成一个类似于下面的类

generated Views

现在,运行应用程序时,EF 将使用此类根据需要加载视图。 如果模型发生更改,并且不重新生成此类,则 EF 会引发异常。

从代码生成映射视图 - EF6 以上版本

生成视图的另一种方法是使用 EF 提供的 API。 使用此方法时,可以自由地序列化视图,但也需要自行加载视图。

注意

仅限 EF6 以上版本 - 本部分中所示的 API 是实体框架 6 中引入的。 如果使用的是早期版本,则此信息不适用。

生成视图

用于生成视图的 API 位于 System.Data.Entity.Core.Mapping.StorageMappingItemCollection 类中。 可以使用 ObjectContext 的 MetadataWorkspace 检索上下文的 StorageMappingCollection。 如果使用的是较新的 DbContext API,则可以使用如下所示的 IObjectContextAdapter 进行访问,在此代码中,我们有一个名为 dbContext 的派生 DbContext 实例:

    var objectContext = ((IObjectContextAdapter) dbContext).ObjectContext;
    var  mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace
                                                                        .GetItemCollection(DataSpace.CSSpace);

拥有 StorageMappingItemCollection 后,可以访问 GenerateViews 和 ComputeMappingHashValue 方法。

    public Dictionary<EntitySetBase, DbMappingView> GenerateViews(IList<EdmSchemaError> errors)
    public string ComputeMappingHashValue()

第一种方法为容器映射中的每个视图创建一个包含条目的字典。 第二种方法为单个容器映射计算哈希值,并在运行时使用该哈希值验证模型是否自预生成视图以来未发生更改。 对于涉及多个容器映射的复杂场景,提供了两种方法的替代方法。

生成视图时,将调用 GenerateViews 方法,然后写出生成的 EntitySetBase 和 DbMappingView。 还需要存储 ComputeMappingHashValue 方法生成的哈希。

加载视图

为了加载 GenerateViews 方法生成的视图,可以为 EF 提供从 DbMappingViewCache 抽象类继承的类。 DbMappingViewCache 指定必须实现的两种方法:

    public abstract string MappingHashValue { get; }
    public abstract DbMappingView GetView(EntitySetBase extent);

MappingHashValue 属性必须返回 ComputeMappingHashValue 方法生成的哈希值。 EF 请求视图时,它将首先生成哈希值并将模型的哈希值与此属性返回的哈希值进行比较。 如果二者不匹配,EF 将引发 EntityCommandCompilationException 异常。

GetView 方法将接受 EntitySetBase,并且你需要返回一个 DbMappingVIew,其中包含生成的 EntitySql,该 EntitySql 与由 GenerateViews 方法生成的字典中的给定 EntitySetBase 相关联。 如果 EF 要求提供不具有的视图,则 GetView 应返回 null。

下面是使用上文所述的 Power Tools 生成的 DbMappingViewCache 中摘录的内容,其中提供了一种存储和检索所需 EntitySql 的方法。

    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");
    }

若要让 EF 使用 DbMappingViewCache,请使用 DbMappingViewCacheTypeAttribute 指定为其创建的上下文。 在下面的代码中,我们将 BlogContext 与 MyMappingViewCache 类相关联。

    [assembly: DbMappingViewCacheType(typeof(BlogContext), typeof(MyMappingViewCache))]

对于更复杂的场景,可以通过指定映射视图缓存工厂来提供映射视图缓存实例。 此操作可通过实现抽象类 System.Data.Entity.Infrastructure.MappingViews.DbMappingViewCacheFactory 来完成。 可以使用 StorageMappingItemCollection.MappingViewCacheFactoryproperty 检索或设置所使用的映射视图缓存工厂的实例。