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;
}
}
Related
I have a strange requirement here. We have a ton of databases that are basically idential but with different clients. So each db has the same table structures. With that being said, I can't use the standard way of setting up the connectionstring in the startup. I would like to see if there is a way I can pass the connectionstring in the contructor without it being the DbContextOptions<> object. We use to be able to do that in earlier versions of EF but now it's expecting that option in the base().
The other option is just doing raw ADO with a command object, which I REALLY don't want to do, I would prefer if I could use EF.
So this is what it looks like, but would prefer to have a second constructor that allows the connectionString to be passed in. NO Start Up or NO appsettings.json, just want to pass in a string with the connectionString.
`
public class Context : DbContext
{
public Context(DbContextOptions<Context> options) : base(options) // <== base expects only options
{
Database.SetCommandTimeout(9000);
}
public Context(string connectionString) : base()
{
this.Database.Connection ????? What to do here
}
public DbSet<Transcription> Transcriptions { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.HasDefaultSchema("dbo");
builder.Entity<Transcription>().ToTable("Transcriptions", "dbo");
}
}`
Any idea why this keeps throwing errors or why it won't allow me to set the connectionString instead of using options?
I have looked an keep getting old solutions from 12+ years ago, nothing for EF 6
Possibly more convenient would be to use multitenancy approaches described in the docs but if you still want to use just the ctor you can override the OnConfiguring method and use connection string from the ctor there:
public class Context : DbContext
{
private readonly string _connectionString;
public Context(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
// setup options - provider, connection string, etc.
optionsBuilder.UseNpgsql(_connectionString);
}
}
}
Or manually building options from the connection string:
public class Context : DbContext
{
public Context(string connectionString):base(BuildOpts(connectionString))
{
}
private static DbContextOptions<Context> BuildOpts(string connectionString) =>
new DbContextOptionsBuilder<Context>()
.UseNpgsql(connectionString) // setup options - provider, connection string, etc.
.Options;
}
I am working on Asp.Net Core 3.1 API. I have a few tables in the SQL Server database.
I am trying to use the InMemory database and I am trying to load the data into these tables on application startup.
I have written the below code.
public partial class MyContext : DbContext
{
public MyContext ()
{
}
public MyContext (DbContextOptions<MyContext > options)
: base(options)
{
}
public virtual DbSet<TestEntity> TestEntity{ get; set; }
...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseInMemoryDatabase("name=RuleDB");
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyContext>(opt => opt.UseInMemoryDatabase("myDbName"));
appsettings.json
"ConnectionStrings": {
"RuleDB": "Server=tcp:xyz.database.windows.net,1433;Initial Catalog=myDB;Persist Security Info=False;User ID=test;Password=****;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30"
}
When I try to do context.TestEntity.ToList();, it shows no records.
I am basically trying to load the data from my SQL Server database to my InMemory database on application startup.
Am I missing any steps this is not a good approach ( I mean should I use InMemory Cache in place of InMemory database)?
According to your code snippet, I didn't find where you add data to the in-memory database when the application starts up. So, I assume the issue is relate it.
Try to refer the following articles to Seed Data to The Database:
Applying Seed Data To The Database
Using EF Core's InMemory Provider To Store A "Database" In Memory
Entity Framework Core InMemory Database
In StartUp.cs, ConfigureServices make a reference to
services.AddTransient<IEntityRepository, EntityRepository>();
services.AddTransient<IEntityService, EntityService>();
services.AddMemoryCache();
Add a new repository interface.
public interface IEntityRepository
{
Task<List<Entity>> GetEntity();
}
Add a new class that will encapsulate the DbContext, and implement the interface
public class EntityRepository : IEntityRepository
{
private readonly DbContext _dbContext;
public(EntityRepository dbContext){
_dbContext = dbContext;
}
public async Task<List<Entity>> GetEntity()
{
return await _dbContext.Entity.ToListAsync();
}
}
Add another interface named IEntityService
public interface IEntityService
{
Task<List<Entity>> GetEntity();
}
Add another class named EntityService, encapsulating the caching and repository calls.
public class EntityService : IEntityService
{
private readonly IEntityRepository _repository;
private readonly IMemoryCache _cache;
public(EntityService repository, IMemoryCache cache){
_repository = repository;
_cache = cache
}
public async Task<List<Entity>> GetEntity()
{
var cacheKey = $"{anything-can-be-user-specific-too}";
return await _memoryCache.GetOrCreateAsync(cacheKey, x =>
{
var entities = _repository.GetEntity().GetAwaiter().GetResult();
x.SlidingExpiration = TimeSpan.FromSeconds(900);
x.AbsoluteExpiration = DateTime.Now.AddHours(1);
return Task.FromResult(entities);
});
}
}
The cache policy that I have used as an example indicates that if the cache is not referenced in 10 minutes (slidingRefresh), it will expire and be removed. This can be used together with AbsoluteExpiration so that you can force the cache to be refreshed after let's say 1 hour...
Now make the IEntityService call your starting point, lets say you have a controller...
Inject the service to the controller e.g
public class RandomController : ControllerBase
{
private readonly IEntityService _entityService;
public RandomController(IEntityService entityService)
{
_entityService = entityService;
}
[HttpGet]
[Route("randomroute")]
public async Task<IActionResult> GetEntities()
{
return Ok( await _entityService.GetEntity());
}
}
the first time executed the application will reference the database, however, all subsequent calls (until the cache policy has expired) will reference the memory cache.
Now if you want to change the cached item, you can introduce new services to perform database updates and then force a cache update by invalidating the cache key.
Updating IMemoryCache once an entity has changed
I have setup .net core project and db context also. But i cant start using dbContext yet due this error-
"there is no argument given that corresponds to the required formal
parameter 'options'"
Controller:
public IActionResult Index()
{
using (var db = new BlexzWebDb())
{
}
return View();
}
Dbcontext Code:
public class BlexzWebDb : DbContext
{
public BlexzWebDb(DbContextOptions<BlexzWebDb> options)
: base(options)
{ }
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<AssignedRole> AssignedRoles { get; set; }
}
error picture attached. How can this issue be fixed?
Instantiate new object of DbContext from ConnectionString
var connectionstring = "Connection string";
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(connectionstring);
ApplicationDbContext dbContext = new ApplicationDbContext(optionsBuilder.Options);
// Or you can also instantiate inside using
using(ApplicationDbContext dbContext = new ApplicationDbContext(optionsBuilder.Options))
{
//...do stuff
}
Note
At the time of writing the use of EF Core with the Dependency injection framework wasn't as known as it is now. This answers gives answer to the question from a DI perspective, which at the time, helped out OP.
The other answer provides you a conventional way to instantiate the DbContext using the new operator.
TL;DR, 3 options:
Option 1
Register the DbContext during application configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextPool<BlexzWebDb>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BlexzWebConnection")));
}
and use the DI framework to retrieve it:
public class SomeController : Controller
{
private readonly BlexzWebDb _db;
//the framework handles this
public SomeController(BlexzWebDb db)
{
_db = db;
}
}
Option 2
If you are looking for a design-time IdentityDbContext using IOptions<OperationalStoreOptions>, see: Add migration for ApiAuthorizationDbContext from another project - EF Core
Option 3
Or use the new operator and provide the details, see #Qamar Zaman's answer for details.
The long answer, and why DI is a treat
In EF Core it's common to pass some DbContextOptions to the constructor.
So in general, a constructor looks like this:
public BlexzWebDb(DbContextOptions<BlexzWebDb> options) : base(options)
As you can see there, there is no valid overload in the form of a parameter-less constructor:
Thus, this does not work:
using (var db = new BlexzWebDb())
Obviously, you can pass in an Option object in the constructor but there is an alternative. So,
Instead
.Net Core has IoC implemented in it's roots. Okay, this means; you don't create a context, you ask the framework to give you one, based on some rules you defined before.
Example: somewhere you will register your dbcontext, (Startup.cs):
//typical configuration part of .net core
public void ConfigureServices(IServiceCollection services)
{
//some mvc
services.AddMvc();
//hey, options!
services.AddDbContextPool<BlexzWebDb>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BlexzWebConnection")));
//...etc
Now the registering part is done, you can retrieve your context from the framework. E.g.: inversion of control through a constructor in your controller:
public class SomeController : Controller
{
private readonly BlexzWebDb _db;
//the framework handles this
public SomeController(BlexzWebDb db)
{
_db = db;
}
//etc.
why?
So, why not just provide the arguments and new it?
There is nothing wrong with the use of new - there are a lot of scenario's in which it works best.
But, Inversion Of Control is considered to be a good practice. When doing asp dotnet core you're likely to use it quite often because most libraries provide extension methods to use it. If you are not familiar with it, and your research allow it; you should definitely give it a try.
Therefore, instead of providing "just a way to instantiate" the object, I'll try to get you onto this track - inline with the framework. It will save you some hassle afterwards. Besides, otherwise "use an activator's CreateInstance" would just be as valid as an answer ;-)
Some links:
MSDN Fundamentals
MSDN Dependency Injection
Wikipedia Inversion Of Control
As addition of #Stefan's answer there is another way to achieve this. You can set db connection string in OnConfiguring method of DbContext class without adding DbContext service in startup.cs.
Setting.cs
public static class Setting
{
public static string ConnectionString { get; set; }
}
Startup.cs
Setting.ConnectionString = Configuration.GetSection("ConnectionStrings:BlexzDbConnection").Value;
BlexzWebDb.cs
public class BlexzWebDb : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(Setting.ConnectionString);
}
}
}
HomeController.cs
public class HomeController : Controller
{
private readonly BlexzWebDb db;
public HomeController()
{
this.db = new BlexzWebDb();
}
//etc.
Code sample for EF Core 3.1:
public class Test
{
private readonly IServiceProvider _serviceProvider;
public Test(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task<RequestResult> Handle(...)
{
await using var context = CreateContext();
...
}
private DocumentContext CreateContext()
{
var options = _serviceProvider.GetService<IOptions<DocumentContextOptions>>();
return new DocumentContext(options);
}
}
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.
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.