Entity framework migrations errors - c#

I want to create a migration for existing entities.
I have a DataContext class
public class DataContext : DbContext
{
public DataContext()
{
}
public DataContext(DbContextOptions<DataContext> options) : base(options)
{
}
public DbSet<AppUser> Users { get; set; }
}
And I added it to the services in IServiceCollection extension method -
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<DataContext>(options =>
{
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly(typeof(DataContext).Assembly.FullName));
});
return services;
}
}
When I try to add a migration from Entity Framework command tool -
"dotnet ef migrations add InitialCreate" such error occurs:
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.
Can't really understand what's wrong with my code. It actually works by overriding OnConfiguring method but I need a solution for this kind of approach. Any ideas?

Try to remove the default public constructor (the parameterless one).
When you execute the CLI command you have some additional options like --project and --startup-project. By specifying them it will enable the use of the host builder to create your DB context, and you don't need this parameterless constructor.
The ef cli is using reflection to look into your project and then executes the host builder setup in order to have a service builder. From there it will instantiate the DB context. If it doesn't work. It will fall back first to a design time factory. And finally will use the default constructor (which is happening in this case).

Related

How to use dotnet-ef tools with DI instanciated DbContext?

I'm using a DDD approach and added a persistence layer to my project. The layer publishes an extension method to IServiceCollection which initializes efcore. I only publish repositories instead of the DbContext itself.
IServiceCollection Extension
public static IServiceCollection AddPersistenceLayer(this IServiceCollection services, string connectionString)
{
// internal dependencies
services.AddDbContextFactory<AppDbContext>(
o => o.UseSqlServer(connectionString, p => p.EnableRetryOnFailure()));
services.AddDbContext<AppDbContext>(
o => o.UseSqlServer(connectionString, p => p.EnableRetryOnFailure()));
// public dependencies (consument of DbContext)
services.AddTransient<IBookRepository, BookRepository>();
return services;
}
DbContext
internal sealed class AppDbContext: DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
:base(options)
{
}
public DbSet<Book> Books { get; set; }
}
This works fine so far when using my app. But when I want to use the dotnet-ef tools the efcore wont be initialized because there is no startup project which calls the above extension method.
Is there a way to somehow work around this? I wish I just could pass the connectionstring from the CLI.
Migrations are also located in the persistence layer project if that matters.
What I've tried is to create a startup project which calls the extension method and then use the following command. But without success.
dotnet ef database update `
--project .\src\Persistence\Persistence.csproj `
--startup-project .\src\Persistence.Tools\Persistence.Tools.csproj `
0
I keep getting the error message
Unable to create an object of type 'AppDbContext'.
Maybe this example can help you in what you want to do:
https://andrewlock.net/using-dependency-injection-in-a-net-core-console-application/

Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContextOptions'

I am trying to access the databse using EF Core 5.0 on an ASP.NET Core project. For the first migration, I overrode the OnConfiguring() method on the DBContext and updated the database successfully.
For the second migration, I decided to use the dependency injection in ASP.NET Core following the guidelines. Here are the changes I made.
Added services.AddDbContext in my Startup.cs.
Removed the OnConfiguring() method from DBContext.
After running dotnet ef migrations add Posts, I get following error:
Microsoft.EntityFrameworkCore.Design.OperationException:
Unable to create an object of type 'BlogContext'.
For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
If I add the --verbose flag, I get this output:
Build started...
dotnet build blog/app/app.csproj /verbosity:quiet /nologo
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:02.50
Build succeeded.
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider in assembly 'app'...
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 'BlogContext'.
Microsoft.EntityFrameworkCore.Design.OperationException: Unable to create an object of type 'BlogContext'. 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[app.Data.BlogContext]' while attempting to activate 'app.Data.BlogContext'.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetServiceOrCreateInstance(IServiceProvider provider, Type type)
at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.<>c__DisplayClass13_4.<FindContextTypes>b__13()
Unable to create an object of type 'BlogContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
However, the code works as expected when I run the web application, as in the BlogContext is created and injected into my classes by the DI layer, and I can access the database.
Hence, I am guessing the DI layer is not running as expected when running the dotnet ef migrations add command.
Here's my code.
// Program.cs
public class Program
{
public static void Main(string[] args)
{
IHostBuilder builder = Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
IHost host = builder.Build();
host.Run();
}
}
// BlogContext
using app.Models;
using Microsoft.EntityFrameworkCore;
namespace app.Data
{
public class BlogContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
public BlogContext(DbContextOptions<BlogContext> options) : base(options)
{
}
}
}
// Startup.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public string ConnectionString => Configuration.GetConnectionString("Blog");
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BlogContext>(opt => { opt.UseSqlite(ConnectionString); });
}
}
Both the Startup and BlogContext live in the same project.
In my experience, better to create a class (in the same project as the context) that implements IDesignTimeDbContextFactory - as described here. That way, there's not guessing as to which code will get used when using the design time tools such as update-database
Thanks to Ivan and Kirk's comments above and reading the entire verbose output, I figured out the problem. Turned out I was not following the correct pattern in Program.cs.
From the documentation,
The tools first try to obtain the service provider by invoking Program.CreateHostBuilder(), calling Build(), then accessing the Services property.
I had refactored the original Program.cs by moving CreateHostBuilder() inside main(), which broke the ef-core migration.
After modifying the Program.cs to following it works as expected.
public class Program
{
public static void Main(string[] args)
=> CreateHostBuilder(args).Build().Run();
// EF Core uses this method at design time to access the DbContext
public static IHostBuilder CreateHostBuilder(string[] args)
=> Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(
webBuilder => webBuilder.UseStartup<Startup>());
}

No Database Provider Error even when DBContext is configured

Mine is a .net core web api project with EF. My plan was to use connection string from Appsettings.json and thus I configured like this in startup.cs
services.AddDbContext<dbLNePMODev1Context>(options => {
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});
Also here is how my DBContext constructor looks like
public partial class dbLNePMODev1Context : DbContext
{
public dbLNePMODev1Context()
{
}
public dbLNePMODev1Context(DbContextOptions<dbLNePMODev1Context> options)
: base(options)
{
}
....
.....
}
But even then I am getting the following error when trying to connect with DB
'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.'
Sorry I believe I could be missing something since I am new to EF. Please help me what I did wrong here.

EF Core Migration with multiple provider always runs SQL Server scripts

I am using EF Core Code first and I have an issue when using multiple DB providers (SQL Server and MySql).
Even when I choose to use MySql DB provider, SQL server migration files are used.
Check the sample project
In my case, I use the same migration for both providers but sometimes I need to do some changes to migration files manually. for example added annotation for both providers (or change the type of some fields like varchar to nvarchar).
Id = table.Column<int>(nullable: false)
.Annotation("MySql:ValueGeneratedOnAdd", true)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
For me it's work fine
Writing provider-specific migrations, database initializers, and services with Entity Framework core can be a difficult task. Use AdaptiveClient to greatly simplify the process. AdaptiveClient is a utility that works with Autofac for provisioning a service layer against multiple database providers or transports. AdaptiveClient.EntityFrameworkCore is an add-on that includes utilities for working with Entity Framework Core. In a nutshell here is how it AdaptiveClient works:
IMigrationContext is a placeholder interface that allows you to associate your DbContext with a specific provider (MSSQL, MySql, etc) for the purpose of creating a migration.
IDbContextOptions is a placeholder interface that allows you to associate an implementation of DbContextOptions that is specific to your provider.
RegistrationHelper is a utility that simplifies registering your components with Autofac. RegisterMigrationContext is a method you can call to easily register your provider-specific migration context.
To create provider-specific migrations you create a class for each database provider you want to target. These classes derive from your DbContext and implement IMigrationContext (which has no members):
public class MyDbContext_MSSQL : MyDbContext, IMigrationContext
{
public MyDbContext_MSSQL(DbContextOptions options) : base(options)
{
}
}
public class MyDbContext_MySQL : MyDbContext, IMigrationContext
{
public MyDbContext_MySQL(DbContextOptions options) : base(options)
{
}
}
The examples above are complete - you do not have to write any additional code. You do not need to create a separate DbContext for each provider (unless you wish to do so). The reason you need to create a class for each provider is because EF reflects on your assembly to find the correct DbContext when you run dotnet ef migrations add....
Create classes that wrap DbContextOptions and implement IDbContextOptions:
public class DbContextOptions_MSSQL : IDbContextOptions
{
public DbContextOptions Options { get; set; }
public DbContextOptions_MSSQL(string connectionString)
{
DbContextOptionsBuilder builder = new DbContextOptionsBuilder();
builder.UseSqlServer(connectionString);
Options = builder.Options;
}
}
public class DbContextOptions_MySQL : IDbContextOptions
{
public DbContextOptions Options { get; set; }
public DbContextOptions_MySQL(string connectionString)
{
DbContextOptionsBuilder builder = new DbContextOptionsBuilder();
builder.UseMySql(connectionString);
Options = builder.Options;
}
}
Use the AdaptiveClient RegistrationHelper to register your classes with Autofac:
registrationHelper.RegisterMigrationContext<Database.Db_MSSQL>(API_Name.MyAPI, DataBaseProviderName.MSSQL);
registrationHelper.RegisterMigrationContext<Database.Db_MySQL>(API_Name.MyAPI, DataBaseProviderName.MySQL);
registrationHelper.RegisterDbContextOptions<DbContextOptions_MSSQL>(DataBaseProviderName.MSSQL);
registrationHelper.RegisterDbContextOptions<DbContextOptions_MySQL>(DataBaseProviderName.MySQL);
In the code above API_Name is just a constant that resolves to a simple string like "MyApplicationName". Same with DataBaseProviderName.MSSQL and .MySQL. They are string constants that resolve to "MSSQL" or "MySQL".
Now, here is the most important part: Just as you register the components of your application using keys such as "MSSQL" or "MySQL", you also register the connection strings for your application using those same constants.
This allows Autofac to resolve the correct provider-specific or transport-specific components based on nothing more than the connection string currently in use for application. You can read up on the entire process here.
You can see the complete working example in the Zamagon Demo. The demo illustrates migrations, database initializers, and drop-and-recreate scenarios for integration testing.

Why does EF7 say I haven't configured any providers, when ASP.net Identity is working fine with the same DbContext?

I've just started work on an experimental project using ASP.net 5, MVC6 and Entity Framework 7. I have ASP.Net Identity working fine, but then tried to add some of my own data to the DbContext and hit this problem. EF7 reports:
An unhandled exception occurred while processing the request.
InvalidOperationException: No database providers are configured.
Configure a database provider by overriding OnConfiguring in your
DbContext class or in the AddDbContext method when setting up
services.
Microsoft.Data.Entity.Internal.DatabaseProviderSelector.SelectServices(ServiceProviderSource
providerSource) Stack Query Cookies Headers
InvalidOperationException: No database providers are configured.
Configure a database provider by overriding OnConfiguring in your
DbContext class or in the AddDbContext method when setting up
services.
Here's my configuration method:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddTransient<ApplicationUnitOfWork>(instance => new ApplicationUnitOfWork());
}
ApplicationUnitOfWork is a façade over EF7 to reduce tight coupling. Here's the whole thing so far:
public class ApplicationUnitOfWork : IUnitOfWork
{
readonly ApplicationDbContext dbContext;
public ApplicationUnitOfWork()
{
dbContext = new ApplicationDbContext();
Products = new ProductRepository(dbContext);
}
public void Dispose() { dbContext.Dispose(); }
public IUserRepository Users { get; }
public IRepository<SoftwareProduct> Products { get; }
public async Task CommitAsync() { await dbContext.SaveChangesAsync(); }
public void Cancel() { throw new NotImplementedException(); }
}
Now when I run the web application, I am able to register a user account and log in and EF creates the database, creates the Identity tables and populates them with user data. The products table is also created - so clearly at some level EF is able to use the ApplicationDbContext and find a provider which it uses to create the database and schema.
However when I try to access my Products controller, which uses the ProductsRepository, EF complains - even though ApplicationDbContext is used in both cases and was used to create the database!
So how come? What's special about ASP.net Identity that it can somehow obtain a provider, but my own code can't? What "magic incantation" am I missing?
its because you are newing it up instead of having it injected for you. The one you are newing up hasn't been configured.
You should change your class to have it passed into the constructor.
DbContext has more than one constructor and you are using the empty one which doesn't configure it.
Better to let it be injected by making your constructor like this:
public ApplicationUnitOfWork(ApplicationDbContext context)
{
dbContext = context;
Products = new ProductRepository(dbContext);
}
your code shows that ApplicationDbContext has been registered with the DI, Identity is using the injected one but you are not since you newed it up yourself with the parameterless constructor
you should also register your ApplicationUnitOfWork so it can be injected:
services.AddScoped<ApplicationUnitOfWork, ApplicationUnitOfWork>();

Categories