How and where to call Database.EnsureCreated and Database.Migrate? - asp.net

I have a ASP.NET MVC 6 application, and i need to call the Database.EnsureCreated and Database.Migrate methods.
But where should I call them?

I think this is an important question and should be well answered!
What is Database.EnsureCreated?
context.Database.EnsureCreated() is new EF core method which ensures that the database for the context exists. If it exists, no action is taken. If it does not exist then the database and all its schema are created and also it ensures it is compatible with the model for this context.
Note:
This method does not use migrations to create the database. In addition, the database that is created cannot later be updated using migrations. If you are targeting a relational database and using migrations, you can use the DbContext.Database.Migrate() method to ensure the database is created and all migrations are applied.
How did we do that with EF 6?
context.Database.EnsureCreated() is equivalent to the below listed approaches of EF 6:
Package Manager Console:
Enable-Migrations -EnableAutomaticMigrations. Add-Migration/Update-Database.
From code:
Database.SetInitializer CreateDatabaseIfNotExists
or
With DbMigrationsConfiguration and set AutomaticMigrationsEnabled = true;
What is Database.Migrate?
Applies any pending migrations for the context to the database. Will create the database if it does not already exist.
How did we do that with EF 6?
context.Database.Migrate() is equivalent to the below listed approaches of EF 6:
Package Manager Console:
Update-Database -TargetMigration
With a custom DbMigrationsConfiguration:
AutomaticMigrationsEnabled = false; or with DbMigrator.
Conclusion:
If you are using migrations there is context.Database.Migrate(). If you don't want migrations and just want a quick database (usually for testing) then use context.Database.EnsureCreated()/EnsureDeleted().

With the information that James P and Bassam Alugili provided, what I ended up doing was to add these lines of code to the Configure method in the Startup class (Startup.cs):
using (var scope =
app.ApplicationServices.CreateScope())
using (var context = scope.ServiceProvider.GetService<MyDbContext>())
context.Database.Migrate();

Ordinarily, the DbContext will be added to the dependency injection container in Startup.ConfigureServices() like so:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add DbContext to the injection container
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(
this.Configuration.GetConnectionString("DefaultConnection")));
}
}
However, the IServiceCollection doesn't act as a service provider, and since the DbContext was not registered with the injection container before the current scope (Startup.ConfigureServices), we can't access the context through dependency injection here.
Henk Mollema discusses manually resolving services during startup here, but mentions that...
manually resolving services (aka Service Locator) is generally
considered an anti-pattern ... [and] you should avoid it as much
as possible.
Henk also mentions that the Startup constructor's dependency injection is very limited and does not include services configured in Startup.ConfigureServices(), so DbContext usage is easiest and most appropriate through the injection container used throughout the rest of the app.
The runtime's hosting service provider can inject certain services into the constructor of the Startup class, such as IConfiguration, IWebHostEnvironment (IHostingEnvironment in pre-3.0 versions), ILoggerFactory and IServiceProvider. Note that the latter is an instance built by the hosting layer and contains only the essential services for starting up an application.
In order to call Database.EnsureCreated() or Database.Migrate(), we can, and want to, have the DbContext resolve automatically in Startup.Configure(), where our configured services are now available through DI:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add DbContext to the injection container
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(
this.Configuration.GetConnectionString("DefaultConnection")));
}
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyDbContext context)
{
if (env.IsDevelopment())
{
context.Database.EnsureCreated();
//context.Database.Migrate();
}
}
}
Please remember as Bassam Alugili's answer referenced from EF Core documentation that Database.EnsureCreated() and Database.Migrate() are not meant to be used together because one ensures your existing migrations are applied to the database, which is created if needed. The other just ensures a database exists, and if not, creates one that reflects your DbContext, including any seeding done through the Fluent API in the context.

