Originally I was using IdentityDbContext for awhile. Everything was working and I was doing migrations and such. But recently I decided to let AWS Cognito handle the users and password thus no longer needing IdentityDbContext. So I switched my ApplicationDbContext class to inherit DbContext instead.
I cleared the migrations and snapshots, killed and pruned the docker containers. Once I try to do the initial migration I get these errors in the Package Manager Console:
An error occurred while accessing the Microsoft.Extensions.Hosting services.
Continuing without the application service provider.
Error: Object reference not set to an instance of an object.
Unable to create an object of type 'ApplicationDbContext'.
For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
After doing some research I added a class that inherits the IDesignTimeDbContextFactory and I was actually able to add migrations but the first error still remained:
An error occurred while accessing the Microsoft.Extensions.Hosting services.
Continuing without the application service provider.
Error: Object reference not set to an instance of an object.
My project structure is setup so the Startup file is in a different project than where the ApplicationDbContext Class is but it's containerized by docker-compose.
Project.Api
- Startup.cs
- Program.cs
Project.App
- ApplicationDbContext.cs
- Migrations Folder
Here is how I added the database in Startup.cs:
var connectionString = Configuration.GetSection("ConnectionStrings").GetSection("DefaultConnection").Value;
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseNpgsql(connectionString,
b =>
{
b.MigrationsAssembly("Project.App");
});
});
What the ApplicationDbContext.cs looks like:
public ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options){}
// DbSet<Entities>...
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// ...
// ...
}
}
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
private readonly DbConfig _dbConfig;
public ApplicationDbContextFactory(IOptionsMonitor<DbConfig> dbConfig)
{
_dbConfig = dbConfig.CurrentValue;
}
public ApplicationDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseNpgsql(_dbConfig.DefaultConnection);
return new ApplicationDbContext(optionsBuilder.Options);
}
}
When I run the migration command I select the Default project as Project.App, and in the Package Manager Console I write:
Add-Migration initMigration -Context ApplicationDbContext -Project Project.App -StartupProject Project.Api.
As mentioned before, with the DbContextFactory included, it just shows this error:
An error occurred while accessing the Microsoft.Extensions.Hosting services.
Continuing without the application service provider.
Error: Object reference not set to an instance of an object.
Ended up being an AWS configuration in the Program.cs file where I wasn't passing my AWS profile to the AWSOptions class.
Related
I have a Azure function solution which is using EF. I'm injecting the DBContext in the startup.cs class.
My project layout
Project>.Function (Startup.cs, Function classes ) - Set as startup project
Project>.Infrastructure (Has the DBContext class, Generated DB table Models)
appsettings.json
"ConnectionStrings": {
"MyDbCon": "Server=xxxx..............."
},
Startup.cs
public override void Configure(IFunctionsHostBuilder builder)
{
.....
serviceCollection.AddDbContext<MySampleContext>(options =>
{
options.UseSqlServer(configuration.GetConnectionString("MyDbCon"));
});
...
}
DBContext class (MySampleContext.cs)
public partial class MySampleContext : DbContext
{
private string connectionString;
public MySampleContext()
{
}
public MySampleContext(DbContextOptions<MySampleContext> options, IConfiguration configuration) : base(options)
{
var sqlServerOptionsExtension = options.FindExtension<SqlServerOptionsExtension>();
if (sqlServerOptionsExtension != null)
{
this.connectionString = sqlServerOptionsExtension.ConnectionString;
}
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(this.connectionString);
}
}
...
}
When i run the function locally, the DBContext is injected as expected. The parameterised constructure is called and im able to get the connection string. All good
BUT when i try to run the following commands
dotnet ef migrations script --output .\Infrastructure\Migrations\migration_script.sql --startup-project .\Functions --project .\Infrastructure
or
Add-Migration InitialCreate
from the PackageManager Console im getting error: Value cannot be null. (Parameter 'connectionString'). This I know is because the default constructure is being used and the connecting string is null
public MySampleContext()
{
}
When I hardcode the connection string all is Ok, but How do I get the connection string (using the default constructure) so I'm able to run EF commands above without hardcoding the connection string?
public MySampleContext()
{
this.connectionString = "Server=........";
}
There are 3 parts here -
Getting connection string from settings (local.settings.json).
Setting up EF (code-first) with Azure Functions
Generating Migrations
Making it all work on Azure
Part 1: For connection string -
In your local.settings.json add the following -
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"AzureWebJobsSecretStorageType": "files",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"MyDbCon": "your-connection-string-here",
}
Then in your startup code, use it like this -
builder.Services.AddDbContext<MySampleContext>(options =>
{
options.UseSqlServer(Environment.GetEnvironmentVariable("MyDbCon"));
});
Part 2: Setting up EF with Azure Function -
This one requires a bit of setup if you're doing code first. The following steps are defined for EF Core, it should be the same for EF.
Add the following nugets -
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.Tools
If your data-access project is separate from the main project, then add the Micrsoft.EntityFrameworkCore.Design nuget to the main project as well.
Now create a file next to your "MySampleContext" calling it - MySampleContextFactory and add the following -
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
namespace MySampleProject.DataAccess
{
public class MySampleContextFactory: IDesignTimeDbContextFactory<MySampleContext>
{
public MySampleContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<MySampleContext>()
.UseSqlServer(Environment.GetEnvironmentVariable("MyDbCon"));
return new MySampleContext(optionsBuilder.Options);
}
}
}
So your MySampleContext will now have a constructor like -
public MySampleContext(DbContextOptions<MySampleContext> options) : base(options)
{
// add other required code here
}
Part 3: Generating Migrations
For this open your package manager console, add the connection string to the environment as a variable so the MySampleContextFactory can pick it up.
Ensure the data-access project is selected (if it isn't your main project)
In the package manager console, enter the following -
$env:MyDbCon="your-connection-string-here"
Note: If you still get the connection string not found error, you'll
need to restart visual studio and enter the above again.
Now you can continue with the normal EF migration commands in the package manager console (do select the data-access library/project) -
add-migration InitialCreate
update-database
This is enough for it all to work locally. But few more steps are involved to make it work once its published.
Part 4: Making it all work in the cloud
To apply the migration to your Cloud SQL (e.g. SQL Database on Azure), you need to apply the "update-database" with your cloud-sql connection string. So you need to restart Visual Studio and enter the new connection string,
$env:MyDbCon="sql-db-cloud-conn-string"
update-database
Finally, for it to work on Azure Function once published, you need to add the MyDbCon to the application settings in the portal.
Go to your Function App in the portal and add the following & then click SAVE -
I am trying to run 'update-database -verbose' in the Package Manager Console but I am getting the following lines at the end of the output: (nothing is being generated in my SQL server)
Using context 'TutoringContext'.
Finding design-time services for provider 'Microsoft.EntityFrameworkCore.SqlServer'...
Using design-time services from provider 'Microsoft.EntityFrameworkCore.SqlServer'.
Finding design-time services referenced by assembly 'LakeTutoringWebsite'...
Finding design-time services referenced by assembly 'LakeTutoringWebsite'...
No referenced design-time services were found.
Finding IDesignTimeServices implementations in assembly 'LakeTutoringWebsite'...
No design-time services were found.
I see that I can create a DesignTimeDbContextFactory like this: https://learn.microsoft.com/en-us/ef/core/cli/dbcontext-creation?tabs=dotnet-core-cli
But the constructor must be parameterless so I can't pass an IConfiguration object to get my connection string. How can I run 'update-database' without hard coding my connection string?
Based on the above link, I thought I would be able to run 'update-database' since I have added my DBContext to services.
I am using dependency injection for my DBContext in my project:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<TutoringContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("LakeTutoringDatabase")));
}
}
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
public class TutoringContext : IdentityDbContext<IdentityUser>
{
public DbSet<Comment> Comments { get; set; }
public TutoringContext(DbContextOptions<TutoringContext> options) : base(options)
{
}
}
I am using ASP.NET Core 3.1
I installed System.Configuration.Configuration version 6.0.0 using NuGet.
Firstly, as well as creating an implementation of IDesignTimeDbContextFactory, your project will need to add a reference to package:
Microsoft.EntityFrameworkCore.Design
Normally, hard-coded strings are not so bad in this class because you are just working with a development database. Ideally, you would apply migrations to other databases (staging, beta, production) in code (instead of update-database in command line) at application startup which will use values from your appsettings.json file accessed thru IConfiguration.
However, if you do want to have the development connection string accessed from a json file, this link does a pretty good job of walking you thru it.
I am using the Visual Studio 2022/.NET 6 Razor pages template. It uses a top-level C# 10 program file. Program.cs looks something like this:
using Microsoft.EntityFrameworkCore;
using Data;
using Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMemoryCache();
builder.Services.AddRazorPages();
var settings = builder.Configuration.Get<AppSettings>();
builder.Services
.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.MapRazorPages();
app.Run();
My DBContext looks like this
public class ApplicationDbContext: DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
internal DbSet<MyModel> MyModels { get; set; }
protected override void OnModelCreating(ModelBuilder builder){ /* ... */ }
}
When I run dotnet ef migrations add InitialMigration -v, I get the following error:
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider in assembly 'Data'...
Finding Microsoft.Extensions.Hosting service provider...
No static method 'CreateHostBuilder(string[])' was found on class 'Program'.
No application service provider was found.
Finding DbContext classes in the project...
Found DbContext 'ApplicationDbContext'.
Microsoft.EntityFrameworkCore.Design.OperationException: Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
---> System.InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContextOptions`1[Data.DatabaseContext.ApplicationDbContext]' while attempting to activate 'Data.DatabaseContext.ApplicationDbContext'.
Visual Studio Add-Migration also fails to build, but provides no stack trace.
The provided link indicated that I might be able to add an implementation of IDesignTimeDbContextFactory to contain some startup code that might work.
I created this class, but it also seems to do nothing.
public class ApplicationContext: IDesignTimeDbContextFactory<ApplicationDbContext>
{
ApplicationDbContext IDesignTimeDbContextFactory<SanDiegoContext>.CreateDbContext(string[] args)
{
IConfigurationRoot configuration = MyStaticConfigurationBuilder.GetConfig();
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(connectionString: configuration.GetConnectionString("DefaultConnection"));
return new ApplicationDbContext(optionsBuilder.Options);
}
}
Is it possible to use EF Core in .NET 6 without building a GetHostBuilder(string[]) method and a Startup class? If so, what am I doing wrong?
I made it work with the following steps:
Rebuild the project.
Make sure I was using Visual Studio Command Line Tools with the right project
Install Microsoft.EntityFrameworkCore.Design to my Web project
Make sure my project builds on its own with a standard CTRL-B or dotnet build
Then my startup looks like this:
using Microsoft.EntityFrameworkCore;
using Data;
using Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMemoryCache();
builder.Services.AddRazorPages();
var settings = builder.Configuration.Get<AppSettings>();
builder.Services
.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.MapRazorPages();
app.Run();
and the migration attempts to scaffold as usual
Im trying to set up a dotnet backend with separate projects, i have the API project(which is a webapi) as the main project, then i have the persistance project that contains the DataContext class.
DataContext.cs:
using Domain;
using Microsoft.EntityFrameworkCore;
namespace Persistence
{
public class DataContext : DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
}
public DbSet<Book> Books { get; set; }
}
}
And i am injecting the DataContext into the configuration services in The startup file:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>(opt =>
{
opt.UseSqlite(Configuration.GetConnectionString("DefaultConnection"));
});
services.AddControllers();
}
then when i try adding a migration using this command:
dotnet ef migrations add InitialCreate -s API -p Persistence
It gives me that error.
Please any idea why this is happening ?
first of all check your connection string in appsettings.json and after that
run this commands for migrations
dotnet ef migrations add 1
then
dotnet ef migrations update
and if didnt work check you ef core package from *.csproject
try to add an empty constructor:
public DataContext(){}
put your (var app = builder.Build();) in Program.cs after than Adding your Context
I have an issue attempting to use Migrations in a ASP.NET Core solution using EF Core where there are multiple DbContext that share the same SQL database.
In my application startup method I'm getting a reference to each context and calling the context.Database.Migrate() method. However as both of these contexts are pointing to the same underlying database I'm getting the error:
There is already an object named '__EFMigrationsHistory' in the database.
Here's a MCVE:
class DbContextA : DbContext {}
class DbContextB : DbContext {}
static void Main(string[] args)
{
var contextA = GetContextFromDIContainer<DbContextA>();
var contextB = GetContextFromDIContainer<DbContextB>();
contextA.Database.Migrate();
contextB.Database.Migrate();
}
void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DbContextA>(opt =>
{
opt.UseSqlServer("connectionstring");
});
services.AddDbContext<DbContextB>(opt =>
{
opt.UseSqlServer("connectionstring");
});
}
Note that each DbContext exists in a separate assembly in which the Migrations are configured.
I am able to manually execute the respective migrations with the Update-Database CLI tool but it doesn't seem to work as part of my app startup code.
Is there a way to execute migrations on both contexts at runtime and bypass the __EFMigrationsHistory table creation if already exists?
I think your problem is just two Context try to use same migration history table
try specific your migration history table for each
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer(
connectionString,
x => x.MigrationsHistoryTable("__MyMigrationsHistoryForDBContextA", "mySchema"));
it should be fix
Custom Migrations History Table