简体   繁体   English

DbSet之间的区别 <T> 财产和集 <T> EF Core中的()函数?

[英]Difference between DbSet<T> property and Set<T>() function in EF Core?

Given this kind of context: 鉴于这种背景:

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

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

I can get to a Bar in two ways: 我可以通过两种方式来到Bar

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

or 要么

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

What is the difference between the two approaches? 这两种方法有什么区别?

I've tried to answer my own question by: 我试图通过以下方式回答我自己的问题:

But I could not find any good explanation about which of the two is used for which purpose. 但我找不到关于哪两个用于哪个目的的任何好的解释。 What is the difference? 有什么区别? Or perhaps more importantly: where and how should I be able to find this in the docs? 或者更重要的是:我应该在哪里以及如何在文档中找到它?

They do exactly the same thing. 他们完全一样。 The real question is when will you use one over the other. 真正的问题是你什么时候使用一个而不是另一个。

You use DbSet when you know the type of entity you want to play with. 当您知道要使用的实体类型时,可以使用DbSet。 You simple write the DbContext name then the entity type name and you can create, read, update or delete entries for this entity with the entity methods available. 您可以简单地编写DbContext名称,然后编写实体类型名称,您可以使用可用的实体方法创建,读取,更新或删除此实体的条目。 You know what you want and you know where to do it. 你知道你想要什么,你知道在哪里做。

You use Set when you don't know the entity type you want to play with. 当您不知道要使用的实体类型时,可以使用Set。 Lets say, you wanted to build a class that does your repository functions for creating, reading, updating and deleting entries for an entity. 可以说,您希望构建一个用于创建,读取,更新和删除实体条目的存储库函数的类。 You want this class to be reusable so that you can just pass a DbContext on it and it will use the same create, read, update and delete methods. 您希望此类可重用,以便您可以在其上传递DbContext,它将使用相同的创建,读取,更新和删除方法。 You don't know for sure what DbContext it will be used on or what DbSet the DbContext will have. 你不确定它将用于什么DbContext或DbContext将具有什么DbSet。 Here's when you use generics so that your class can be used by any DbContext for any DbSet. 这是当你使用泛型时,任何DbContext的任何DbContext都可以使用你的类。

Here's an example of a class you can use for creating any entity on any DbSet in any DbContext 这是一个类的示例,可用于在任何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;
    }
}

And this is how to use it. 这就是如何使用它。

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
});

Here's a link if you want to know more about generics. 如果您想了解有关泛型的更多信息,请参阅此链接。 Its super awesome. 它超级棒。

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

Unfortunately currently you won't find explanation in the official documentation, mainly because all these are functionally equivalent. 不幸的是,目前你在官方文档中找不到解释,主要是因为所有这些都在功能上相同。

First, the generic methods of DbConext like Add<TEntity> , Remove<TEntity> , Attach<TEntity> etc. a fully equivalent of the corresponding DbSet<TEntity> methods (actually currently they are the implementation of the later, ie DbSet methods simply call the corresponding DbContext generic method). 首先, DbConext泛型方法如Add<TEntity>Remove<TEntity>Attach<TEntity>等完全等效于相应的DbSet<TEntity>方法(实际上目前它们是DbSet<TEntity>的实现,即DbSet方法简单调用相应的DbContext泛型方法)。 Which one you use is just a matter of taste. 你使用哪一个只是品味问题。

Second, DbSet<TEntity> property and Set<TEntity> method are functionally equivalent, but do have some non functional differences. 其次, DbSet<TEntity>属性和Set<TEntity>方法在功能上是等价的,但确实有一些非功能差异。

The DbSet properties are populated once at the context creation, while Set method always performs a lookup, so DbSet property access should be faster than Set method (although not significant). DbSet属性在创建上下文时填充一次,而Set方法总是执行查找,因此DbSet属性访问应该比Set方法快(尽管不重要)。

The important difference is actually the EF Core Including & Excluding Types convention: 重要的区别实际上是EF核心包含和排除类型约定:

By convention, types that are exposed in DbSet properties on your context are included in your model. 按照惯例,在上下文中的DbSet属性中公开的类型包含在模型中。 In addition, types that are mentioned in the OnModelCreating method are also included. 此外,还包括OnModelCreating方法中提到的类型。

So while you can keep your DbContext without exposed DbSet properties and work just with Set method, if you do so you have to tell explicitly EF Core which are your entity types by adding in OnModelCreating a call to modelBuilder.Entity<TEntity>(); 因此,虽然您可以保留DbContext而不暴露DbSet属性并且只使用Set方法,但是如果这样做,则必须通过添加OnModelCreatingmodelBuilder.Entity<TEntity>();的调用来明确告知EF Core哪些是您的实体类型modelBuilder.Entity<TEntity>(); for each entity type (this is what the documentation does mean by types that are mentioned in the OnModelCreating method ). 对于每个实体类型(这是文档的含义, OnModelCreating方法中提到类型 )。

They are the same and actually returns the same DbSet instance. 它们是相同的,实际上返回相同的DbSet实例。

var options = //...;

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

One use case for not including a DbSet property in your DbContext is when you want to hide an entity type from a consumer. 一个用例不包括DbSet在你的财产DbContext是,当你想隐藏从消费者的实体类型。 (eg an entity that acts as join table for many-to-many relationship ). (例如,充当多对多关系的连接表的实体)。 You can then mark the entity as internal class so consumers also can't also access it using Set<> . 然后,您可以将实体标记为internal class以便消费者也无法使用Set<>访问它。

Also, if you don't expose a DbSet property, you need to explicitly configure the entity or you'll get the following exception: 此外,如果您不公开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());    

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM