add-migration command fails on ApplicationDbContex - c#

App: Asp.NET Core 1.1.1, EF Core
Platform: Visual Studio 2017 5.3.3
Authentication Mode: Individual User Accounts
Following this tutorial from ASP.NET official team, the following command runs successfully:
PM> Add-Migration MyFirstMigration -context BloggingContext
As we know VS2017 by default creates ApplicationDbContext under MyProject\Data folder for creating user tables (ASPNETUsers, ASPNETRoles etc...) for authentication. But for that when I run the following command it first gives me Error-1 below. And when I follow the instructions in first error message, I get Error-2 below. Question: How do I make the following command work without using IDbContextFactory?
PM> Add-Migration MyFirstAuthenMigration -context ApplicationDbContext
Error 1
No parameterless constructor was found on 'ApplicationDbContext'. Either add a parameterless constructor to 'ApplicationDbContext' or add an implementation of 'IDbContextFactory' in the same assembly as 'ApplicationDbContext'.
After I add a parameterless constructor in ApplicationDbContext.cs (shown below) I get the second error shown below:
Error 2
No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
//NOTE: I added following constructor after Error 1 shown above
public ApplicationDbContext()
{
}
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}

You can't get it to work.
Because EF needs to know which connectionstring should be used. You can either put the connectionstring into your OnModelCreating method.
Which is bad when you have more than one DB (dev, test and production for example).
Or you implement IDbContextFactory which will be loaded by EF through reflection.
So implementing IDbContextFactory is the better option. Simply create it where you have your ApplicationDbContext.
Sample implementation.
public class DomainContextFactory : IDbContextFactory<DomainContext>
{
public string BasePath { get; protected set; }
public DomainContext Create()
{
var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var basePath = AppContext.BaseDirectory;
return Create(basePath, environmentName);
}
public DomainContext Create(DbContextFactoryOptions options)
=> Create(options.ContentRootPath, options.EnvironmentName);
private DomainContext Create(string basePath, string environmentName)
{
BasePath = basePath;
var configuration = Configuration(basePath, environmentName);
var connectionString = ConnectionString(configuration.Build());
return Create(connectionString);
}
private DomainContext Create(string connectionString)
{
if (string.IsNullOrEmpty(connectionString))
{
throw new ArgumentException($"{nameof(connectionString)} is null or empty", nameof(connectionString));
}
var optionsBuilder = new DbContextOptionsBuilder<DomainContext>();
return Configure(connectionString, optionsBuilder);
}
protected virtual IConfigurationBuilder Configuration(string basePath, string environmentName)
{
var builder = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("constr.json")
.AddJsonFile($"constr.{environmentName}.json", true)
.AddEnvironmentVariables();
return builder;
}
protected virtual string ConnectionString(IConfigurationRoot configuration)
{
string connectionString = configuration["ConnectionStrings:DefaultConnection"];
return connectionString;
}
protected virtual DomainContext Configure(string connectionString, DbContextOptionsBuilder<DomainContext> builder)
{
builder.UseSqlServer(connectionString, opt => opt.UseRowNumberForPaging());
DomainContext db = new DomainContext(builder.Options);
return db;
}
DomainContext IDbContextFactory<DomainContext>.Create(DbContextFactoryOptions options)
=> Create(options.ContentRootPath, options.EnvironmentName);
}
It works with 3 configuration files (final, local and test)
constr.dev-final.json
constr.dev-local.json
constr.dev-test.json
The usage looks like the following:
public override IServiceResult<IList<Rolle>> LoadAllData()
{
using (var db = this.DomainContextFactory.Create())
{
Task<List<Rolle>> rollen = db.Roles
.ToListAsync<Rolle>();
return new ServiceResult<IList<Rolle>>(rollen.Result, rollen.Result.Count);
}
}
And using "Add-Migration" inside MSVC works also.

Related

Error finding connection string in Blazor .net core app

I have a .netcore 3.1 blazor app with EF Core 5 and after scaffolding my database with
Scaffold-DbContext "Name=MyDb" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Context MyDb_Context -Force
Everything works without issue if I use
Scaffold-DbContext "Server=servername;Database=MyDb;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Context MyDb_Context -Force
But I want to avoid having the connection string in the source code.
everything scaffolds correctly, however, when I try to display data to a list, a get this error:
System.InvalidOperationException: A named connection string was used, but the name 'MyDb' was not found in the application's configuration. Note that named connection strings are only supported when using 'IConfiguration' and a service provider, such as in a typical ASP.NET Core application. See https://go.microsoft.com/fwlink/?linkid=850912 for more information.
Below is the code for my appsettings.json:
{
"ConnectionStrings": {
"MyDb": "Server=servername;Database=MyDb;Integrated Security=True"
}
}
Here is my MyDb_Context:
public partial class MyDb_Context : DbContext
{
public MyDb_Context()
{
}
public MyDb_Context(DbContextOptions<MyDb_Context> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Name=MyDb");
}
}
...
}
Here is my Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
_env = env;
}
private readonly IWebHostEnvironment _env;
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var connection = Configuration.GetConnectionString("MyDb");
services.AddDbContext<MyDb_Context>(options => options.UseSqlServer(connection));
}
...
}
And here is what is being called in my service:
public class MyDbService
{
public readonly MyDb_Context mydb_context;
public MyDbService(MyDb_Context mydbctx)
{
MyDb_Context = mydbctx;
MyDb_Context.Database.SetCommandTimeout(60);
}
public Task<List<MyDbTableName>> GetMyDbContent()
{
var usernames = mydb_context.usernames.Where(u => u.AppId == 2).ToListAsync();
return usernames ;
}
...
}
Then in my Razor page I am using the following code:
#code{
private List<usernames> _usernames;
MyDbService mydbservice = new MyDbService(new MyDb_Context()); //I think this line is where it is breaking
protected override async Task OnInitializedAsync()
{
//Grab function from the service listed above
//Function:
_usernames = await mydbservice.GetMyDbContent();
}
}
I have no idea why its not finding the connectionstring, and I can even put a breakpoint on startup.cs on
optionsBuilder.UseSqlServer("Name=MyDb"); and it is able to read the connection string perfectly fine.
I also can see that DBContext gets loaded in when try
services.toList(); and breakpoint on it.
Please help, I have been at this for over a week and have exhausted my efforts.
Register the service MyDbService in the Startup class to be injected latter by DI:
public void ConfigureServices(IServiceCollection services)
{
//...other services
services.AddScoped<MyDbService>();
}
Inject the service into the component
#* inject the service *#
#inject MyDbService mydbservice
//....
#code{
private List<usernames> _usernames;
//DO NOT instianate the service, it's injected
// MyDbService mydbservice = new MyDbService(new MyDb_Context()); //I think this line is where it is breaking
protected override async Task OnInitializedAsync()
{
//Grab function from the service listed above
//Function:
_usernames = await mydbservice.GetMyDbContent(); //now it's working
}
}
Modify the next line in the Constructor of MyDbService:
// MyDb_Context = mydbctx; //invalid
mydb_context = mydbctx;
The Scaffold-DbContext using named connection is valid because we are using DI.
Scaffold-DbContext "Name=MyDb" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Context MyDb_Context -Force
I tested this solution and it's working fine.
I doubt the Scaffold-DbContext Command has problem. I think you should give the complete connection string, something like this:
Scaffold-DbContext "Server=servername;Database=MyDb;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Context MyDb_Context -Force
In your MyDb_Context page, I didn't see puplic dbset<MyModel> Object {get; set;} , you need it to query and save an instance of your model!

Missing DbProvider?

C#; .NET Core 2.2; Visual Studio 2019
I get the following exception on the first DB query in my app.
No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider.
If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.
That method looks like:
public List<string> GetList()
{
try
{
using (var db = new CommoditiesContext())
{
var list = (from commodity in db.Commodity
where commodity.DateObsolete.Equals(null)
select commodity.Name.Trim().ToUpper()).OrderBy(a => a).ToList();
return list;
}
}
catch (Exception e)
{
throw new Exception("GetList: " + e.Message);
}
}
I created the model involved via:
Scaffold-DbContext -Connection "Server=MyDB.company.com;Initial Catalog=THINGS;Integrated Security=True" -Context CommoditiesContext -Schemas "dbo" -Provider Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models\Commodities
The CommoditiesContext.cs file is auto generated and has the method:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
//warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
optionsBuilder.UseSqlServer("Server=MyDB.company.com;Initial Catalog=THINGS;Integrated Security=True");
}
}
This method appears to only be called when the query above is executed. The CommoditiesContext class is not instantiated as far as I can tell.
As the warning comment in the OnConfiguring() method says, it is a bad idea to have connection strings in the code. Cool, I dig that. I have commented out the OnConfiguring() method and instead am trying to use:
services.AddDbContext<CommoditiesContext>(
options => options.UseSqlServer(
Configuration.GetConnectionString("CommoditiesContext")
)
);
in my startup.cs file.
I run web app locally and can put a break point on the services line and see the correct connection string going in. I break on the query above but generate the exception at the top.
For some reason the service version is not found by the query.
I do a bunch of googling and re-enable the OnConfiguring() method. Looks like I need to pull in the connection string in the OnConfiguring() method.
The majority of solutions I found say to add:
public class CommoditiesContext : DbContext {
private readonly IConfiguration _config;
public CommoditiesContext(IConfiguration config) {
_config = config;
}
...
}
and then use:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(_config.GetConnectionString("CommoditiesContext"));
}
}
The problem with this is that this auto generated class does not appear to be instantiated in the process of calling OnConfiguring() so _config is null.
What should I be doing?
The way it is set up, you need to inject it.
Here is an example:
public class MyController : Controller
{
private readonly CommoditiesContext db;
public MyController(CommoditiesContext db)
{
// db is 'automatically' created and given to you.
this.db = db;
}
[HttpGet]
public IActionResult GetList()
{
var list = (from commodity in db.Commodity
where commodity.DateObsolete.Equals(null)
select commodity.Name.Trim().ToUpper()).OrderBy(a => a).ToList();
return this.Ok(list);
}
}
Further reading: Dependency injection in ASP.NET Core
Addition:
If you have multiple db contexts, you can add multiple like this:
services.AddDbContext<CommoditiesContext>(
options => options.UseSqlServer(
Configuration.GetConnectionString("CommoditiesContext")
)
);
services.AddDbContext<AnotherDbContext>(
options => options.UseSqlServer(
Configuration.GetConnectionString("AnotherDbContext")
)
);
usage:
public class MyController : Controller
{
private readonly CommoditiesContext comDb;
private readonly AnotherDbContext anoDb
public MyController(
CommoditiesContext comDb,
AnotherDbContext anoDb)
{
this.comDb = comDb;
this.anoDb = anoDb;
}
}

Entity Framework Core migration - connection string

I'm having a problem to handle the DB connection string in conjunction with migrations.
I have 2 projects:
Domain
Application
The DbContext is in the Domain project, so this is the project I run migrations against.
The migrations concept enforces me to implement OnConfiguring in my DbContext and therein specify the database provider, eg:
protected override void OnConfiguring(DbContextOptionsBuilder builder)
{
builder.UseSqlServer("<connection string>");
}
My problem is that I don't want to use a hard coded connection string, for obvious reasons, and I cannot use ConfigurationManager to read it from the config file since the config file is in the application project.
All the examples I've seen involve either hard-coding the connection string or putting it in my ASP.NET Core application's settings files.
If you aren't using ASP.NET Core, or maybe, I don't know, don't want to have your local environment's database details committed to source control, you can try using a temporary environment variable.
First, implement IDesignTimeDbContextFactory like this (note that IDbContextFactory is now deprecated):
public class AppContextFactory: IDesignTimeDbContextFactory<AppContext>
{
public AppContextFactory()
{
// A parameter-less constructor is required by the EF Core CLI tools.
}
public AppContext CreateDbContext(string[] args)
{
var connectionString = Environment.GetEnvironmentVariable("EFCORETOOLSDB");
if (string.IsNullOrEmpty(connectionString))
throw new InvalidOperationException("The connection string was not set " +
"in the 'EFCORETOOLSDB' environment variable.");
var options = new DbContextOptionsBuilder<AppContext>()
.UseSqlServer(connectionString)
.Options;
return new AppContext(options);
}
}
Then, you can include the environment variable when you call Update-Database, or any of the other EF Core tools:
$env:EFCORETOOLSDB = "Data Source=(local);Initial Catalog=ApplicationDb;Integrated Security=True"; Update-Database
Here's how I do it, without a lot of extra code or craziness.
Project Structure:
AspNetCoreProject.Web
AspNetCoreProject.Data <-- DbContext here
My DbContext is set up with the constructor that allows you to inject the DbContextOptions
AspNetCoreProject.Data
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options)
{
}
}
In your application or web application, you set up your ConfigureServices normally.
AspNetCoreProject.Web / Startup.cs / ConfigureServices()
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("connection"))
Now, what about migrations? Well, I "trick" the Visual Studio UI into working as expected.
First, make sure your application (AspNetCoreProject.Web project with Startup.cs) is the start up project.
Second, open up your Nuget Package Manager Console. At the top of the Nuget PM> Console, there's a dropdown for 'Set Default Project', point this to your AspNetCoreProject.Data or project with the DbContext class.
Run your migration commands normally. add-migration init then update-database
Assuming your DbContext class has a constructor that accepts a parameter of type DbContextOptions, the dotnet ef commands have native support for this scenario - requiring no code changes nor additional configuration. Just use the "--startup-project" and "--project" parameters when creating and running migrations.
For example, let's say you have a "Application" project with your configuration and a separate project called "Domain" where the DbContext is implemented.
Context:
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options)
{
}
}
Startup:
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("connection"))
CLI command:
dotnet ef database update --startup-project Application --project Domain
We've had a same issue and there is a solution. :)
You have to implement IDbContextFactory<TContext>
When doing so you can read the connectionstrings from your appsettings.json. You can also use Add-Migration without errors, because overwriting OnConfigure() is obsolete then.
Sample implementation:
public class DomainContextFactory : IDbContextFactory<DomainContext>
{
public string BasePath { get; protected set; }
public DomainContext Create()
{
var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var basePath = AppContext.BaseDirectory;
return Create(basePath, environmentName);
}
public DomainContext Create(DbContextFactoryOptions options)
=> Create(options.ContentRootPath, options.EnvironmentName);
private DomainContext Create(string basePath, string environmentName)
{
BasePath = basePath;
var configuration = Configuration(basePath, environmentName);
var connectionString = ConnectionString(configuration.Build());
return Create(connectionString);
}
private DomainContext Create(string connectionString)
{
if (string.IsNullOrEmpty(connectionString))
{
throw new ArgumentException($"{nameof(connectionString)} is null or empty", nameof(connectionString));
}
var optionsBuilder = new DbContextOptionsBuilder<DomainContext>();
return Configure(connectionString, optionsBuilder);
}
protected virtual IConfigurationBuilder Configuration(string basePath, string environmentName)
{
var builder = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("constr.json")
.AddJsonFile($"constr.{environmentName}.json", true)
.AddEnvironmentVariables();
return builder;
}
protected virtual string ConnectionString(IConfigurationRoot configuration)
{
string connectionString = configuration["ConnectionStrings:DefaultConnection"];
return connectionString;
}
protected virtual DomainContext Configure(string connectionString, DbContextOptionsBuilder<DomainContext> builder)
{
builder.UseSqlServer(connectionString, opt => opt.UseRowNumberForPaging());
DomainContext db = new DomainContext(builder.Options);
return db;
}
DomainContext IDbContextFactory<DomainContext>.Create(DbContextFactoryOptions options)
=> Create(options.ContentRootPath, options.EnvironmentName);
}
How we use it:
public override IServiceResult<IList<Datei>> LoadAllData()
{
using (var db = this.DomainContextFactory.Create())
{
var files = db.Datei
.ToListAsync<Datei>();
return new ServiceResult<IList<Datei>>(files.Result, files.Result.Count);
}
}
sample config
{
"ConnectionStrings": {
"DefaultConnection": "Put your connectionstring here"
}
}
I was using OnConfiguring below with configured in Windows environment variable MsSql.ConnectionString and command for initial ef migration creation started to work: dotnet ef migrations add InitialCreate
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = Environment.GetEnvironmentVariable("MsSql.ConnectionString");
if(string.IsNullOrEmpty(connectionString))
throw new ConfigurationErrorsException("Sql server connection string configuration required");
if (!optionsBuilder.IsConfigured)
{
optionsBuilder
.UseSqlServer(connectionString)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}
}
To configure environment variable:
use Win + R hotkeys kombination to open Run command window
Type systempropertiesadvanced and hit Enter
On Advanced tab click Environment Variables
Click New... button
In Variable name field type MsSql.ConnectionString
In Variable value field type your connection string value
Make sure console(and any program that starts console) is restarted after new variable addition and before running dotnet ef related commands
I have my DBContext in my console app and was using a ctor with few parameters (such as connection string etc), since EF Core Migrations was using the default parameter less ctor and hence the connection string wasn't being populated I had the migrations failing.
Just added code to get the connection string from ConfigurationBuilder within my default ctor to by pass this.
Was only playing around with console app and EF Core so this works for me for now.

Pass connection string to dbcontext for Sqlite

I would like to pass the connection string to my dbcontext model.
I'm using the Microsoft.EntityFrameworkCore.Sqlite package.
public void ConfigureServices(IServiceCollection services)
{
//Adds Entity FrameWork for SqlLite
var config = Configuration.GetConnectionString("DefaultConnection");
//How to pass the connection string, AddEntityFrameworkSqlite take no extra parameters
//services.AddEntityFrameworkSqlite().AddDbContext<ETLDataBase>(opt => opt.????)
services.AddEntityFrameworkSqlite().AddDbContext<ETLDataBase>();
services.AddMvc();
services.AddScoped<IRepository<User>, AccountRepository>();
}
I didn't find a way to access to the ContentRootPath directly in the my dbcontext. I hardcoded the path of my DB in the appsettings.json, but even like this I can't pass the path to my model.
Here's my context model:
public class ETLDataBase: DbContext
{
public ETLDataBase(DbContextOptions<ETLDataBase> options)
: base(options)
{}
public DbSet<Datasouce> Datasources { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// I want to get my connection string in here
//I can't access to the env.ContentRootPath
var connection = new SqliteConnection(connectionString);
optionsBuilder.UseSqlite(connection);
}
}
EDIT
If I do it like this in the Startup.cs:
services.AddEntityFrameworkSqlite().AddDbContext<ETLDataBase>(options=> options.UseSqlite(Configuration["ConnectionStrings:Default‌​Connection"]));
And I remove the OnConfiguring function in the ETLDataBase class.
I have this error:
No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.

How do you configure the DbContext when creating Migrations in Entity Framework Core?