Just as a foreward you should read this from Rowan Miller:
... EnsureCreated totally bypasses migrations and just creates the
schema for you, you can't mix this with migrations. EnsureCreated is
designed for testing or rapid prototyping where you are ok with
dropping and re-creating the database each time. If you are using
migrations and want to have them automatically applied on app start,
then you can use context.Database.Migrate() instead.
According to answer here you need to add Globals.EnsureDatabaseCreated(); it to Startup.cs:
Startup function in Startup.cs:
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
if (env.IsDevelopment())
{
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
Configuration = builder.Build();
Globals.Configuration = Configuration;
Globals.HostingEnvironment = env;
Globals.EnsureDatabaseCreated();
}
And define Globals.EnsureDatabaseCreated() as follows:
public static void EnsureDatabaseCreated()
{
var optionsBuilder = new DbContextOptionsBuilder();
if (HostingEnvironment.IsDevelopment()) optionsBuilder.UseSqlServer(Configuration["Data:dev:DataContext"]);
else if (HostingEnvironment.IsStaging()) optionsBuilder.UseSqlServer(Configuration["Data:staging:DataContext"]);
else if (HostingEnvironment.IsProduction()) optionsBuilder.UseSqlServer(Configuration["Data:live:DataContext"]);
var context = new ApplicationContext(optionsBuilder.Options);
context.Database.EnsureCreated();
optionsBuilder = new DbContextOptionsBuilder();
if (HostingEnvironment.IsDevelopment()) optionsBuilder.UseSqlServer(Configuration["Data:dev:TransientContext"]);
else if (HostingEnvironment.IsStaging()) optionsBuilder.UseSqlServer(Configuration["Data:staging:TransientContext"]);
else if (HostingEnvironment.IsProduction()) optionsBuilder.UseSqlServer(Configuration["Data:live:TransientContext"]);
new TransientContext(optionsBuilder.Options).Database.EnsureCreated();
}
To use context.Database.Migrate() see here or here.

var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddDbContext<YourDbContext>(option => option.UseSqlServer(#"Data source=(localdb)\ProjectModels;Initial Catalog=YourDb;Integrated Security=True"));
var app = builder.Build();
// Configure the HTTP request pipeline.
YourDbContext dbcontext = app.Services.GetRequiredService<YourDbContext>();
dbcontext.Database.EnsureCreated();

Additionally you may see a performance hit if you call this in the constructor of your context... After moving EnsureCreated to the setup.cs utility, I noticed considerable improvements to my response times.
Note: I am using EFC and UWP.

If you are working with VS 2022 / .Net Version 6 and you are trying to find a way to create your database..then
Do these following steps
Add Microsoft.EntityFramework.Tools reference through Package manager
from Package Manager Console
Run Step 1
Add-Migration InitialMigration
InitialMigration here is custom name you can type anything you want..
let it run
Step 2
Update-Database
This should create your database.

I am developing ASP .NET Core 6 Web API.
In Program.cs after building the app I wrote the code below.
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
using var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
context.Database.EnsureCreated();
}
context.Database.EnsureCreated() ensures that the database for the context exists.
If the database exists and has any tables, then no action is taken. Nothing is done to ensure the database schema is compatible with the Entity Framework model.
If the database exists but does not have any tables, then the Entity Framework model is used to create the database schema.
If the database does not exist, then the database is created and the Entity Framework model is used to create the database schema.
I hope I helped.

Related

How to replace IDbContextFactory in Service Injection for xUnit integration test

I am setting up an API using .NET 5 with dependency injection for my data access; using Entity Framework code first. I need to use IDbContextFactory because some of my controllers need to use more than one DbContext instance for proper Unit of Work scope.
I'm also trying to use xUnit for integration tests and am running into difficulties getting it to use in memory database.
Here is the relevant snippet from my Startup class
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<ApplicationDbContext>(
options => options.UseSqlServer("connection string")
);
//other services configured
}
And this is the xUnit Application Factory that is supposed to remove the existing DbContextFactory and replace it with the InMemory factory.
public class TestApplicationFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup: class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var contextFactory = services.SingleOrDefault(
d => d.ServiceType ==
typeof(IDbContextFactory<ApplicationDbContext>)
);
services.Remove(contextFactory);
services.AddDbContextFactory<ApplicationDbContext>(
options => options.UseInMemoryDatabase("InMemoryDbForTesting")
);
var sp = services.BuildServiceProvider();
var dbf = sp.GetRequiredService<IDbContextFactory<ApplicationDbContext>>();
var db = dbf.CreateDbContext();
db.Database.EnsureCreated();
// Pass the context to a class that will add seed data
DbSeed.InitializeDbForTests(db);
});
}
When I step through in debug, it appears to be removing the existing factory as evidenced by the count of services dropping after the services.Remove(contextFactory); line and it also seems to add the new one, again by looking at the count.
This is not throwing any errors, it's just not using the new DbContextFactory.
What am I missing here?
Thank you in advance for any help.
I figured out the issue.
When the Startup class runs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<ApplicationDbContext>(
options => options.UseSqlServer("connection string")
);
//other services configured
}
It is actually adding four descriptors to the service collection:
IDbContextFactory\<ApplicationDbContext>
IDbContextFactorySource\<ApplicationDbContext>
DbContextOptions
DbContextOptions\<ApplicationDbContext>
The solution is to remove all of these in the xUnit ApplicationFactory. I was only removing one. Once I removed the other three, I could continue on to add the InMemory version of the DbContextFactory and it now correctly points to the test database.
If you have a more efficient or elegant solution, I'd be interested to know.

