Avoid injecting concrete database context class to controller

asp.net-core-mvc c# dependency-injection entity-framework-core

Question

I am using Entity Framework Core for MySql with Database first approach.

After scaffolding the database, it generates the context class: sakilaContext.cs(sakila is the built-in database/scheme in MySql, I use it for playing around).

I have my database injected in Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        var connectionString = @"server=localhost;port=1123;user=root;password=xxx;database=sakila";
        services.AddDbContext<sakilaContext>(options => options.UseMySql(connectionString));
    }

In my api controller, I do it like this:

private sakilaContext dbCtx;

public SakilaController(sakilaContext dbContext)
{
    dbCtx = dbContext;
}

Just wonder,

  1. is it wrong to inject a concrete implementation in this case??
  2. If it is, how can I just inject an interface rather than inject a concrete implementation?

Thanks

1
3
4/2/2019 12:52:43 AM

Accepted Answer

is it wrong to inject a concrete implementation in this case??

It depends. If you're writing a multi-tier application with separated concerns (business logic, validation, authorization) or are you writing a 5 page application to display misc information for a limited number of users?

I've done exactly that for small, mostly readonly, applications because there are only small changes and I don't need to Over design and architect the solution.

If it is, how can I just inject an interface rather than inject a concrete implementation?

Sure, however at some point a class will need a dbcontext, which could be DI'd or created by the object that needs it. If you do abstract away the Data Access into a separate class/interface, I wouldn't recommend DI (to inject the context into the concrete data access) because that concrete type will be tighly coupled to EF. Meaning if you decided to switch to Dapper, NHibernate or some other ORM you'll have to rewrite all the methods anyway.

A quick and dirty way (for small/learning experiences, I would not ever recommend this for large million user systems) to abstract away the DbContext is as simple as:

public interface IUserDA
{
  IQueryable<User> Users { get; }
}

public class MyDbContext : IUserDA
{
  public DbSet<User> Users { get; set; }

  IQueryable<User> IUserDA.Users
  {
     get
     {
        return Users;  // references the DbSet
     }
  }

}

Now your MyDbContext implements an interface that is not coupled to Entity Framework.

But if we inject a concrete class, does it defeat the DI purpose?

Depends on your purpose. What exactly are you looking to achieve by using DI? Are you actually going to write real unit tests? Do you honestly see yourself changing or having multiple instances of the interfaces for multiple installation/tenants?

2
4/2/2019 1:12:45 AM

Popular Answer

I would not suggest abstracting Entity Framework out as it is already an abstraction itself (you can swap out DB providers from, let's say, SQL Server to PostgreSQL without your consuming classes knowing). I would not suggest injecting your context into your controller either.

What I like to do, and I would suggest that you look into this yourself to see if it is suitable, is to adopt the Command Pattern, leveraging the amazing Jimmy Bogard's libraries AutoMapper and MediatR.

To give you a basic example of how a controller might look using such an architectural design choice:

[Route("api/[controller]")]
[ApiController]
public class SomeTypeController : ControllerBase
{
    private readonly IMediator mediator;
    private readonly IMapper mapper;

    public SomeTypeController(IMediator mediator, IMapper mapper)
    {
        this.mediator = mediator;
        this.mapper = mapper;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<SomeTypeContract>>> GetAll(CancellationToken cancellationToken)
    {
        var result = await mediator.Send(new GetAllSomeTypeRequest(), cancellationToken);
        return Ok(mapper.Map<IEnumerable<SomeTypeContract>>(result));
    }
}

I really like this because now your controllers act as routers to business logic. They are blissfully unaware of what exactly is going on under the hood (or how it is going on).

To get to the heart of your question:

Is it wrong to inject a concrete implementation in this case??

The answer to this is a bit of an opinionated one, but many believe it to not only be an acceptable choice, but the right one. At some point in time, in order to make actual DB queries, you will want to have knowledge of your DBContext.

By abstracting it out, you will only cause yourself pain by writing hundreds of different methods for querying your data in different ways, doing all sorts of selects and includes. Either that or you will have 'chunky' methods that return far more data than you actually need.

By having concrete knowledge of your DBContext (likely in your request handlers if you go with the Command Pattern) you get full control over how your data is acquired, allowing for query optimisation as well as enforcing a separation of concerns.

Disclaimer: I brought up the above-mentioned libraries because they are wonderful and helpful, but I know many people look down on their inclusion in an answer. I would just like to make it clear that they are in no way a requirement to carry out the Command Pattern, but if you're going to adopt it, why reinvent the wheel?



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow