Add migration with different assembly
Asked Answered
K

23

119

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?

Kozhikode answered 1/8, 2016 at 18:29 Comment(3)
Did you read the docs? docs.efproject.net/en/latest/miscellaneous/cli/…Holbert
Yes, but i didnt get fix the error, i tryed diferent options of commands with --startup-project and --assebly but i didnt get nothingKozhikode
Don't use --assembly. That's an internal implementation detail that should have been hidden from the help message, but shows up anyways because of github.com/aspnet/EntityFramework/issues/5188Bashuk
B
152

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"))
Bashuk answered 2/8, 2016 at 16:9 Comment(10)
I cant execute a command from Project.Data because is a classlibrary and it doesnt allow execute command, i only can execute command in Project.ApiKozhikode
Thanks , it was very useful ;)Idiosyncrasy
Thanks, this helped after spending a lot of time on other solutions.Grader
Excellent explanation and examples, solved my problem immediately after wasting hours on other "solutions". Thank you!!!Iver
Do you have any explanation or link to one as to why this check was implemented? What was the reason behind forbidding migrations when the context is from an other assembly?Benz
Why might I choose one option over the other?Buckhound
The reason I separated my data from my project, is because the data does not rely on a specific sql server tech. It defeats the purpose of sepration, if I have to specify sql server in my data project. My data project must NOT have or rely on any connection string or database server. It does not compute to have UseSqlServer in both projects.Lothario
Changing the migration assembly will put migrations in that location, which is most likely not what you want to do. To keep migrations in your .Data project you will need to do the first one.Mesocratic
None of this options is working as of dotnet core sdk 6 and EF 7.0.8.Interlinear
Microsoft, please change this, I agree with Mathieu. Clean architecture and now I'm forced to reference a web.api project from a data project. "Reference" meaning get the connection string.Armored
N
111

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.

default project selection

Nogas answered 8/12, 2017 at 0:42 Comment(2)
Exactly. The easiest solution will be to set the default project to the project that contains the ApplicationDbContext file.Trudi
If your project includes a third shared component library, you cannot select it here...Vaasta
D
22

Add Migration With CLI Command:

dotnet ef migrations add NewMigration --project YourAssemblyName

Add Migration With PMC Command:

Add-Migration NewMigration -Project YourAssemblyName

Notice: you instead of YourAssemblyName, you should write 'csproj' library name that there is the Migration folder inside.

Notice: if you have multiple layers in your project, you can use the --startup-project switch to specify where is Startup class in which one of the layers.

(e.g. --startup-project YourWebApiCsprojName)

Link About CLI Commands

Link About PMC Commands

Doublereed answered 22/12, 2019 at 10:34 Comment(3)
not clear is where do you start from, and what is the "yourassemblyname" exactlyOswin
thanks, dear @serge. I added more descriptions.Doublereed
I solved it with this one, including the full path to the *.proj file instead of just the assembly name.Greaten
J
19

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.

Jenevajeni answered 7/5, 2018 at 4:59 Comment(4)
Can confirm this works. We had a need for somewhat more complicated setup. It even works if the DbContext itself is located in a NuGet package. You can reference the context in the IDesignTimeDbContextFactory<YourNugetContext> implementation and it works. The only downside is you cannot run the "migrations add" command because it would attempt to add the migrations to where the db context is. You can only run "ef database update" and "ef database drop" in this fashion.Limpet
You have used .AddJsonFile("appsettings.json"). What if appsettings.json file do not exist in your .Data Project?Gorgias
There a 3.0/1 version of this?Lathery
Microsoft put up some docs here learn.microsoft.com/en-us/ef/core/cli/…Review
F
13

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

Fir answered 5/8, 2016 at 18:54 Comment(3)
Thank you. I saw this solution but it seembed a little forced but so far it is the only solution I've seen it work. i will follow the link to get the newsKozhikode
Thanks a lot!! services.AddDbContext<EventbookingContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), x => x.MigrationsAssembly("ClasslibraryGoesHere")));Salient
don't use links, rather code, if link became invalid your answer is uselessOswin
C
11

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:

  1. Pick your default project
  2. 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
Crossness answered 21/9, 2016 at 16:3 Comment(1)
This answer helped me to find the right command. I have a .NET 7 class library named "Data" and a .NET 7 console application (Web API) named "Api" that is set as the startup project. The command I needed to use for creating the migration was: <dotnet ef migrations add InitialCreate -p Data -s Api> and for the database update: <dotnet ef database update -p Data -s Api>Caron
I
11

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
Inexpungible answered 28/12, 2021 at 20:1 Comment(1)
I did this and I got "Unable to create an object of type 'DataContext'. For the different patterns supported at design time..." how do I resolve this?Cesar
N
9

