【问题标题】:Net Core: Generic Repository Primary Id Key Performance in Entity FrameworkNet Core:实体框架中的通用存储库主 ID 键性能
【发布时间】:2019-08-07 15:40:48
【问题描述】:

我们正在审查通用存储库模式中的两种不同方法。 目前,想要将主键映射到 Id。这样做的目的是映射到使用 Id 的通用存储库接口。下面提供了两种解决方案。

.FindPrimaryKey().Properties 对性能有何影响。在尝试查找主键时是否会导致数据库表的模式锁定?它会导致任何应用程序缓慢吗?

与部分类方法解决方案 2 相比,它的性能如何? 哪个选项在性能方面更好?

注意:架构师要求在工作场所使用存储库模式,因此实施它。知道围绕这个问题存在争议,但不是我的呼吁。

脚手架模型示例:

namespace Datatest
{
    public partial class Property
    {
        public int Property { get; set; }
        public int DocumentId { get; set; }
        public string Address { get; set; }
    }
}

所有表的示例通用基础存储库:

    public T Get(int id)
    {
        return Table.Find(id);
    }
    public async Task<T> GetAsync(int id)
    {
        return await Table.FindAsync(id);
    }
    public T Single(Expression<Func<T, bool>> predicate)
    {
        return All.Single(predicate);
    }
    public async Task<T> SingleAsync(Expression<Func<T, bool>> predicate)
    {
        return await All.SingleAsync(predicate);
    }
    public T FirstOrDefault(int id)
    {
        return All.FirstOrDefault(CreateEqualityExpressionForId(id));
    }

解决方案 1:FindPrimaryKey()

Generic Repository in C# Using Entity Framework

使用 EF FindPrimaryKey()

var idName = _context.Model.FindEntityType(typeof(TEntity))
        .FindPrimaryKey().Properties.Single().Name;

解决方案 2:部分类映射

Net Core: Create Generic Repository Interface Id Mapping for All Tables Auto Code Generation

public partial class Property: IEntity
{
    [NotMapped]
    public int Id { get => PropertyId; set => PropertyId = value; }
}

【问题讨论】:

  • 停止在 EF Core 中使用存储库模式。请!你杀了它!
  • 嗨@Artur 注意:架构师要求在工作场所使用存储库模式,因此实施它。知道围绕这个问题存在争议,但不是我的呼吁,试图充分利用它
  • 尝试向你的老时尚建筑师展示你如何加入 2 个有和没有存储库模式的巨大表。在第一种情况下,整个数据将被物化,并且连接将在内存中执行,这会破坏您的性能以及多年的数据库开发和优化工作。
  • 无论如何这是另一个话题,但感谢您的意见,:) 我们已经在工作场所就这个问题进行了辩论,他们告诉我们继续前进
  • 如果您有外键和导航属性会有所帮助,但在某些随机连接的情况下则不然。无论如何,所有这些变通方法只会增加代码的复杂性,并使新团队成员花费大量时间来学习他已经熟悉的包装器框架。

标签: c# entity-framework asp.net-core .net-core entity-framework-core


【解决方案1】:

关于第一种方法(使用 EF Core 元数据服务):

首先,EF Core 是 ORM(Object Relational Mapper),这里最重要的是 Mapper

其次,它使用所谓的基于代码的模型,这意味着所有的映射都是由代码而不是实际的数据库提供的(即使模型是通过现有数据库的逆向工程创建的) .

简而言之,EF Core 在运行时创建一个内存数据结构,其中包含有关类和属性的信息(元数据),以及它们到数据库表、列和关系的映射。所有这些信息都基于纯代码模型——实体类、约定、数据注释和流畅的配置。

所有 EF Core 运行时行为都基于该元数据模型。 EF Core 在内部使用它来构建查询、将查询结果映射到对象、链接导航属性、生成创建/更新/删除命令及其执行顺序、在获取真正的自动生成的主键值后更新临时 FK 属性值等。

因此元数据模型和发现服务(方法)使用优化的数据结构并且(必须)非常高效。同样,不涉及任何数据库操作。

所以第一种方法非常有效。与实际的查询构建、执行和实现相比,通过元数据服务获取 PK 属性 名称对性能的影响可以忽略不计。

第一种方法的性能也类似于您在另一种方法中使用的 EF Core Find 方法。请注意,在调用 Find 方法时,您只需传递 PK 值而不是属性。所以方法实现应该知道如何构建Where 表达式,对吧?而且它在内部的作用与建议的 sn-p 非常相似。


关于第二种方法:

它根本无法比较,因为它不起作用。可以使用基类/接口,但前提是映射了实际的属性名称 - 就像所有类都具有 Id 属性,并且使用 [Column] 映射到数据库表中的不同 列名数据注释或HasColumnName fluent API。

在您的示例中,Id 属性为[NotMapped](忽略)。这意味着 EF Core 无法映射到表列。您通过代码将其映射到另一个属性(属性 getter/setter)这一事实并不重要。 EF Core 不是一个(反)编译器,它看不到您的代码,因此无法将使用此类属性的 LINQ 查询转换为 SQL。

在 EF Core 2.x 中会导致 client evaluation(非常低效,读取整个表并在内存中应用过滤器),或者如果客户端评估为 configured to do so 则异常。在 EF Core 3.0+ it will always be an exception.

因此,如果您不删除 PropertyIdma​​p 属性 Id 之类的属性(这对于“数据库优先”模型来说很难),那么第二个“方法”应该是避免。即使您可以映射实际的Id 属性,您所节省的只是几毫秒。再说一次,当使用 Find 时,您不必担心性能,为什么还要使用使用相同(或相似)方法的方法。

【讨论】:

  • 问题在于它在 EF Core(或任何库)无法“看到”的代码中执行“映射”。是的,它会导致客户端评估或异常。我在另一篇文章的第一条评论中告诉过你。并再次阅读答案的结尾 - 即使它在今天技术上“有效” - 效率非常低,并且只有在启用客户端评估时,当您升级到 EF Core 3.0+ 时它真的会停止工作。所以我不知道你的建筑师,但我认为它“不起作用”。甚至 EF Core 设计人员也认为客户端 eval 是一个错误,并决定在下一个版本中将其删除。
  • 随着您对第二种方法的阐述,您假设 TPC 映射必须保留被忽略的属性,并且 Fluent API 不存在?
  • @DevilSuichiro 不确定我是否关注。 Fluent API 或数据注释、忽略/未映射的属性不能用于 L2E 查询。我的观点是,为了使 #2 可行,实体必须使用真实属性,最终映射到特定的列名。但可能我不明白你的问题吗?
  • 嗨,Ivan,我们正在使用 Temporal Sys 版本控制表,并且使列未映射,我们应该使用 [DatabaseGenerated(DatabaseGeneratedOption.Computed)] 来代替吗? [未映射] public DateTime SysUtcstartTime { get;放; } [未映射] 公共日期时间 SysUtcendTime { 获取;放; }
  • @AlanSmith5482 [NotMapped] 列被 EF 完全忽略 - 它们无法读取、更新等。考虑其他选项。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多