EF Core中的DbSet <t>属性和Set <t>()函数之间的区别?

时间:2018-11-25 16:26:04

标签: c# entity-framework-core

鉴于这种情况:

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

两种方法有什么区别?

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

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

3 个答案:

答案 0 :(得分:3)

他们做的完全一样。真正的问题是,什么时候可以使用另一个。

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

当您不知道要使用的实体类型时,可以使用Set。可以说,您想构建一个类,该类执行您的存储库功能,以创建,读取,更新和删除实体的条目。您希望此类可重用,以便您可以在其上传递DbContext,并且它将使用相同的create,read,update和delete方法。您不确定要在哪个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/

答案 1 :(得分:2)

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

首先,DbConext泛型方法(例如Add<TEntity>Remove<TEntity>Attach<TEntity>等),与相应的{{1 }}方法(实际上,它们实际上是后者的实现,即DbSet<TEntity>方法仅调用相应的DbSet通用方法)。您使用哪一个只是一个品味问题。

第二,DbContext属性和DbSet<TEntity>方法在功能上是等效的,但是确实有一些非功能上的差异。

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

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

  

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

因此,尽管您可以保留OnModelCreating而不暴露DbContext属性,并且仅使用DbSet方法工作,但是如果这样做,则必须明确地告诉EF Core哪些是您的实体类型,在每种实体类型的Set中添加对OnModelCreating的调用(这就是文档在modelBuilder.Entity<TEntity>();方法中提到的类型的含义)。 / p>

答案 2 :(得分:0)

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

var options = //...;

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

在您的DbSet中不包括DbContext属性的一个用例是,您想向使用者隐藏实体类型。 (例如,充当many-to-many relationship的联接表的实体)。然后,您可以将实体标记为internal class,这样,消费者也就无法使用Set<>来访问它。

此外,如果您不公开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());