Resolve your DbContext as an interface using dependency injection

Solarion

Honorary Master
Joined
Nov 14, 2012
Messages
28,059
Reaction score
17,821
Just a few thoughts I'm posting here.

I've pretty much dumped repositories and unit of work. Of the opinion I don't need them anymore and on the hunt for other alternatives.

This is one of them by jerrie pelser. Your DBContext Inteface is resolved in the middleware and then you simply make your _context calls in the services.

C#:
public void ConfigureServices(IServiceCollection services)
{
    ...

    // Add EF services to the services container.
    services.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

    // Register the service and implementation for the database context
    services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());

    ...
    }

There is another take on this with Jason Taylor's clean architecture:


iu
 
Another cleaner alternative is Query Specification Pattern

1. https://gunnarpeipman.com/ef-core-repository-unit-of-work/
2. https://gunnarpeipman.com/ef-core-query-specification/

My previous blog post No need for repositories and unit of work with Entity Framework Core showed how Repository and Unit of Work patterns are already implemented in Entity Framework Code (and NHibernate) and how we can use IDataContext generalization to get ORM-s behind same interface. As I’m still trying to find out what’s the best place where to isolate queries without exposing IQueryable I started playing with Query Specification pattern.
 
really not sure I see the point of an interface for DbContext.

would be interested to see an example of a DbContext interface, and what advantages it gives over just having the implementation
 
really not sure I see the point of an interface for DbContext.

would be interested to see an example of a DbContext interface, and what advantages it gives over just having the implementation

How would you get the implementation into a service if your context is segregated on it's own layer? Just trying to keep the db stuff away from the front end.
 
How would you get the implementation into a service if your context is segregated on it's own layer? Just trying to keep the db stuff away from the front end.
Yah I suppose if you want to strictly follow clean architecture then you have no choice.

i just don’t really see the “problem” with having a “Model” project that contains the DbContext and entities that I reference in my “Application” project.
I then reference the “Application” project (which is basically just a collection of folders containing MediatR requests and handlers) in my “Web” project which dispatches requests to the MediatR via controllers

but I say as long as a project is easy to extend/maintain safely and is relatively simple to onboard new people, then your architecture is good :thumbsup:
 
tbh I have never seen it, I suppose the reasoning makes sense but there’s nothing wrong with it.
Having an interface with DbSet keeps you independent of database but just those that EF supports.

The repo approach is fine, it just depends on how flexible you wanna be.
 
If dbcontext leaks into your domain, you are doing it wrong
 
depends what you mean by "your domain"

you obviously need to call your dbcontext at some point
 
depends what you mean by "your domain"

you obviously need to call your dbcontext at some point

Perhaps he means EF logic like .Include or .ThenInclude.

Which is the problem I have with basically injecting the dbcontext into anything else really as it ultimately is a dependency, at least I think it is.

Busy looking at your suggestion with the MediatR and CQRS.

Specifically this approach.

 
Perhaps he means EF logic like .Include or .ThenInclude.

Which is the problem I have with basically injecting the dbcontext into anything else really as it ultimately is a dependency, at least I think it is.

Busy looking at your suggestion with the MediatR and CQRS.

Specifically this approach.


To me, .Include, .Where, .FirstOrDefault, etc is just a much nicer way of not having to define lots of repository methods that just end up being wrappers for loading data in different ways

I see a service being able to retrieve the data it needs as it wants an advantage, not a disadvantage.

By service, I mean a traditional *Service class, or a MediatR handler, or however else you compose your business logic
 
Your business logic should not have any dependency on EF namespace

The way I have it set up now, the business logic has no idea about the db context or anything related to EF. I've gone with the old tried and tested UnitOfWork for now until I can spend more time on CQRS and MediatR.

I do have one somewhat unrelated question about EF and the IdentityTables it's creates. Say a user logs into the site and you only want him to view his personal records such as payslip etc. How is this achieved with his login/role etc in the Identity tables?
 
I do have one somewhat unrelated question about EF and the IdentityTables it's creates. Say a user logs into the site and you only want him to view his personal records such as payslip etc. How is this achieved with his login/role etc in the Identity tables?
There has to be an association between the user and the data.
You’d then create the logic to filter based on user identity or role.
 
Your business logic should not have any dependency on EF namespace
So “useless” wrapper repositories?

to each their own I guess

or are you advocating an interface that your db context implements, or something else? Always interested to see alternative data access patterns that potentially lead to more succinct code bases
 
Last edited:
There has to be an association between the user and the data.
You’d then create the logic to filter based on user identity or role.

I think the association may be, or could be, the email address. So once he has logged in it could possibly check his role to hide/not load certain View elements and then only show records based on his credentials. Something like that maybe.
 
I think the association may be, or could be, the email address. So once he has logged in it could possibly check his role to hide/not load certain View elements and then only show records based on his credentials. Something like that maybe.

