How can I dynamically register DbSets into DbContext and add a row to the table?

104 Views Asked by At

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 DbSets directly into DbContext and there is no client in the context.

1

There are 1 best solutions below

0
Ripmax On BEST ANSWER

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


    }

}