Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

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:

  • Inspecting the intellisense for both (only tells me that Set<T>() also creates a DbSet<T>)
  • Googling for "EF Core Set vs property" but that doesn't seem to be the 'right' query
  • Google for DbSet<T> specifically on the docs urls but no relevant results here either it seems
  • Reading the intro of the DbSet<T> docs which just suggests that you can get a set through either of the two methods (not if there is or isn't a difference)
  • Read the Set<T>() docs which has no relevant info

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?

like image 839
Jeroen Avatar asked Nov 25 '18 16:11

Jeroen


3 Answers

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. 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. 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. 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. You don't know for sure what DbContext it will be used on or what DbSet the DbContext will have. Here's when you use generics so that your class can be used by any DbContext for any DbSet.

Here's an example of a class you can use for creating any entity on any DbSet in any DbContext

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://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/

like image 52
calingasan Avatar answered Oct 31 '22 07:10

calingasan


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, i.e. DbSet methods simply call the corresponding DbContext generic method). 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.

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).

The important difference is actually the EF Core Including & Excluding Types convention:

By convention, types that are exposed in DbSet properties on your context are included in your model. In addition, types that are mentioned in the OnModelCreating method are also included.

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>(); for each entity type (this is what the documentation does mean by types that are mentioned in the OnModelCreating method).

like image 37
Ivan Stoev Avatar answered Oct 31 '22 07:10

Ivan Stoev


They are the same and actually returns the same DbSet instance.

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. (e.g. 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<>.

Also, if you don't expose a DbSet property, you need to explicitly configure the entity or you'll get the following exception:

//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());    
like image 1
Jan Paolo Go Avatar answered Oct 31 '22 06:10

Jan Paolo Go