Why is the IConfiguration object null after injecting it to DbContext? [ASP.NET Core 3.1]

Before posting this question I have gone through multiple posts that are similar. My question focuses on "why is it happening despite applying the common and possibly right solution?".
I am developing a .NET Core 3.1 web app. I have a DbContext named 'SkipQContext'. I am trying to access the connection string from appsettings.json in the SkipQContext file using Configuration object.
For that, I have injected IConfiguration as a service to the SkipQContext constructor.
Constructor:
private readonly string ConnectionString;
public SkipQContext()
{
}
public SkipQContext(DbContextOptions<SkipQContext> options, IConfiguration configuration)
: base(options)
{
ConnectionString = configuration.GetConnectionString("DefaultConnection");
}
I have also registered in ConfigureServices method of Startup class.
services.AddSingleton(Configuration);
Now when I instantiate SkipQContext in one of my repository classes, the default constructor of SkipQContext is called. When I try to fetch data using it, I get the "IConfiguration object is null."
I applied breakpoints in ConfigureServices method and can see that the IConfiguration object has the connection string value.
My first question is, why is it null in SkipQContext when I am registering it in ConfigureServices and also injecting it in SkipQContext constructor? Multiple answers online state this as the right method.
Also, I am thinking, I might not be instantiating the SkipQContext rightly. As my statement :
SkipQContext db = new SkipQContext();
hits the default constructor of SkipQContext which is empty and not the overloaded constructor where IConfiguration is injected.
P.S. If the last question is dumb. I am still a bit unclear about how dependency injection works in .NET Core.
Also, I am thinking, I might not be instantiating the SkipQContext rightly. As my statement:
SkipQContext db = new SkipQContext();
hits the default constructor of SkipQContext which is empty and not the overloaded constructor where IConfiguration is injected.
You are right, this is not how dependency injection is supposed to work. Whenever you do new Something, then you are explicitly going around dependency injection. The main point about dependency injection is that a component that has a dependency (e.g. a database context) does not need to create that dependency itself, or even know how to create that dependency itself.
When you call new SkipQContext(), you are explicitly creating that depenndency, so you are tightly coupled to that SkipQContext and whatever that context needs in order to work properly (in this case, it needs DbContextOptions and IConfiguration). What you want instead is components to be loosely coupled to their dependencies. So you just declare what dependencies you need and require that someone or something else fulfills these dependencies for you. And that’s exactly where dependency injection comes in:
With dependency injection, you have a “dependency injection container” which takes care of creating all the dependencies that you or some component may require. You configure the container in a central location, in Startup.ConfigureServices, and then you are able to simply declare what dependencies you need via a service’s constructor. But in order for the container to provide these dependencies to that service, the service will have to be created by the container itself.
So what you will see is that you will basically have to consume everything through dependency injection. But this also makes it easy to realize when you are not using dependency injection: Whenever you write new Something, then that something won’t be created by the container and as such won’t have its dependencies automatically fulfilled. Depending on what that something is that might be what you want, or maybe not (e.g. creating a List<string> or a DTO object is something you want to do directly, creating a service or something that has other dependencies likely isn’t).
Coming back to your problem, in order to have the DI container take care of the dependencies in the constructor of SkipQContext, you will have to let the DI container create that context for you. So you cannot create it with new but instead you will have to depend on it by adding it to the constructor of whatever component you need it in.
E.g. if you have a controller, just add it as a dependency there:
public class HomeController : Controller
{
private readonly SkipQContext _db;
public HomeController(SkipQContext db)
{
_db = db;
}
public async Task<IActionResult> Index()
{
var items = await _db.Items.ToListAsync();
return View(new IndexViewModel
{
Items = items,
});
}
}
One final note regarding your database context: If you register the database context correctly with the DI container, then it will already be configured using the DbContextOptions that gets passed to the constructor. These options will also include the connection string the context needs to open the database connection. So you do not need to pass the IConfiguration manually or extract the connection string there. It will be automatically done for you by EF Core.
A proper context setup could look like this (in ConfigureServices):
services.AddDbContext<SkipQContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
There is no need to instantiate the Configuration as a Singleton, the Default builder of WebHost already inject the configuration in the request , your Startup Class should look like this
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
string conn = Configuration.GetConnectionString("NAME OF YOUR CONNECTION STRING IN THE application.json FILE");
services.AddDbContext<CLASSOFURDBCONTEXT>(config =>
{
config.UseSqlServer(conn);
});
}
}
And your dbcontext should have the following constructor
public YourDbContext(DbContextOptions<YourDbContext> options) : base(options)
{
}
Then you only need to call the DbContext in a controller or a service and the DI will do the rest
As per why your IConfiguration throw the null reference exception i can think of 2 possibilities. Either you need to do the other kind instanciation which would be like this
services.AddSingleton<IConfiguration,Configuration>();
Or maybe it is because you are not using DI into the DbContext itself, you shouldnt need to do the new YourContextDbContext(). You should just simply put it in the constructor of the service or controller and it should work "magically" without you actually need to make an instance of it.

