How to access ER Core migrations arguments in code - c#

According to the latest documentation, EF Core supports an --environment parameter, which can be used to define the environment during migration script generation.
I am using it as follows:
dotnet ef migrations script --context:DataContext
-o:migrs.sql 20221024100851_latest_executed_migration -- --environment Production
CLI reports that the parameter is understood:
Build started...
Build succeeded.
info: xxxxx.xxxx[0]
Configuring: 'xxxxx'
Environment: 'Production'
ContentRoot: 'D:\GIT\xxxxx\src\app'
but the script generation still uses the Development environment instead. The following two environment variables are accessible in the C# code:
ASPNETCORE_ENVIRONMENT=Development
DOTNET_ENVIRONMENT=Development
I access the variables using the following command:
public partial class test1 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
StringBuilder sb = new StringBuilder();
foreach (DictionaryEntry test in System.Environment.GetEnvironmentVariables())
{
sb.AppendLine(test.Key.ToString() + "=" + test.Value.ToString());
}
migrationBuilder.Sql(sb.ToString());
}
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
EF Core version 6.0.10 is used.
There is no launchSettings.json file located in the project.
Other SO questions suggest setting the ASPNETCORE_ENVIRONMENT variable in a PowerShell script before command execution, but this is not my goal. I would like to get the new EF Core 5 way working.
Many thanks

Related

Custom Entity Framework Core Migration Script?

I need to make a "full-text indexed" but I am using ef core 2.1 code first migration, but Full Indexes do not have first class support in core.
How can I write my own migration that would be applied while the other generated migrations are being applied?
You have to create empty migration and then in Up method you can write your SQL query.
Create Empty Migration by running Add-Migration command in Package
Manager Console.
Add the SQL function in Up method like the following way.
public partial class your_migration_name : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(#"<YourQuery>");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(#"Drop <YourQuery>");
}
}
Run Update-Database in Package Manager Console.

EF Core 2.0 update database again, once all migrations run

It's a simple task I think.
My requirement is to run one .sql file after all migrations runs successfully.
it contains few alter statements. the system is in a way that I must have to run this Sql, there are no other way like I just update my entity.
I am using asp.net zero architecture.
Right now I am updating my migrations manually and adding this query's with
migrationBuilder.Sql("");
but it's hard to maintain.
I have done some R&D on this topic but not found anything proper.
as I am following best practice of .net boilerplate structure, I would like to hear from boilerplate dev side too.
You can implement the requirement by creating Stored Procedures or Scalar value function.
Create Empty Migration by running Add-Migration command in
Package Manager Console.
Add the SQL query in Up method like the following way.
public partial class testQuery : Migration {
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(#"You query");
}
}
Or
Add Stored Procedures folder and testSP.sql inside it. testSP.sql will have whole SP definition.
protected override void Up(MigrationBuilder migrationBuilder)
{
var spPath = #"MyCompany.MyTemplate.EntityFrameworkCore\Migrations\Stored Procedures\testSP.sql";
var spMigratorPath = Path.Combine("..", spPath);
if (!File.Exists(spMigratorPath))
{
spMigratorPath = Path.Combine("..", "..", "..", "..", spPath);
}
migrationBuilder.Sql(File.ReadAllText(spMigratorPath));
}
Run Update-Database in Package Manager Console.
It will create the function or SP in DB.
Now You can call SP by using ExecuteSqlCommand or ExecuteSqlCommandAsync methods.
You can also refer this.

Unable to create migrations after upgrading to ASP.NET Core 2.0

