【问题标题】:Difference between DbSet<T> property and Set<T>() function in EF Core?EF Core 中 DbSet<T> 属性和 Set<T>() 函数之间的区别?
【发布时间】:2018-11-26 02:21:34
【问题描述】:

鉴于这种情况:

public class FooContext : DbContext 
{
    public FooContext(DbContextOptions<FooContext> opts) : base(opts)
    { }

    public DbSet<Bar> Bars { get; set; }
}

我可以通过两种方式联系Bar

fooContext.Bars.Add(new Bar()); // Approach 1

fooContext.Set<Bar>().Add(new Bar()); // Approach 2

这两种方法有什么区别?

我尝试通过以下方式回答我自己的问题:

但是我找不到任何好的解释来说明两者中的哪一个用于哪个目的。有什么区别?或者更重要的是:我应该在哪里以及如何在文档中找到它?

【问题讨论】:

  • 他们似乎在做同样的事情:github.com/aspnet/EntityFrameworkCore/blob/master/src/EFCore/… 第 245 行。我认为不同之处在于Set&lt;Bar&gt; 将使用 IDbSetSource 在运行时查找类型并计算出它需要什么表,而 @ 987654336@ 属性在编译时执行此操作。
  • @Ben Ahh 当然可以。几年后,我仍然需要偶尔提醒一下,微软在这个领域几乎开源了任何东西。谢谢!
  • 有没有人在具有大量映射实体的上下文中使用 DbSet 属性与 Set() 方法进行性能测试?我正在一个包含大约 200 个实体的应用程序中工作。 DbContext 可以为所有实体提供 200 个属性,或者 Set 方法可以用于特定位置所需的各个实体。我很好奇使用这么多 DbSet 属性启动上下文的工作是否可能比仅根据需要使用 Set 方法要慢。应用程序将使用 DI 在 MVC 应用程序中注入 DbContext,因此将为每个请求创建一个新对象。

标签: c# entity-framework-core


【解决方案1】:

他们做同样的事情。真正的问题是你什么时候会使用其中一个。

当您知道要使用的实体类型时,您可以使用 DbSet。您只需编写 DbContext 名称,然后编写实体类型名称,您就可以使用可用的实体方法创建、读取、更新或删除该实体的条目。您知道自己想要什么,也知道在哪里做。

当您不知道要使用的实体类型时,您可以使用 Set。比方说,您想构建一个类来执行您的存储库功能,以创建、读取、更新和删除实体的条目。你希望这个类是可重用的,这样你就可以在它上面传递一个 DbContext 并且它将使用相同的创建、读取、更新和删除方法。您不确定它将用于什么 DbContext 或 DbContext 将具有什么 DbSet。这是您使用泛型的时候,这样您的类就可以被任何 DbContext 用于任何 DbSet。

这是一个类示例,可用于在任何 DbContext 中的任何 DbSet 上创建任何实体

public class Repository<TDbContext> where TDbContext : DbContext
{
    private TDbContext _context { get; }

    public Repository(TDbContext context)
    {
       _context = context;
    }

    public TEntity Create<TEntity>(TEntity entity) where TEntity : class
    {
        if(entity != null)
        {
            var dataSet = _context.Set<TEntity>();

            if(entity is IEnumerable)
            {
                dataSet.AddRange(entity);
            }
            else
            {
                dataSet.Add(entity);
            }

            _context.SaveChanges();


        }

        return entity;
    }
}

这就是它的使用方法。

var dbContext01 = new DbContext01();
var dbContext02 = new DbContext02();

var repository01 = new Repository<DbContext01>(dbContext01);
var repository02 = new Repository<DbContext02>(dbContext02);

repository01.Create(new EntityOnDbContext01 {
    Property01A = "String",
    Property01B = "String"
});

repository02.Create(new EntityOnDbContext02 {
    Property02A = 12345,
    Property02B = 12345
});

如果您想了解更多关于泛型的信息,请点击此处。它超级棒。

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/

【讨论】:

    【解决方案2】:

    很遗憾,目前您在官方文档中找不到解释,主要是因为所有这些在功能上都是等效的。

    首先,DbConextgeneric 方法,如Add&lt;TEntity&gt;Remove&lt;TEntity&gt;Attach&lt;TEntity&gt; 等,完全等效于对应的DbSet&lt;TEntity&gt; 方法(实际上目前它们是实现后者,即DbSet 方法只需调用相应的DbContext 泛型方法)。您使用哪一种只是口味问题。

    其次,DbSet&lt;TEntity&gt; 属性和Set&lt;TEntity&gt; 方法在功能上是等价的,但是确实有一些非功能上的区别。

    DbSet 属性在上下文创建时填充一次,而Set 方法始终执行查找,因此DbSet 属性访问应该比Set 方法更快(尽管不重要)。

    重要的区别实际上是 EF Core Including & Excluding Types 约定:

    按照惯例,在您的上下文的DbSet 属性中公开的类型包含在您的模型中。此外,OnModelCreating 方法中提到的类型也包括在内。

    因此,虽然您可以在不暴露 DbSet 属性的情况下保留 DbContext 并仅使用 Set 方法,但如果这样做,您必须通过添加 OnModelCreating 明确告诉 EF Core 哪些是您的实体类型为每个实体类型调用modelBuilder.Entity&lt;TEntity&gt;();(这是文档所指的OnModelCreating 方法中提到的类型)。

    【讨论】:

    • 我不能同意 DbSet 方法与 DbContext.Add 等的实现相同。我没有查看代码,但在 EfCore 6 中我确实发现了一些差异。当我尝试使用 DbContext.Add 添加实体并且该实体作为 DbSet 添加到我的 DbContext 中时,出现了一些问题。出于某种原因,当实体作为 DbSet 在 DbContext 中时,我需要使用: DbContext.MyEntityDbSet.Add 而不是 DbContext.Add
    【解决方案3】:

    它们是相同的并且实际上返回相同的DbSet 实例。

    var options = //...;
    
    using (var ctx = new FooContext(options))
    {
        // true
        bool isSame = ReferenceEquals(ctx.Bars, ctx.Set<Bar>());
    }
    

    在您的DbContext 中不包含DbSet 属性的一个用例是您想要对消费者隐藏实体类型。 (例如,充当many-to-many relationship 的连接表的实体)。然后,您可以将该实体标记为internal class,这样消费者也无法使用Set&lt;&gt; 访问它。

    此外,如果您不公开DbSet 属性,则需要显式配置实体,否则您将收到以下异常:

    //throws System.InvalidOperationException: 'The entity type 'Foo' was not found. Ensure that the entity type has been added to the model.'
    ctx.Set<Foo>().Add(new Foo());    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-15
      • 2011-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-07
      • 2016-09-07
      相关资源
      最近更新 更多