(ASP.NET Core 2+)

Had the same issue. Here is what I did:

  1. Reference the project that contains the DbContext (Project.A) from the project that will contain the migrations (Project.B).

  2. Move the existing migrations from Project.A to Project.B (If you don't have migrations - create them first)

  3. Configure the migrations assembly inside Project.A

options.UseSqlServer( connectionString, x => x.MigrationsAssembly("Project.B"));

Assuming your projects reside in the same parent folder:

  1. dotnet ef migrations add Init --p Project.B -c DbContext

The migrations now go to Project.B

Source: Microsoft

Need answered 25/3, 2019 at 21:9 Comment(2)
I was missing the -c DbContext. Thanks!Vito
Very nice source with example: github.com/dotnet/EntityFramework.Docs/tree/main/samples/core/…Vaasta
S
6

dotnet ef update-database --startup-project Web --project Data

  1. Web is my startup project
  2. Data is my the my class library
Shuster answered 26/2, 2021 at 12:56 Comment(0)
V
3

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.

Valaria answered 28/3, 2019 at 21:4 Comment(2)
Can you explain the context of this more to make it a better answer.Survival
This was helpful, thank you. I placed my Migrations folder and DbContext in a class library and then reference the class library in my asp.net web app project. The reason is that I am also using the class library in a console app that will be a web job deployed to the same App Service in Azure.Catarina
P
3

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

Polygraph answered 9/4, 2020 at 23:39 Comment(0)
H
3

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

Helve answered 4/6, 2020 at 18:55 Comment(0)
C
3

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

Concatenate answered 10/8, 2020 at 18:1 Comment(0)
V
3

If you have solution with few projects, where

  • API - startup here
  • EF - db context here

then to perform migration:

  1. install Microsoft.EntityFrameworkCore.Tools for API
  2. open Package Manager Console in Visual Studio
  3. perform Add-Migration InitialCreate

notice that "DefaultProject: EF" should be selected in the console.

Vagary answered 5/3, 2022 at 20:44 Comment(0)
T
2

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.

Thais answered 26/3, 2021 at 13:59 Comment(0)
T
1

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.

Tunesmith answered 26/2, 2019 at 1:39 Comment(0)
C
1

I had the same Issue and this was the best and simple solution.

I am using DotNetCore 7 and EFCore 7.0.10

My Folder Structure was as follows

|- Project.DataAccess
|--- ProjectDBContext.cs
|- Project.Presentation (Default Project)
|--- appsetting.json
|--- program.cs

The Problem Description: As we would run migration from the presentation layer of the project migrations folder needs to be created somewhere in our solution so we need to specify our program that Use the DbContext from DataAccess Layer but keep the migration folder within the presentation layer in the solution.

Solution:

    builder.Services.AddDbContext<ProjectDbContext>(options => options.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly("Project.Presentation")));

We need to specify the migration assembly project.

Chancellorship answered 27/8, 2023 at 1:54 Comment(1)
this seemed to work for me thanks. i changed the adddbcontextfactor in program.cs to builder.Services.AddDbContextFactory<Application_Db_Context>(options => options.UseSqlServer(connectionString, b => b.MigrationsAssembly("myprojectname")));Broderick
F
1

I am working on a project with ASP.NET CORE 8.0.0 and I am using EntityFrameworkCore and my database is Postgres. I have separate collections and my project structure is like this:

builder.Services.AddDbContext<ProductDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("ProjectName")
, b => b.MigrationsAssembly("ProjectName")));



PM> Update-Database -ProjectName 
Fetich answered 20/12, 2023 at 11:38 Comment(0)
L
0

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;
                });
Lentissimo answered 24/12, 2019 at 5:31 Comment(0)
S
0

temporary rename docker proj file, solve on my issue

temporary rename docker proj file, solve my issue

Synsepalous answered 17/1, 2023 at 14:51 Comment(0)
I
0

Try this:

dotnet ef migrations add <paramname> --startup-project ../<API/API.csproj> --project ../<Domain/Domain.csproj>

dotnet ef migrations remove --startup-project ../<API/API.csproj> --project ../<Domain/Domain.csproj>
Impetuous answered 18/10, 2023 at 11:23 Comment(0)
G
0

In my case, I had an Api project which is my startup, as well as a Data Access project, which contains the context.

I had to cd into the directory of the startup project, and then set the 'Default Project' in PMC to the one containing the context, then run dotnet ef migrations add InitialCreate

Gurl answered 19/3, 2024 at 23:21 Comment(0)
C
-1

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 :)

Cairistiona answered 12/7, 2018 at 11:35 Comment(1)
Dependency Injection requires a single startup project.Yellowweed

© 2022 - 2025 — McMap. All rights reserved.