You can use HttpContext.User (in your controller, or off IHttpContextAccessor) to get the current "ClaimsPrincipal" i.e. current logged in user.

you can then save/fetch data using that Identity on that object
 
You can use HttpContext.User (in your controller, or off IHttpContextAccessor) to get the current "ClaimsPrincipal" i.e. current logged in user.

you can then save/fetch data using that Identity on that object

That's perfect thanks. I'm not there yet but it's something that got me terribly confused first time around with this project. Doing a full rebuild atm and working on the JWT stuff now.
 
Finally got some time, here's my 2c.
This is one of them by jerrie pelser. Your DBContext Inteface is resolved in the middleware and then you simply make your _context calls in the services.
This honestly makes no sense, even MS doesn't do it with IdentityDbContent.
It locks you into EF.

An example of the extension method I use
C#:
public static IServiceCollection AddMyDbContext<TContext>(
this IServiceCollection serviceCollection, string connectionString,
ServiceLifetime contextLifetime = ServiceLifetime.Scoped,
ServiceLifetime optionsLifetime = ServiceLifetime.Scoped) where TContext : IdentityDbContext<MyUser>
{
    return serviceCollection.AddDbContextPool<TContext, TContext>(options =>
    options.UseSqlServer(connectionString));
}
Also a generic repository, custom repos seem like too much work.
C#:
services.AddScoped(typeof(IEntityRepository<>), typeof(EntityFrameworkEntityRepository<>));

There is another take on this with Jason Taylor's clean architecture:
Important note because a lot of authors forget it, clean architecture is not DDD.

Another cleaner alternative is Query Specification Pattern
This is part of my EF repo implementation, also flexible for AutoMapper.
C#:
private IQueryable<TEntity> ApplySpecification(IEntitySpecification<TEntity> spec) =>
            SpecificationEntityEvaluator<TEntity>.GetQuery(_dbContext.Set<TEntity>().AsQueryable(), spec);
        
public virtual IQueryable<TDestination> ProjectTo<TDestination>(IConfigurationProvider configuration) =>
            _dbContext.Set<TEntity>().AsQueryable().ProjectTo<TDestination>(configuration);
An example of using a query spec would be
C#:
public class GetMapLocationByPlaceIdSpecifications : EntitySpecificationBase<MapLocation>
    {
        public GetMapLocationByPlaceIdSpecifications(string placeId)
            : base(m =>
                string.Equals(m.PlaceId, placeId))
        {
        }
    }
Well.. SaveChanges() method sends all changes to database. Not only changes related to given entity are saved but also changes to all other entities. If we have service class using this repository and it implements some more complex use case where update and delete are called multiple times we are screwed. If one update or delete fails then there’s no way to roll back all changes done to database before.
That honestly seems like an issue with DDD and not with EF. There's a change tracker, it can be turned off.
I have a read-only context just for pulling stats etc.
 
Last edited:
I have custom repos right now and I simply hate it. Seems like way too much boilerplate. A generic Repo huh?

I have a UnitOfWork. I may try your way first as I have a generic repo and then move on to the Query Specification pattern. Not entirely sure what to do with that extension method of yours but I'll make a start.
 
I think the association may be, or could be, the email address. So once he has logged in it could possibly check his role to hide/not load certain View elements and then only show records based on his credentials. Something like that maybe.
Rather use the UserId, an email address can change.
Remember based on your model definition is what EF will translate to SQL, if it can be null you won't get Inner Joins etc.

I have custom repos right now and I simply hate it. Seems like way too much boilerplate. A generic Repo huh?

I have a UnitOfWork. I may try your way first as I have a generic repo and then move on to the Query Specification pattern. Not entirely sure what to do with that extension method of yours but I'll make a start.
The Query Specification pattern is also generic, honestly I think UnitOfWork only matters in a DDD approach.
You're pulling data from various places then you have to worry how it all affects the database.
This isn't an issue when feature slicing.

Honestly in a CRUD app, you should decide if you really need all that abstraction.
I literally have 3 pages, List, CreateEdit, Delete.
All the logic etc. is there in the code behind. In one place to manage.
My Razor is also compact, FormBlock is basically the label and input elements.
Code:
<form method="post">
@Html.ValidationDiv()
@Html.HiddenFor(m => m.Data.Id)
@Html.FormBlock(m => m.Data.Name)
@Html.FormBlock(m => m.Data.PhoneNumber, inputModifier: tag => tag.Attr("type", "tel"))
@Html.FormBlock(m => m.Data.ContactPerson)
@Html.FormBlock(m => m.Data.ContactPersonPhoneNumber, inputModifier: tag => tag.Attr("type", "tel"))
</form>
 
Last edited:
Top
Sign up to the MyBroadband newsletter
X