After upgrading to ASP.NET Core 2.0, I can't seem to create migrations anymore.
I'm getting
"An error occurred while calling method 'BuildWebHost' on class
'Program'. Continuing without the application service provider. Error:
One or more errors occurred. (Cannot open database "..." requested by
the login. The login failed. Login failed for user '...'"
and
"Unable to create an object of type 'MyContext'. Add an implementation
of 'IDesignTimeDbContextFactory' to the project, or see
https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns
supported at design time."
The command I previously ran was $ dotnet ef migrations add InitialCreate --startup-project "..\Web" (from the project/folder with the DBContext).
Connection string: "Server=(localdb)\\mssqllocaldb;Database=database;Trusted_Connection=True;MultipleActiveResultSets=true"
This is my Program.cs
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
You can add a class that implements IDesignTimeDbContextFactory inside of your Web project.
Here is the sample code:
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<CodingBlastDbContext>
{
public CodingBlastDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<CodingBlastDbContext>();
var connectionString = configuration.GetConnectionString("DefaultConnection");
builder.UseSqlServer(connectionString);
return new CodingBlastDbContext(builder.Options);
}
}
Then, navigate to your Database project and run the following from command line:
dotnet ef migrations add InitialMigration -s ../Web/
dotnet ef database update -s ../Web/
-s stands for startup project and ../Web/ is the location of my web/startup project.
resource
No need for IDesignTimeDbContextFactory.
Run
add-migration initial -verbose
that will reveal the details under
An error occurred while accessing the IWebHost on class 'Program'. Continuing without the application service provider.
warning, which is the root cause of the problem.
In my case, problem was, having ApplicationRole : IdentityRole<int> and invoking services.AddIdentity<ApplicationUser, IdentityRole>() which was causing below error
System.ArgumentException: GenericArguments[1], 'Microsoft.AspNetCore.Identity.IdentityRole',
on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`9[TUser,TRole,TContext,
TKey,TUserClaim,TUserRole,TUserLogin,TUserToken,TRoleClaim]' violates the constraint of type 'TRole'.
---> System.TypeLoadException: GenericArguments[1], 'Microsoft.AspNetCore.Identity.IdentityRole',
on 'Microsoft.AspNetCore.Identity.UserStoreBase`8[TUser,TRole,TKey,TUserClaim,
TUserRole,TUserLogin,TUserToken,TRoleClaim]' violates the constraint of type parameter 'TRole'.
Solution 1: (Find the problem in 99% of cases)
Set Web Application project as Startup Project
Run the following commands with -verbose option.
Add-Migration Init -Verbose
-verbose option helps to actually uncover the real problem, It
contains detailed errors.
Solution 2:
Rename BuildWebHost() to CreateWebHostBuilder(), because Entity Framework Core tools expect to find a CreateHostBuilder method that configures the host without running the app.
.NET Core 2.2
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
.NET Core 3.1
Rename BuildWebHost() to CreateHostBuilder()
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>();
});
}
Solution 3:
Make sure you added Dbcontext to dependency injection:
AddDbContext<TContext> will make both your DbContext type, TContext, and the corresponding DbContextOptions<TContext> available for injection from the service container.
This requires adding a constructor argument to your DbContext type that accepts DbContextOptions<TContext>.
Example:
In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
}
AppDbContext code:
public class AppDbContext: DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
:base(options)
{ }
}
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
Just rename BuildWebHost() to CreateWebHostBuilder(), because migrations use this method by default.
In my case, the cause of the problem was multiple startup projects. I have three projects in my solution: Mvc, Api, and Dal. DbContext and Migrations in the Dal project.
I had configured multiple startup projects. Both Mvc and Api projects were running when I clicked Start. But in this case I was getting this error.
"Unable to create an object of type 'MyContext'. Add an implementation
of 'IDesignTimeDbContextFactory' to the project, or see
https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns
supported at design time."
I could successfully add migration after setting Mvc as the only startup project and selecting Dal in the Package Manager Console.
In the AppContext.cs besides AppContext class add another class:
// required when local database deleted
public class ToDoContextFactory : IDesignTimeDbContextFactory<AppContext>
{
public AppContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<AppContext>();
builder.UseSqlServer("Server=localhost;Database=DbName;Trusted_Connection=True;MultipleActiveResultSets=true");
return new AppContext(builder.Options);
}
}
This will solve your second problem:
"Unable to create an object of type 'MyContext'. Add an implementation of 'IDesignTimeDbContextFactory' to the project,
After that you will be able to add-migration Initial and execute it by running update-database command.
However if running these commands when there is no DataBase yet in your local SqlServer you will get the warning like your first error: "An error
occurred while calling method 'BuildWebHost' on class 'Program'... The
login failed. Login failed for user '...'"
But it is not error because migration will be created and it can be executed.
So just ignore this error for the first time, and latter since Db will exist it won't happen again.
You can try this solution from this discussion, which was inspired by this post.
public static IWebHost MigrateDatabase(this IWebHost webHost)
{
using (var scope = webHost.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var db = services.GetRequiredService<MyContext>();
db.Database.Migrate();
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while migrating the database.");
}
}
return webHost;
}
public static void Main(string[] args)
{
BuildWebHost(args)
.MigrateDatabase()
.Run();
}
Something that really helped me was this article: https://elanderson.net/2017/09/unable-to-create-an-object-of-type-applicationdbcontext-add-an-implementation-of-idesigntimedbcontextfactory/
The basic idea is that in the change over from .net core 1 to 2 all db initialization should be moved out of the StartUp.cs and into the Program.cs. Otherwise the EF tasks try and run your DB inits when doing tasks.
"There is a nice section in the official migration docs (https://learn.microsoft.com/en-us/ef/core/miscellaneous/1x-2x-upgrade) titled “Move database initialization code” which I seemed to have missed. So before you head down any rabbit holes like I did make sure this isn’t what is causing your need to add an implementation of IdesignTimeDbContextFactory."
Please verify that you have the reference
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0" />
From
https://learn.microsoft.com/en-us/ef/core/miscellaneous/cli/dbcontext-creation
When you create a new ASP.NET Core 2.0 application, this hook is
included by default. In previous versions of EF Core and ASP.NET Core,
the tools try to invoke Startup.ConfigureServices directly in order to
obtain the application's service provider, but this pattern no longer
works correctly in ASP.NET Core 2.0 applications. If you are upgrading
an ASP.NET Core 1.x application to 2.0, you can modify your Program
class to follow the new pattern.
Add Factory in .Net Core 2.x
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);
}
}
I had this problem and this solved By Set -> Web Application(Included Program.cs) Project to -> "Set as Startup Project"
Then run -> add-migration initial -verbose
in Package Manager Console
Set as Startup Project
If you want to avoid those IDesignTimeDbContextFactory thing: Just make sure that you don't use any Seed method in your startup. I was using a static seed method in my startup and it was causing this error for me.
I was facing the error
"Unable to create an object of type 'MyContext'. Add an implementation of 'IDesignTimeDbContextFactory' to the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for additional patterns supported at design time."
This is how my problem was solved. Run the below command while you are in your solution directory
dotnet ef migrations add InitialMigration --project "Blog.Infrastructure" --startup-project "Blog.Appication"
Here Application is my startup project containing the Startup.cs class & Infrastructure is my project containing the DbContext class.
then run update using the same structure.
dotnet ef database update --project "Blog.Infrastructure" --startup-project "Blog.Application"
Previously, you configured the seed data in the Configure method in Startup.cs. It is now recommended that you use the Configure method only to set up the request pipeline. Application startup code belongs in the Main method.
The refactored Main method. Add the following references to the Program.cs:
using Microsoft.Extensions.DependencyInjection;
using MyProject.MyDbContextFolder;
public static void Main(string[] args)
{
var host = BuildWebHost(args);
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<MyDbConext>();
DbInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while seeding the database.");
}
}
host.Run();
}
There's a problem with ef seeding db from Startup.Configure in 2.0 ... you can still do it with this work around. Tested and worked fine
https://garywoodfine.com/how-to-seed-your-ef-core-database/
In my case I got the problem because I had a method named SeedData.EnsurePopulated() being called on my Startup.cs file.
public class Startup
{
public Startup(IConfiguration configuration) => Configuration = configuration;
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
//
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseStaticFiles();
app.UseSession();
app.UseMvc(routes =>
{
//
});
SeedData.EnsurePopulated(app);
}
}
The work of SeedData class is to add initial data to the database table. It's code is:
public static void EnsurePopulated(IApplicationBuilder app)
{
ApplicationDbContext context = app.ApplicationServices.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
if (!context.Products.Any())
{
context.Products.AddRange(
new Product
{
Name = "Kayak",
Description = "A boat for one person",
Category = "Watersports",
Price = 275
},
....
);
context.SaveChanges();
}
}
SOLUTION
Before doing migration simply comment out the calling of SeedData class in the Startup.cs file.
// SeedData.EnsurePopulated(app);
That solved my problem and hope your problem is also solved in the same way.
I ran into same problem. I have two projects in the solution. which
API
Services and repo, which hold context models
Initially, API project was set as Startup project.
I changed the Startup project to the one which holds context classes.
if you are using Visual Studio you can set a project as Startup project by:
open solution explorer >> right-click on context project >> select Set as Startup project
First of all make sure you have configured your database in Startup.cs
In my case, i was getting this error since i didn't specify the below in Startup.cs
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection"), x => x.MigrationsAssembly("<Your Project Assembly name where DBContext class resides>")));
Using ASP.NET Core 3.1 and EntityFrameWorkCore 3.1.0. Overriding the OnConfiguring of the context class with a parameterless constructor only
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString = configuration.GetConnectionString("LibraryConnection");
optionsBuilder.UseSqlServer(connectionString);
}
}
I got the same issue since I was referring old- Microsoft.EntityFrameworkCore.Tools.DotNet
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
After upgrading to the newer version it got resolved
In main project's appsettings.json file, I had set 'Copy to Output directory' to "Copy always" and it worked.
Sample DB context class for .net core console applications
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
using System.IO;
namespace EmailServerConsole.Data
{
public class EmailDBContext : DbContext
{
public EmailDBContext(DbContextOptions<EmailDBContext> options) : base(options) { }
public DbSet<EmailQueue> EmailsQueue { get; set; }
}
public class ApplicationContextDbFactory : IDesignTimeDbContextFactory<EmailDBContext>
{
EmailDBContext IDesignTimeDbContextFactory<EmailDBContext>.CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<EmailDBContext>();
var connectionString = configuration.GetConnectionString("connection_string");
builder.UseSqlServer(connectionString);
return new EmailDBContext(builder.Options);
}
}
}
You also can use in the startup class constructor to add json file (where the connection string lies) to the configuration. Example:
IConfigurationRoot _config;
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json");
_config = builder.Build();
}
For me it was because I changed the Output Type of my startup project from Console Application to Class Library.
Reverting to Console Application did the trick.
I had this issue in a solution that has:
a .NET Core 2.2 MVC project
a .NET Core 3.0 Blazor project
The DB Context in a .NET Standard 2.0 class library project
I get the "unable to create an object..." message when the Blazor project is set as the start up project, but not if the MVC project is set as the startup project.
That puzzles me, because in the Package Manager Console (which is where I'm creating the migration) I have the Default project set to a the C# class library that actually contains the DB Context, and I'm also specifying the DB context in my call to add-migration add-migration MigrationName -context ContextName, so it seems strange that Visual Studio cares what startup project is currently set.
I'm guessing the reason is that when the Blazor project is the startup project the PMC is determining the version of .NET to be Core 3.0 from the startup project and then trying to use that to run the migrations on the .NET Standard 2.0 class library and hitting a conflict of some sort.
Whatever the cause, changing the startup project to the MVC project that targets Core 2.2, rather than the Blazor project, fixed the issue
For me the problem was that I was running the migration commands inside the wrong project. Running the commands inside the project that contained the Startup.cs rather than the project that contained the DbContext allowed me to move past this particular problem.
In my case setting the StartUp project in init helps. You can do this by executing
dotnet ef migrations add init -s ../StartUpProjectName
Manzur Alahi is right! I'm trying to learn Rider by JetBrains and I had the same error when I was trying to use dotnet-ef migrations add ... in Cmd, PowerShell, etc. but when I used Visual Studio IDE I didn't have problem.
I fixed the error with:
dotnet ef migrations add InitialMigration --project "Domain.Entities" --startup-project "WebApi"
and this to update the database
dotnet ef database update --project "Domain.Entities" --startup-project "WebApi"
just like Manzur Alahi said.
If context class is in another class library project and this error is occurred, change command line default project to the context project and set solution startup project to the main API / ASP.net core project (that your DI container is there), then re-run command
It seems ef core tools package has this bug a reported in
https://github.com/dotnet/efcore/issues/23957 and https://github.com/dotnet/efcore/issues/23853
I had same problem. Just changed the ap.jason to application.jason and it fixed the issue

DropCreateDatabaseIfModelChanges when EF is another project

I have separated my solution in separate projects, a DAL project with entity framework and an ASP.NET MVC project.
I want to use DropCreateDatabaseIfModelChanges, but I don't know where to put it to make it work.
I've tried to put it in the web.config of the MVC project and the app.config of the DAL project (both by making use of the context element), I've tried putting it in the global.asax (Database.SetInitializer(new DropCreateDatabaseIfModelChanges<BreakAwayContext>());), I've tried a custom initialization class, but none of these seem to work.
If possible, I don't want to make use of migrations. How can I make it work?
You could create a class to implement CreateDatabaseIfNotExists and call Database.SetInitializer function in Application_Start().
-DbInitializer
public class MyDbInitializer : CreateDatabaseIfNotExists<MyDbContext>
{
protected override void Seed(MyDbContext context)
{
//Data initializing...
}
}
-Application_Start
protected void Application_Start()
{
Database.SetInitializer(new MyDbInitializer());
}
The database will be create when running the application.
And if you would like to do a automatic migration of database, use MigrateDatabaseToLatestVersion class
public class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
this.AutomaticMigrationsEnabled = true;
}
}
-Application_Start
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext,Configuration>());
Howerver, I recomand that using migraion commands will be more flexible. See this walkthru: Overview of Entity Framework Code First Migrations with example, by Bhavik Patel.
I guess by 'database initialization' you actually mean 'updating the database schema'.
Set the EfRepository as start up project of the solution
Open the Package manager console Choose EfRepository as default project
Run the following commands:
Enable-Migrations -ConnectionStringName "EfDataRepository"
Add-Migration Initial -ConnectionStringName "EfDataRepository"
Update-Database -ConnectionStringName "EfDataRepository" -Script -SourceMigration:0
This will give you a .sql script. Execute it against your database (and usually store it as part of the solution - either Create.sql or some kind of a migration .sql, depends on whether you already have a schema or you are creating it from scratch).
Of course, replace EfDataRepository with the data connection name from your .config file.

