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:
Set<T>()
also creates a DbSet<T>
)DbSet<T>
specifically on the docs urls but no relevant results here either it seemsDbSet<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)Set<T>()
docs which has no relevant infoBut 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. 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/
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 theOnModelCreating
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).
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());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With