I have too many entities and registering everyone of them is giving me a headache not to mention the load to instantiate the DbContext. I tried to dynamically register the dbsets.
To dynamically register DbSet s into DbContext I used the article in this link:
how to dynamically register entities dbcontext
I added an extension as a file:
public static class ModelBuilderExtensions
{
public static void RegisterAllEntities<BaseModel>(this ModelBuilder modelBuilder, params Assembly[] assemblies)
{
IEnumerable<Type> types = assemblies.SelectMany(a => a.GetExportedTypes())
.Where(c => c.IsClass &&
!c.IsAbstract &&
c.IsPublic &&
typeof(BaseModel).IsAssignableFrom(c));
foreach (Type type in types)
modelBuilder.Entity(type);
}
}
I made an abstract class called BaseEntity that all my entities implemented.
Inside the ApplicationDbContext
, I overrode the OnModelCreating
method like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var entitiesAssemblyInt = typeof(BaseEntity<int>).Assembly;
var entitiesAssemblyGuid = typeof(BaseEntity<Guid>).Assembly;
modelBuilder.RegisterAllEntities<BaseEntity<int>>(entitiesAssemblyInt);
modelBuilder.RegisterAllEntities<BaseEntity<Guid>>(entitiesAssemblyGuid);
}
It works perfectly in migrations but when I want to use the DbContext
in my CommandHandler
to add a row to my table named client I don't know how to call client from context.
context.Client.Add(client);
This doesn't work because I didn't register the DbSet
s directly into DbContext
and there is no client in the context.
In my opinion this is generally a bad idea*, for maintainability, debugging, readability reasons... The list goes on.
However, if you must, you can use _context.Set<Donation>()
to fetch the DbSet for a given entity.
Example
The following code with a correctly registered DbSet & Context.
This is the recommended way
public async Task<Donation?> GetDonationAsync(Guid donationID)
{
return await _context.Donations
.Where(x => x.ID == donationID)
.SingleOrDefaultAsync();
}
public async Task AddDonationAsync(Donation donation)
{
await _context.Donations.AddAsync(donation);
}
Is the same as doing:
public async Task<Donation?> GetDonationAsync(Guid donationID)
{
return await _context.Set<Donation>()
.Where(x => x.ID == donationID)
.SingleOrDefaultAsync();
}
public async Task AddDonationAsync(Donation donation)
{
await _context.Set<Donation>().AddAsync(donation);
}
The above code is contained in my repository services. I would recommend doing it this way and calling your repository, rather than directly calling your db context.
Example
Repository example
public class DonationRepository : IDonationRepository
{
private readonly MyDatabaseContext _context;
public DonationRepository(MyDatabaseContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
public async Task<Donation?> GetDonationAsync(Guid donationID)
{
return await _context.Donations
.Where(x => x.ID == donationID)
.SingleOrDefaultAsync();
}
public async Task AddDonationAsync(Donation donation)
{
await _context.Donations.AddAsync(donation);
}
}
Controller example
[ApiController]
[Route("[controller]")]
public class DonationController : Controller
{
private readonly IDonationRepository _donationRepository;
private readonly ILogger<DonationController> _logger;
public DonationController(ILogger<DonationController> logger,
IDonationRepository donationRepository)
{
_logger = logger;
_donationRepository = donationRepository;
}
[HttpGet]
[Route("GetDonationValue")]
public async Task<IActionResult> GetDonationValueAsync(Guid donationID)
{
var donation = await _donationRepository.GetDonationAsync(donationID);
// Doesn't exist!
if (donation == null) {
_logger.LogError($"Couldn't find Donation with ID: '{donationID}'.");
return NotFound();
}
_logger.LogInformation($"Found Donation with value of: '{donation.Amount}'.");
return Ok(donation.Amount);
}
}