ASP.NET 5 with Entity Framework 7 - No database providers are configured when try to seed DB

I am playing with ASP.NET 5 project using EF7. I have created basic testing model, made code-first migrations, database updates and now I try to send some data to created database.
I am using the default DbContext (ApplicationDbContext.cs) included with project tepmplates. When I have tried to send data with controller, everything worked. Now I want to seed database on start. To do this, I have created seed extension method to ApplicationDbContext:
public static class ProjectExtensions
{
public static void SeedDB(this ApplicationDbContext context)
{
context.Add(new TestingModel() { Name = "foo" });
context.SaveChanges();
}
}
Then, I have added method calling inside Startup.cs in Configure method:
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
using (var context = new ApplicationDbContext())
{
context.SeedDB();
}
}
But when I try to run the project, system throws an exception:
An exception of type 'System.InvalidOperationException' occurred in EntityFramework.Core.dll but was not handled in user code
Additional information: 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.
It seem strange, becuase database provider should be already configured. As I said, I am using default context with default settings. And when I call controller to make some CRUD operation, everything is working.
Does anyone know, where is a mistake?
Found solution. All seems to be fixed with this edit inside the Startup.cs in Configure method:
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
serviceScope.ServiceProvider.GetService<ApplicationDbContext>().Database.Migrate();
serviceScope.ServiceProvider.GetService<ApplicationDbContext>().SeedDB();
}
}
Hope this could help someone with the same problem :)

Dependancy Injected DbContext is empty after populating DbContext created with new (EF7, ASP.NET 5, vnext)

