4

I have some problems with EF-Core that I'm trying to figure out.
I use the startup code in the MVC Core application to initalize the db context.
This is my DB context:

public class AccountsDBContext : DbContext
{
    public AccountsDBContext(DbContextOptions<AccountsDBContext> options)
        :base(options)
    {

    }

    // ...

}

And startup code:

 public void ConfigureServices(IServiceCollection services)
 {
        // Inject the account db
        services.AddDbContext<AccountsDBContext>(options =>
           options.UseMySQL(Configuration.GetConnectionString("AccountsStore")));

        // ...

In all the exampes I see the DB Context is a delivered via the constructor to the controller (I assume by dependency injection) and from there on to other entities\ layers.

 [Route("api/[controller]")]
 public class AccountsController : Controller
 {
    private AccountsDBContext _db;

    public AccountsController(AccountsDBContext context)
    {
        this._db = context;
    }

However, I'm not very fond of the idea that the db context will be a member at the controller.
I really prefer to get a hold of the db context in the data access layer instead of getting it passed into the repositories classes.
Is there a way to get the context inside the data access layer? (There is no IServiceCollection, IApplicationBuilder, IServiceScopeFactory there as far as I know)

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
Yaron
  • 2,209
  • 3
  • 18
  • 33
  • 1
    You're supposed to inject your DAL into the controller, and in your DAL have the constructor expect a EF context as a dependency. The built-in DI of .Net Core should be able to resolve both easily. See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection – haim770 Nov 29 '16 at 09:36
  • If you would like to get(create) the dbcontext in DAL, just initialize a DAL.DbContextFactory in startup.cs, and let the DbContextFactory create the dbcontext at the right time. See [http://stackoverflow.com/a/40837070/7045253](http://stackoverflow.com/a/40837070/7045253) – KarateJB Nov 29 '16 at 10:07
  • @haim770 The example is great. Thank you. – Yaron Nov 29 '16 at 10:18

3 Answers3

2

I Understand what you are trying to do. I have done exactly that. The key is to Create a static class in your DAL that uses the IServiceCollection. then in here you add your context here's mine and it works a treat My front end doesn't even know about entity framework, nethier does my business layer:

public static IServiceCollection RegisterRepositoryServices(this IServiceCollection services)
    {
        services.AddIdentity<ApplicationUser, IdentityRole<int>>(
            config => { config.User.RequireUniqueEmail = true;
                config.Cookies.ApplicationCookie.LoginPath = "/Account/Login";
                config.Cookies.ApplicationCookie.AuthenticationScheme = "Cookie";
                config.Cookies.ApplicationCookie.AutomaticAuthenticate = false;
                config.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents()
                {
                    OnRedirectToLogin = async ctx =>
                    {
                        if (ctx.Request.Path.StartsWithSegments("/visualjobs") && ctx.Response.StatusCode == 200)
                        {
                            ctx.Response.StatusCode = 401;
                        }
                        else
                        {
                            ctx.Response.Redirect(ctx.RedirectUri);
                        }
                        await Task.Yield();
                    }
                };
            }).AddEntityFrameworkStores<VisualJobsDbContext, int>()
          .AddDefaultTokenProviders();

        services.AddEntityFramework().AddDbContext<VisualJobsDbContext>();

        services.AddScoped<IRecruiterRepository, RecruiterRepository>();
        services.AddSingleton<IAccountRepository, AccountRepository>();

        return services;
    }

then in my service layer I have another static class. My service layer has a reference to the repository layer and I register the repository services here (bootstrapping the repository into the service layer), like so and then I do the same again in the UI:

Service layer code:

public static class ServiceCollectionExtensions
{
    public static IServiceCollection RegisterServices(this IServiceCollection services)
    {
        services.RegisterRepositoryServices();
        services.AddScoped<IRecruiterService, RecruiterService>();
        services.AddSingleton<IAccountService, AccountService>();

        return services;
    }
}

The Magic in the Repository Layer:

public partial class VisualJobsDbContext : IdentityDbContext<ApplicationUser, IdentityRole<int>, int>
{
    private IConfigurationRoot _config;

    public VisualJobsDbContext() { }

    public VisualJobsDbContext(IConfigurationRoot config, DbContextOptions<VisualJobsDbContext> options) : base(options)
    {
        _config = config;
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);

        optionsBuilder.UseSqlServer(@_config["ConnectionStrings:VisualJobsContextConnection"]);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {....
bilpor
  • 3,467
  • 6
  • 32
  • 77
  • How can you specify a connection string in DAL if you haven't IConfiguration there? – Pyrejkee Sep 26 '18 at 10:19
  • @Pyrejkee My example above only shows the service layer. in the repository layer, I pass in an `IConfigurationRoot` on the constructor with an override on the `OnConfiguring` method and set it in there. – bilpor Sep 26 '18 at 19:10
  • @Pyrejkee for clarity, I've extended my answer with the bit of code you are probably looking for, – bilpor Sep 26 '18 at 19:24
0

Inject your repository/DAL implementation into the controller and have the DbContext injected into the repo constructor. The DI container will hook it all up as long as the appropriate classes are registered

Moho
  • 15,457
  • 1
  • 30
  • 31
0

How about this?

DALAccount.cs

public class DALAccount
{
     private AccountsDBContext _db;
     public DALAccount(AccountsDBContext db)
     {
          _db = db;
     }
     public IQueryable<User> Get()
         => _db.User.AsQueryable();
}

Your Api

public class AccountsController : Controller
{
     private AccountsDBContext _db;

     public AccountsController(AccountsDBContext context)
     {
          this._db = context;
     }
     public IActionResult Index()
     {
          DALAccount dal = new DALAccount(_db);
          var list = dal.Get();
     }
}
Asherguru
  • 1,687
  • 1
  • 5
  • 10