首页 > 其他分享 >EF Core预编译模型Compiled Model

EF Core预编译模型Compiled Model

时间:2023-11-20 11:00:54浏览次数:48  
标签:Core string Compiled 模型 EF runtimeEntityType 编译

前言

最近还在和 npgsqlEF Core 斗争,由于 EF Core 暂时还不支持 AOT,因此在 AOT 应用程序中使用 EF Core 时,会提示问题:

image

听这个意思,似乎使用 Compiled Model 可以解决问题,于是就又研究了一下 EF Core 的这个功能。

在 EF Core 中,模型根据实体类和配置构建,默认情况下,每次创建一个新的 DbContext 实例时,EF Core 都会构建模型。对于需要频繁创建 DbContext 实例的应用程序,这可能会导致性能问题。

Entity Framework Core(EF Core)的预编译模型(Compiled Model)对应提供了一种优化,在 EF Core 6 preview 5 中首次增加了这个功能,可以让设计人员预编译模型,避免在后续执行查询时动态生成模型。

预编译模型的优势

  1. 性能提升:通过预编译模型,可以减少应用程序启动时的开销,特别是对于大型模型。

此处的启动时间,指 DbContext 的首次启动时间,由于延迟查询的机制,一般 DbContext 并不会在新建对象时完成启动,而是在首次执行插入或者查询时完成这个过程。

参考下图(来自参考 1):
image

显然,随着模型的规模增大,启动时间线性增长;但是使用预编译模型后,启动时间和模型大小基本无关,保持在一个极低的水平。

  1. 一致性:确保每个 DbContext 实例使用相同的模型配置。

使用预编译模型

  1. 生成编译模型
    使用 EF Core 命令行工具,命令:
dotnet ef dbcontext optimize

这将生成 DbContext 的预编译模型。我只有一个 POCO 类,生成了 3 个文件,类名称就是文件名称。

[DbContext(typeof(DataContext))]
public partial class DataContextModel : RuntimeModel
{
    static DataContextModel()
    {
        var model = new DataContextModel();
        model.Initialize();
        model.Customize();
        _instance = model;
    }

    private static DataContextModel _instance;
    public static IModel Instance => _instance;

    partial void Initialize();

    partial void Customize();
}
public partial class DataContextModel
{
    partial void Initialize()
    {
        var deviceDatum = DeviceDatumEntityType.Create(this);

        DeviceDatumEntityType.CreateAnnotations(deviceDatum);

        AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
        AddAnnotation("ProductVersion", "8.0.0-rc.2.23480.1");
        AddAnnotation("Relational:MaxIdentifierLength", 63);
        AddRuntimeAnnotation("Relational:RelationalModel", CreateRelationalModel());
    }

    private IRelationalModel CreateRelationalModel()
    {
	    // 这里面非常多描述类型的代码,节约篇幅我就不写全了。
        var relationalModel = new RelationalModel(this);

        var deviceDatum = FindEntityType("AspireSample.DeviceDatum")!;

        var defaultTableMappings = new List<TableMappingBase<ColumnMappingBase>>();
        deviceDatum.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings);
        
	    ....
	    
        return relationalModel.MakeReadOnly();
    }
}
internal partial class DeviceDatumEntityType
{
    public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
    {
        var runtimeEntityType = model.AddEntityType(
            "AspireSample.DeviceDatum",
            typeof(DeviceDatum),
            baseEntityType);

        var id = runtimeEntityType.AddProperty(
            "Id",
            typeof(string),
            propertyInfo: typeof(DeviceDatum).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
            fieldInfo: typeof(DeviceDatum).GetField("<Id>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
            afterSaveBehavior: PropertySaveBehavior.Throw);
        id.TypeMapping = StringTypeMapping.Default.Clone(
            comparer: new ValueComparer<string>(
                (string v1, string v2) => v1 == v2,
                (string v) => v.GetHashCode(),
                (string v) => v),
            keyComparer: new ValueComparer<string>(
                (string v1, string v2) => v1 == v2,
                (string v) => v.GetHashCode(),
                (string v) => v),
            providerValueComparer: new ValueComparer<string>(
                (string v1, string v2) => v1 == v2,
                (string v) => v.GetHashCode(),
                (string v) => v),
            mappingInfo: new RelationalTypeMappingInfo(
                dbType: System.Data.DbType.String));
                
        ......

        var key = runtimeEntityType.AddKey(
            new[] { id });
        runtimeEntityType.SetPrimaryKey(key);

        return runtimeEntityType;
    }

    public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
    {
        runtimeEntityType.AddAnnotation("Relational:FunctionName", null);
        runtimeEntityType.AddAnnotation("Relational:Schema", null);
        runtimeEntityType.AddAnnotation("Relational:SqlQuery", null);
        runtimeEntityType.AddAnnotation("Relational:TableName", "devicedata");
        runtimeEntityType.AddAnnotation("Relational:ViewName", null);
        runtimeEntityType.AddAnnotation("Relational:ViewSchema", null);

        Customize(runtimeEntityType);
    }

    static partial void Customize(RuntimeEntityType runtimeEntityType);
}

可以看到,优化工具帮我们生成了非常多的代码,尤其是与类型描述相关的代码,因此,如果我们修改模型,那么必须重新执行一遍对应的生成指令。

  1. 修改 DbContext
    修改你的 DbContext 类,让它使用这个预编译模型。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (!optionsBuilder.IsConfigured)
    {
        // 指定编译模型的使用
        optionsBuilder.UseModel(CompiledModels.MyCompiledModel.Instance);
    }
}

权衡利弊

核心优点:

  1. 提升启动速度,对实体类型较多的 DbContext 尤其显著。

缺点:

  1. 不支持全局查询过滤、Lazy loading proxiesChange tracking proxies 和自定义 IModelCacheKeyFactory
  2. 每次修改模型都必须重新生成优化代码。

不支持的东西很多,每次修改模型还需要重新生成就非常麻烦,因此,如果不是真的启动速度已经非常慢了不建议使用

后记

我在使用 EF Core 的 Compiled Model 之后依然提示相同的错误,后来发现错误是从 Reflection 相关类爆出的,而不是 EF Core 的相关类。所以错误里说的 Compiled Model 和 EF Core 的 Compiled Model 概念不同,应该指 AOT 不支持反射中动态加载,需要提前编译。现在 EF Core 还没完全准备好,因此,重申一下,EF Core 8 暂时不支持 AOT

参考

标签:Core,string,Compiled,模型,EF,runtimeEntityType,编译
From: https://www.cnblogs.com/podolski/p/17843466.html

相关文章

  • Codeforces Round 910 (Div. 2)
    CodeforcesRound910(Div.2)基本情况做A题的速度比之前快多了,大概20分钟搞定。B题想了一个贪心错解,想用链表实现,但是不熟练,实现太慢,而且还被hack了。但是自己hack掉了,造数据上进步。B.MilenaandAdmirer贪心思路发现一个大于下一个的数,直接对半分,如果不能对半就小的在......
  • Data Definition
    USEUSE语句允许您更改当前的keyspace(用于连接当前执行的keyspace)。CQL中的一些对象被绑定到一个keyspace(表,用户定义类型,函数,…),当这些对象在没有完全限定名的情况下,当前被引用的keyspace是默认使用的键空间(也就是说,没有前缀键空间名称)。USE语句只是接受指定的键空间,并使用......
  • 解决UnboundLocalError: local variable 'time' referenced before assignment
    解决UnboundLocalError:localvariable'time'referencedbeforeassignment介绍在Python开发中,经常会遇到UnboundLocalError:localvariable'xxx'referencedbeforeassignment的错误。这个错误通常发生在在一个函数内部,尝试访问一个在函数内定义的局部变量之前。这篇文章将......
  • Codeforces Round 909 (Div. 3)
    CodeforcesRound909(Div.3)基本情况第一次在CF上AC了超过一道题。(毕竟是Div3)B题卡住了很久。D没有深入思考。[B.250ThousandTonsofTNT](Problem-B-Codeforces)一开始死活过不了的代码:#include<iostream>#include<cstdio>#include<cstring>#inc......
  • Unity学习笔记--数据持久化之PlayerPrefs的使用
    数据持久化PlayerPrefs相关PlayerPrefs是Unity游戏引擎中的一个类,用于在游戏中存储和访问玩家的偏好设置和数据。它可以用来保存玩家的游戏进度、设置选项、最高分数等信息。PlayerPrefs将数据存储在本地文件中,因此可以在游戏重新启动时保持数据的持久性。//PlayerPrefs的数据......
  • Makefile - What is a Makefile and how does it work?
    Ifyouwanttorunorupdateataskwhencertainfilesareupdated,the make utilitycancomeinhandy.The make utilityrequiresafile, Makefile (or makefile),whichdefinessetoftaskstobeexecuted.Youmayhaveused make tocompileaprogramf......
  • Adobe AfterEffects CC 2021 Mac Intel芯片 中文版
    软件介绍AfterEffects2021是Adobe推出的一款专业非线性视频编辑软件,经常需要编辑视频的人士对这款软件应该都不陌生吧。通过AfterEffects2021可以轻松制作火焰、冰雪、下雨等等视频后期特效,且效果都非常真实酷炫。软件自带丰富的动画资源库,满足用户的使用需求。神秘文件获......
  • Adobe AfterEffects CC 2021 Mac M1芯片 中文版
    软件介绍AfterEffects2021是Adobe推出的一款专业非线性视频编辑软件,经常需要编辑视频的人士对这款软件应该都不陌生吧。通过AfterEffects2021可以轻松制作火焰、冰雪、下雨等等视频后期特效,且效果都非常真实酷炫。软件自带丰富的动画资源库,满足用户的使用需求。神秘文件获......
  • 探索CodeFuse:AI助力编程效率的新高度
    引言在人工智能与软件开发的交汇点,CodeFuse以其独树一帜的技术实力和应用广度,正引领着一场编程界的AI革命。作为蚂蚁集团自研的代码生成模型,CodeFuse不仅在多语言编程支持、代码生成和优化方面展现出卓越性能,而且在提升开发效率、降低编程门槛方面具有革命性意义。CodeFuse技术深度......
  • NEFU OJ Problem 1489 青蛙赶路 题解【动态规划DP】
    Problem:GTimeLimit:2000msMemoryLimit:65535KDescription有一只青蛙,每秒选择走1米或跳m米,青蛙体力不足,所以不能连续两秒都在跳。青蛙将移动到[l,r]之间,它想知道有多少种不同的方式来实现其目标。两种方式是不同的,当且仅当它们移动不同的米或花费不同的秒,或者是在一秒......