I am relatively new to EF7 and have heard that Dependency Injection of DbContexts into the Controller constructor is a good way to go about getting the DbContext for use within given Action methods. However, there are many situations where Dependency Injection is impossible (for example, accessing the Db in ordinary classes), and the using(VectorDbContext dbContext...) pattern must be used.
I have run into an issue where adding data to a DbContext created with the using pattern cannot be accessed by a context that was dependency injected. The DbContext is a simple InMemory database used for testing - it doesn't connect to anything.
Here is the code that adds entities to the DbContext, for testing I am calling this in Startup.cs:
using (ExampleDbContext dbContext= new ExampleDbContext()) {
dbContext.Things.Add(
new Thing() {
Stuff= "something"
});
dbContext.SaveChanges();
}
Here is the access code within the Controller:
public class ExampleController : Controller {
public ExampleController(ExampleDbContext exampleDbContext) {
this.ExampleDbContext= exampleDbContext;
}
public ExampleDbContext ExampleDbContext { get; set; }
public async Task<IActionResult> ExampleAction() {
// new DbContext:
using(ExampleDbContext dbContext = new ExampleDbContext ()) {
var List1 = (await dbContext.Things
.AsNoTracking()
.ToListAsync());
}
// Injected DbContext:
var List2 = (await this.ExampleDbContext.Things
.AsNoTracking()
.ToListAsync());
}
}
When stepping through, List1 has the expected one item in it, but List2 is always empty!
What am I doing wrong? It appears the DbContexts aren't in sync somehow, how does Dependency Injection create the DbContext/where does it come from?
EDIT: I just did some additional testing and have confirmed that any entities added within the DbContext created with new are only visible in new, and the entities added within the Injected DbContext are only visible within the Injected DbContext, leading me to believe they are connecting to different backing databases, but I cannot confirm.
I might be wrong, but my assumption is that when you create a new instance of DbContext in code, you are using the parameterless constructor that sets underlying connection string to some default value. However, DI-injected DbContext could be resolved using another constructor with different connection string passed in explicitly.
That's an example of Unity config that explicitly specifies constructor parameter:
<register type="DbContext, [assembly-name]" mapTo="DbContext, [assembly-name]">
<constructor>
<param name="nameOrConnectionString" value="Test"/>
</constructor>
</register>
So I would check a configuration of your container.

Referencing in the context of asp.net Identity 2.0 separation

I try to separate one of my API projects into three different layers.
The API
Data access(repos + uow)
Data Entities
The API is using Asp.net Identity 2.0 with code from a sample I installed, just enough to work with OAuth Authorization.
However, When I do this separation, sometimes I get an error telling me that I need to reference the third layer(entities) from my first layer. And I can't figure out why. That would break the whole purpose of the separation, right?
For example, when I try to replace this line(from the API layer in Startup.Auth.cs, ConfigureAuth method)
app.CreatePerOwinContext(ApplicationDbContext.Create);
With
app.CreatePerOwinContext(uow.CreateDbContext())
A method that returns a new instance of the ApplicationDbContext.
I would expect that context to be returned from my second layer, where my UnitOfWork is(which in turn gets the ApplicationDbContext from the data layer).
Could someone please explain how this works?
To solve your issue you need to start use Interfaces and any DI-framework. Here I can provide you with the code if you want to start using AutoFac (https://code.google.com/p/autofac/wiki/WebApiIntegration).
When you installed AutoFac to your solution through Nuget. Add this part of code in your Global.asax.cs file.
protected void Application_Start()
{
...
SetupAutoFac();
...
}
private static void SetupAutoFac()
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Setup();
var resolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
}
Create this part of code in your BLL-layer:
public static class AutoFacConfiguration
{
public static IContainer Setup(this ContainerBuilder builder)
{
REGISTER ALL YOUR SERVICES AND UOW HERE
return builder.Build();
}
}
After this you can inject every services Interface to your ApiControllers, and the the WebAPi will only have a reference to your BLL-layer or to the layer where you put all your interfaces.

Resources