代码之家  ›  专栏  ›  技术社区  ›  Jeroen

ef core中dbset<t>属性与set<t>()函数的区别?

  •  2
  • Jeroen  · 技术社区  · 7 年前

    考虑到这种情况:

    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 回复  |  直到 7 年前
        1
  •  2
  •   calingasan    7 年前

    他们做同样的事情。真正的问题是你什么时候用一个而不是另一个。

    当知道要使用的实体类型时,可以使用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/

        2
  •  2
  •   Ivan Stoev    7 年前

    不幸的是,目前你在官方文件中找不到解释,主要是因为这些都是 功能上 当量。

    首先, 通用的 方法 DbConext 喜欢 Add<TEntity> , Remove<TEntity> , Attach<TEntity> 等,相当于 DbSet<TEntity> 方法(实际上,目前是后者的实现,即 DbSet 方法只调用相应的 DbContext 通用方法)。你用哪一个只是一个品味问题。

    第二, 数据库集 财产和 Set<TEntity> 方法在功能上是等效的,但是 确实有 一些非功能性差异。

    这个 数据库集 属性在上下文创建时填充一次,而 Set 方法总是执行查找,因此 数据库集 属性访问应比 集合 方法(尽管不重要)。

    重要的区别实际上是EF核心 Including & Excluding Types 公约:

    按照惯例,在 数据库集 上下文中的属性包含在模型中。此外,在 OnModelCreating 方法也包括在内。

    所以当你可以保留你的 数据库上下文 不暴露 数据库集 属性和工作 集合 方法,如果这样做,则必须通过添加 OnMe建模的创建 对…的呼唤 modelBuilder.Entity<TEntity>(); 对于每种实体类型(这就是文档所指的 中提到的类型 OnMe建模的创建 方法 )

        3
  •  0
  •   Jan Paolo Go vikomall    7 年前

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

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

    一个不包括 数据库集 您的财产 DbContext 当您想对使用者隐藏实体类型时。(例如,作为联接表的实体 many-to-many relationship )然后可以将实体标记为 internal class 所以消费者也不能使用 Set<> .

    另外,如果你不暴露 数据库集 属性,您需要显式配置该实体,否则将得到以下异常:

    //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());