Add migration with different assembly

I am working on a project with ASP.NET CORE 1.0.0 and I am using EntityFrameworkCore. I have separate assemblies and my project structure looks like this:
ProjectSolution
-src
-1 Domain
-Project.Data
-2 Api
-Project.Api
In my Project.Api is the Startup class
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ProjectDbContext>();
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ProjectDbContext>()
.AddDefaultTokenProviders();
}
The DbContext is in my Project.Data project
public class ProjectDbContext : IdentityDbContext<IdentityUser>
{
public ProjectDbContext(DbContextOptions<ProjectDbContext> options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var builder = new ConfigurationBuilder();
builder.SetBasePath(Directory.GetCurrentDirectory());
builder.AddJsonFile("appsettings.json");
IConfiguration Configuration = builder.Build();
optionsBuilder.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection"));
base.OnConfiguring(optionsBuilder);
}
}
When I try to make the initial migration, I get this error:
"Your target project 'Project.Api' doesn't match your migrations assembly 'Project.Data'. Either change your target project or change your migrations assembly.
Change your migrations assembly by using DbContextOptionsBuilder. E.g. options.UseSqlServer(connection, b => b.MigrationsAssembly("Project.Api")). By default, the migrations assembly is the assembly containing the DbContext.
Change your target project to the migrations project by using the Package Manager Console's Default project drop-down list, or by executing "dotnet ef" from the directory containing the migrations project."
After I seeing this error, I tried to execute this command located in Project.Api:
dotnet ef --startup-project ../Project.Api --assembly "../../1 Data/Project.Data" migrations add Initial
and I got this error:
"Unexpected value '../../1 Domain/Project.Data' for option 'assembly'"
I don't know why I get this error, when I try to execute the command with the '-assembly' parameter.
I can't create a Initial Migration from other assembly and I've searched for information about it but didn't got any results.
Has someone had similar issues?
All EF commands have this check:
if (targetAssembly != migrationsAssembly)
throw MigrationsAssemblyMismatchError;
targetAssembly = the target project you are operating on. On the command line, it is the project in the current working directory. In Package Manager Console, it is whatever project is selected in the drop down box on the top right of that window pane.
migrationsAssembly = assembly containing code for migrations. This is configurable. By default, this will be the assembly containing the DbContext, in your case, Project.Data.dll.
As the error message suggests, you have have a two options to resolve this
1 - Change target assembly.
cd Project.Data/
dotnet ef --startup-project ../Project.Api/ migrations add Initial
// code doesn't use .MigrationsAssembly...just rely on the default
options.UseSqlServer(connection)
2 - Change the migrations assembly.
cd Project.Api/
dotnet ef migrations add Initial
// change the default migrations assembly
options.UseSqlServer(connection, b => b.MigrationsAssembly("Project.Api"))
I had the same problem until I noticed that on the package manager console top bar => "Default Projects" was supposed to be "Project.Data" and not "Project.API".
Once you target the "Project.Data" from the dropdown list and run the migration you should be fine.
Using EF Core 2, you can easily separate your Web project from your Data (DbContext) project. In fact, you just need to implement the IDesignTimeDbContextFactory interface. According to Microsoft docs, IDesignTimeDbContextFactory is:
A factory for creating derived DbContext instances. Implement this
interface to enable design-time services for context types that do not
have a public default constructor. At design-time, derived DbContext
instances can be created in order to enable specific design-time
experiences such as Migrations. Design-time services will
automatically discover implementations of this interface that are in
the startup assembly or the same assembly as the derived context.
In the bottom code snippet you can see my implementation of DbContextFactory which is defined inside my Data project:
public class DbContextFactory : IDesignTimeDbContextFactory<KuchidDbContext>
{
public KuchidDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var dbContextBuilder = new DbContextOptionsBuilder<KuchidDbContext>();
var connectionString = configuration.GetConnectionString("Kuchid");
dbContextBuilder.UseSqlServer(connectionString);
return new KuchidDbContext(dbContextBuilder.Options);
}
}
Now, I can initialize EF migration by setting my Web project as the StartUp project and selecting my Data project inside the Package Manager Console.
Add-Migration initial
You can find more details here. However, this blog post uses an obsoleted class instead of IDesignTimeDbContextFactory.
Add Migration With CLI Command:
dotnet ef migrations add NewMigration --project YourAssemblyName
Add Migration With PMC Command:
Add-Migration NewMigration -Project YourAssemblyName
Link About CLI Commands
Link About PMC Commands
I ran on the same problem and found this
We’re you trying to run your migrations on a class library? So was I. Turns out this isn’t supported yet, so we’ll need to work around it.
EDIT: I found solution on this git repo
Currently I think EF only supports to add migrations on projects not yet on class libraries.
And just side note for anybody else who wants to add migrations to specific folder inside your project:
EF CLI not support this yet. I tried --data-dir but it didn't work.
The only thing works is to use Package Manager Console:
Pick your default project
use -OutputDir command parameter, .e.g., Add-Migration InitConfigurationStore -OutputDir PersistedStores/ConfigurationStore command will output the mgiration to the folder 'PersistedStores/ConfigurationStore' in my project.
Updates as of 10/12/2017
public void ConfigureServices(IServiceCollection services)
{
...
string dbConnectionString = services.GetConnectionString("YOUR_PROJECT_CONNECTION");
string assemblyName = typeof(ProjectDbContext).Namespace;
services.AddDbContext<ProjectDbContext>(options =>
options.UseSqlServer(dbConnectionString,
optionsBuilder =>
optionsBuilder.MigrationsAssembly(assemblyName)
)
);
...
}
Updates as of 1/4/2021
I am using EF Core 5.0 this time. I was hoping optionBuilder.MigrationAssembly() method would work when you want to generate migrations under a folder in the target project but it didn't.
The structure I have this time is:
src
- presentation
- WebUI
- boundedContext
- domain
- application
- infrastructure
- data/
- appDbContext
- email-services
- sms-services
See I have the infrastructure as a class library, and it contains multiple folders because I want to just have a single project to contain all infrastructure related services. Yet I would like to use folders to organize them.
string assemblyName = typeof(ProjectDbContext).Namespace would give me the correct path "src/infrastructure/data", but doing add-migration still fails because that folder is not an assembly!
Could not load file or assembly. The system cannot find the file
specified.
So the only thing that actually works is, again, to specify the output folder...
Using .NET Core CLI you would have to open the command line under your target project, and do the following:
dotnet ef migrations add Init
-o Data\Migrations
-s RELATIVE_PATH_TO_STARTUP_PROJECT
Directory Structure
Root
APIProject
InfrastructureProject
By going Root directory
To add migration
dotnet ef migrations add Init --project InfrastructureProject -s APIProject
To update database
dotnet ef database update --project InfrastructureProject -s APIProject
(ASP.NET Core 2+)
Had the same issue.
Here is what I did:
Reference the project that contains the DbContext (Project.A) from the project that will contain the migrations (Project.B).
Move the existing migrations from Project.A to Project.B
(If you don't have migrations - create them first)
Configure the migrations assembly inside Project.A
options.UseSqlServer(
connectionString,
x => x.MigrationsAssembly("Project.B"));
Assuming your projects reside in the same parent folder:
dotnet ef migrations add Init --p Project.B -c DbContext
The migrations now go to Project.B
Source: Microsoft
dotnet ef update-database --startup-project Web --project Data
Web is my startup project
Data is my the my class library
There are multiple projects included in the Solution.
Solution
|- MyApp (Startup Proj)
|- MyApp.Migrations (ClassLibrary)
Add-Migration NewMigration -Project MyApp.Migrations
Note: MyApp.Migrations also includes the DbContext.
The below command did the trick for me. I'm using VS Code and I run the following command:
SocialApp.Models> dotnet ef migrations add InitialMigartion --startup-project ../SocialApp.API
Courtesy: https://github.com/bricelam/Sample-SplitMigrations
This is for EF Core 3.x.
Based on this answer from Ehsan Mirsaeedi and this comment from Ales Potocnik Hahonina, I managed to make Add-Migration work too.
I use Identity Server 4 as a NuGet package and it has two DB contexts in the package.
Here is the code for the class that implements the IDesignTimeDbContextFactory interface:
public class PersistedGrantDbContextFactory : IDesignTimeDbContextFactory<PersistedGrantDbContext>
{
public PersistedGrantDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var dbContextBuilder = new DbContextOptionsBuilder<PersistedGrantDbContext>();
var connectionString = configuration.GetConnectionString("db");
dbContextBuilder.UseSqlServer(connectionString, b => b.MigrationsAssembly("DataSeeder"));
return new PersistedGrantDbContext(dbContextBuilder.Options, new OperationalStoreOptions() { ConfigureDbContext = b => b.UseSqlServer(connectionString) });
}
}
Compared to the answer of Ehsan Mirsaeedi I modified these:
I added the MigrationsAssembly:
dbContextBuilder.UseSqlServer(connectionString, b => b.MigrationsAssembly("DataSeeder"));
Where the "DataSeeder" is the name of my startup project for seeding and for migrations.
I added an options object with ConfigureDbContext property set to the connection string:
return new PersistedGrantDbContext(dbContextBuilder.Options, new OperationalStoreOptions() { ConfigureDbContext = b => b.UseSqlServer(connectionString) });
It is now usable like this:
'Add-Migration -Context PersistedGrantDbContext
At this point, when a migration has been created, one can create a service for this in a migration project having a method like this:
public async Task DoFullMigrationAsync()
{
using (var scope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var persistedGrantDbContextFactory = new PersistedGrantDbContextFactory();
PersistedGrantDbContext persistedGrantDbContext = persistedGrantDbContextFactory.CreateDbContext(null);
await persistedGrantDbContext.Database.MigrateAsync();
// Additional migrations
...
}
}
I hope I helped someone.
Cheers,
Tom
All you have to do, is modify your ConfigureServices like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ProjectDbContext>(item => item.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly("Project.Api")));
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ProjectDbContext>()
.AddDefaultTokenProviders();
}
By Default VS will use the Assembly of the project where the DbContext is stored. The above change, just tells VS to use the assembly of your API project.
You will still need to set your API project as the default startup project, by right clicking it in the solution explorer and selecting Set as Startup Project
Mine is a single .net core web project.
Had to ensure 1 thing to resolve this error. The following class must be present in the project.
public class SqlServerContextFactory : IDesignTimeDbContextFactory<SqlServerContext>
{
public SqlServerContext CreateDbContext(string[] args)
{
var currentEnv = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{ currentEnv ?? "Production"}.json", optional: true)
.Build();
var connectionString = configuration.GetConnectionString("MsSqlServerDb");
var optionsBuilder = new DbContextOptionsBuilder<SqlServerContext>();
//var migrationAssembly = typeof(SqlServerContext).Assembly.FullName;
var migrationAssembly = this.GetType().Assembly.FullName;
if (connectionString == null)
throw new InvalidOperationException("Set the EF_CONNECTIONSTRING environment variable to a valid SQL Server connection string. E.g. SET EF_CONNECTIONSTRING=Server=localhost;Database=Elsa;User=sa;Password=Secret_password123!;");
optionsBuilder.UseSqlServer(
connectionString,
x => x.MigrationsAssembly(migrationAssembly)
);
return new SqlServerContext(optionsBuilder.Options);
}
}
Note there the migration assembly name.
//var migrationAssembly = typeof(SqlServerContext).Assembly.FullName;
I have commented that out. That is the culprit in my case. What is needed is the following.
var migrationAssembly = this.GetType().Assembly.FullName;
With that in place the following two commands worked perfectly well.
Add-Migration -StartupProject MxWork.Elsa.WebSqLite -Context "SqlServerContext" InitialMigration
Add-Migration InitialMigration -o SqlServerMigrations -Context SqlServerContext
If you want a reference of such a project, take a look at this git hub link
There you should find a project attached with the name Elsa.Guides.Dashboard.WebApp50.zip. Download that see that web app.
I was facing similar issue, though answers seems straight forward somehow they didn't work.
My Answer is similar to #Ehsan Mirsaeedi, with small change in DbContextFactory class. Instead of Adding migration assembly name in Startup class of API, I have mentioned in DbContextFactory class which is part of Data project(class library).
public class DbContextFactory : IDesignTimeDbContextFactory<KuchidDbContext>
{
public KuchidDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var dbContextBuilder = new DbContextOptionsBuilder<KuchidDbContext>();
var connectionString = configuration.GetConnectionString("connectionString");
var migrationAssemblyName= configuration.GetConnectionString("migrationAssemblyName");
dbContextBuilder.UseSqlServer(connectionString, o => o.MigrationAssembly(migrationAssemblyName));
return new KuchidDbContext(dbContextBuilder.Options);
}
}
You would need 'Microsoft.Extensions.Configuration' and 'Microsoft.Extensions.Configuration.Json' for SetBasePath & AddJsonFile extensions to work.
Note: I feel this is just a work around. It should pickup the DbContextOptions from the startup class somehow it is not. I guess there is definitely some wiring issue.
If you have solution with few projects, where
API - startup here
EF - db context here
then to perform migration:
install Microsoft.EntityFrameworkCore.Tools for API
open Package Manager Console in Visual Studio
perform Add-Migration InitialCreate
notice that "DefaultProject: EF" should be selected in the console.
I have resolved it by adding below line in Startup.cs. Hope it will help you also. I have used Postgres you can use Sql Server instead of that
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
.AddSigningCredential(cert)
.AddCustomUserStore<IdentityServerConfigurationDbContext>()
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseNpgsql(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseNpgsql(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 30;
});
temporary rename docker proj file, solve on my issue
For all of you who have multiple startup projects.
Notice that you need to set your target project as startup project - Project.Api(form the question example) should be the startup project.
Hope that will help someone :)

Categories