1

The connection string for our app is set in appsettings.json

  "Data": {
"DefaultConnection": {
  "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=Customers;Trusted_Connection=True;MultipleActiveResultSets=true",

In ConfigureServices we have

            services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<CustomersContext>(options =>
                options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

This seems to work in cases like this

var membershipUser = await _userManager.FindByEmailAsync(email);

and this

var result = await _userManager.CreateAsync(newUser);

but falls over when I try this

            using (var customers = new CustomersContext())
        {
            var deviceList = customers.Devices.Where(d => d.UserId == membershipUser.Id);

The error is InvalidOperationException: No database providers are configured. Configure a database provider by overriding OnConfiguring in your DbContext class or in the AddDbContext method when setting up services.

If I try this

            public partial class CustomersContext : IdentityDbContext<ApplicationUser>
// note this inherits from IdentityDbContext<ApplicationUser> not DbContext
// refer http://stackoverflow.com/questions/19902756/asp-net-identity-dbcontext-confusion
{
    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        options.UseSqlServer(@"Server=(localdb)\\mssqllocaldb;Database=Customers;Trusted_Connection=True;MultipleActiveResultSets=true");
    }

I get this error

Local Database Runtime error occurred. Specified LocalDB instance name is invalid

Why is it my app can find the database in some cases but not others?

Vague
  • 2,198
  • 3
  • 18
  • 46
  • 1
    You shouldn't be newing up a dbcontext, it should be injected where you need it since it is registered with services – Joe Audette Jan 04 '16 at 12:50
  • 1
    If you do new up CustomersContext, you should use the constructor that takes IServiceProvider and DbContextOptions. The connection string comes from the DbContextOptions, so yours isn't getting one because of the way you newed it up. – Joe Audette Jan 04 '16 at 13:04
  • Thank you @Joe Audette. Could you give me examples of the two methods you mentiion? – Vague Jan 04 '16 at 19:27
  • 1
    In [code from one of my projects](https://github.com/joeaudette/cloudscribe.Logging/blob/master/src/cloudscribe.Logging.EF/LogRepository.cs) you can see my LogRepository gets an IServiceProvider and DbContextOptions injected to its constructor. Then those 2 objects are used to new up my dbcontext correctly. Alternatively you can just take a constructor dependency on the dbcontext itself and not have to new it up yourself. I have an [example of that in another project here](https://github.com/joeaudette/cloudscribe/blob/master/src/cloudscribe.Core.Repositories.EF/GeoRepository.cs) – Joe Audette Jan 04 '16 at 20:10
  • 1
    Thanks again @Joe Audette. If you add your comments as an answer I can mark them as the answer. – Vague Jan 04 '16 at 21:38
  • I may have spoken too soon. The examples given by @Joe Audette refer to classes that inherit from ILogRepository. I am trying to access CustomersContext in AccountsController.cs which does not have a dbContext property. – Vague Jan 05 '16 at 07:15
  • If you need it in accounts controller just add it to the constructor of accounts controller so it gets injected – Joe Audette Jan 05 '16 at 13:20

1 Answers1

4

The problem is that although you've configured a CustomerContext with the DI services as shown here:

services.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<CustomersContext>(options =>
           options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

You are not having the CustomerContext injected, instead you are newing it up like this:

using (var customers = new CustomersContext())
 {
...
}

using a constructor that takes no parameters, so your CustomersContext is not configured like the one in startup and it has no connection string.

Since you mention you need it in the AccountController, then all you need to do is add CustomersContext to the constructor of AccountController, so that the one you configured in startup will get injected. Like this:

private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ISmsSender _smsSender;
private readonly ILogger _logger;
private CustomersContext _customerContext;

public AccountController(
    UserManager<ApplicationUser> userManager,
    SignInManager<ApplicationUser> signInManager,
    IEmailSender emailSender,
    ISmsSender smsSender,
    ILoggerFactory loggerFactory,
    CustomersContext customerContext)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _emailSender = emailSender;
    _smsSender = smsSender;
    _logger = loggerFactory.CreateLogger<AccountController>();
    _customerContext = customerContext;
}

That way you get a properly configured CusotmersContext and you don't have to new it up yourself. If for some reason you did want to new it up yourself you need to do so using a constructor that takes IServiceProvider and DbContextOptions. So you would receive those objects in the constructor of AccountController and you would pass them in as you new up the CustomersContext like this:

using (var customers = new CustomersContext(serviceProvider, dbContextOptions))
 {
...
}
Joe Audette
  • 35,330
  • 11
  • 106
  • 99