Is there way that dependency injection can be configured/bootstrapped when using Entity Framework's migration commands?
Entity Framework Core supports dependency injection for DbContext subclasses. This mechanism includes allowing for configuration of data access outside of of the DbContext.
For example, the following would configure EF to persist to a SQL server using a connection string retrieved from config.json
ServiceCollection services = ...
var configuration = new Configuration().AddJsonFile( "config.json" );
services.AddEntityFramework( configuration )
.AddSqlServer()
.AddDbContext<BillingDbContext>( config => config.UseSqlServer() );
However, the migrations commands do not know to execute this code so Add-Migration will fail for lack of a provider or lack of a connection string.
Migrations can be made to work by overriding OnConfiguring within the DbContext subclass to specify the provider and configuration string, but that gets in the way when different configuration is desired elsewhere. Ultimately keeping my the migration commands and my code both working becomes undesirably complex.
Note: My DbContext lives in a different assembly than the entry point that uses it and my solution has multiple start-up projects.
If you are looking for a solution to configure context for migrations, you can use this in your DBContext class:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString = configuration.GetConnectionString("DbCoreConnectionString");
optionsBuilder.UseSqlServer(connectionString);
}
}
Remember to install those two packages to have SetBasePath and AddJsonFile methods:
Microsoft.Extensions.Configuration.FileExtensions
Microsoft.Extensions.Configuration.Json
Use IDesignTimeDbContextFactory
If a class implementing this interface is found in either the same project as the derived DbContext or in the application's startup project, the tools bypass the other ways of creating the DbContext and use the design-time factory instead.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace MyProject
{
public class BloggingContextFactory : IDesignTimeDbContextFactory<BloggingContext>
{
public BloggingContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlite("Data Source=blog.db");
return new BloggingContext(optionsBuilder.Options);
}
}
}
applied in Entity Framework 2.0, 2.1
Using IDbContextFactory<TContext> is now obsolete.
Implement this interface to enable design-time services for context types that do not have a public default constructor. Design-time services will automatically discover implementations of this interface that are in the same assembly as the derived context.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace MyProject
{
public class BloggingContextFactory : IDbContextFactory<BloggingContext>
{
public BloggingContext Create()
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer("connection_string");
return new BloggingContext(optionsBuilder.Options);
}
}
}
more info : https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext
If you're not happy with the hard-coded connection-string, take a look at this article.
As #bricelam commented this functionality does not yet exist in Entity Framework 7. This missing functionality is tracked by GitHub issue aspnet/EntityFramework#639
In the mean time, the easier workaround I found was to utilize a global state rather than hassle with subclassing. Not usually my first design choice but it works well for now.
In MyDbContext:
public static bool isMigration = true;
protected override void OnConfiguring( DbContextOptionsBuilder optionsBuilder )
{
// TODO: This is messy, but needed for migrations.
// See https://github.com/aspnet/EntityFramework/issues/639
if ( isMigration )
{
optionsBuilder.UseSqlServer( "<Your Connection String Here>" );
}
}
In Startup.ConfigureServices().
public IServiceProvider ConfigureServices( IServiceCollection services )
{
MyContext.isMigration = false;
var configuration = new Configuration().AddJsonFile( "config.json" );
services.AddEntityFramework( configuration )
.AddSqlServer()
.AddDbContext<MyDbContext>( config => config.UseSqlServer() );
// ...
}
(The configuration code actually lives in an Autofac Module in my case.)
In .NET Core since version 2.1 should be used IDesignTimeDbContextFactory because IDbContextFactory is obsolete.
public class FooDbContextFactory : IDesignTimeDbContextFactory<FooDbContext>
{
public FooDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<FooDbContext>();
var connectionString = configuration.GetConnectionString("ConnectionStringName");
builder.UseSqlServer(connectionString);
return new FooDbContext(builder.Options);
}
}
To combine the answers above this works for me
private readonly bool isMigration = false;
public MyContext()
{
isMigration = true;
}
public MyContext(DbContextOptions<MyContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (isMigration)
{
optionsBuilder.UseSqlServer("CONNECTION_STRING");
}
}
I know this is a old question but I use the onConfiguring method and I don't have this problem
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(Startup.Configuration.Get("Data:DefaultConnection:ConnectionString"));
}
I just ask for an instance and run migrations in my Startup.cs file
public void ConfigureServices(IServiceCollection services)
{
// ASPNet Core Identity
services.AddDbContext<RRIdentityDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("RRIdentityConnectionString")));
}
And then in Configure:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
var rrIdentityContext = app.ApplicationServices.GetService<RRIdentityDbContext>();
rrIdentityContext.Database.Migrate();
}
Note: There is no 'EnsureCreated' for the database. Migrate is supposed to create it if it doesn't exist, although how it is supposed to figure out the permissions I don't know - so I created an empty database.

Categories

Resources