Enable Migrations with Context in Separate Assembl

2020-01-27 00:40发布

I have one project that I want to run my update-database against but I have my Models and Context in a separate project.

If I run enable-migrations I get this error: No context type was found in the assembly 'MyProject'.

This is presumably because my Context is in MyProject.MVC.

If I run enable-migrations against MyProject.MVC I have to add an app config file. I don't want to do that as I want to use the code across many projects.

So can I run enable-migrations against MyProject and somehow tell it to look in MyProject.MVC for the Context?

4条回答
Summer. ? 凉城
2楼-- · 2020-01-27 00:50

I had the same problem, and I'm using EntityFramework 4.3.1. It seems that EF6 solves this issue (according to the answer by @SOfanatic) but I didn't want to upgrade to EF6 because of some breaking changes (in the DataAnnotations, for example).

So, what I did to solve this (and what I learned in the process):

  1. Create a new solution (empty project) and add the project where you have the model you want to enable migrations for (in your case MyProject.MVC). You may need to install the required NuGet packages for it before you are able to add the existing project.

  2. Add a config file with a connection string (don't worry, this is only to trick the migrations engine). Copy your existing database to the model project output folder (should be MVC\bin\Debug in your case). Make sure the connection string in the config file points to that database:

    <connectionStrings>
        <add name="MyDB" providerName="System.Data.SqlServerCe.4.0" connectionString="DataSource=|DataDirectory|\MyDB.sdf"/>
      </connectionStrings>
    
  3. Since you are in a new solution, set your model project as a startup project (you can remove the default project).

  4. Run the enable-migrations command in the package manager console. It should create a Migrations folder with two files: a Configuration.cs and a timestamped InitialCreate.cs file. It's nice to have the InitialCreate, that's why you put your existing database in the model project's output folder (but this is optional).

  5. Reload your original solution so that these changes are updated.

What I learned (as far as I understand):

  1. The migrations engine needs something that looks like a valid connection to work. I was creating my connection string in code (in another project) and that didn't work. I just gave the Migrations engine a "valid" connection string to make it work.
  2. Put your database where the migrations engine can find it (aka model project's output folder) so it creates a starting point for migrations. This starting point is basically your database schema written in the migrations API.
  3. You can restore everything to your previous state once the migrations are set in place, and it works ok.
  4. Everytime you want to manually add a migration, you must "trick" the migrations engine again, just like the first time. I haven't tried with automatic migrations, by I guess this approach works as well.

By th way, I am using a SQL Server CE 4.0 database, so some things about the connection string have a little twist compared to a standard SQL Server DB or LocalDB. Besides that, everything's the same.

Hope this is helpful and gives you some insight. Please comment if you know more about the way this migrations work.

查看更多
啃猪蹄的小仙女
3楼-- · 2020-01-27 00:51

This will only work in EF 6, but there was a release that added the -ContextProjectName parameter to the -enable-migrations command. By using this command you could do the following:

enable-migrations -ContextProjectName MyProject.MVC -StartUpProjectName MyProject.MVC 
-ContextTypeName MyProject.MVC.MyContextFolder.MyContextName -ProjectName MyProject

This will add migrations to your MyProject project using the context in the MyProject.MVC. You need to make sure that the project with the Migrations has a reference to the project with your Context, i.e., MyProject references MyProject.MVC

查看更多
够拽才男人
4楼-- · 2020-01-27 01:09

You may only run "Enable-Migrations" in the project containing the Database Context class.

Your solution will contain 2 projects:

1) MyProject.Models
    |- Migrations
        |- 201401061557314_InitialCreate.cs
        |- Configuration.cs
    |- MyContext.cs
    |- App.config (no connection string)


App.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
</configuration>


2) MyProject.MVC
        |- Filters
            |- InitializeSimpleMembershipAttribute.cs


InitializeSimpleMembershipAttribute.cs

namespace MyProject.MVC.Filters
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
    {
        private static SimpleMembershipInitializer _initializer;
        private static object _initializerLock = new object();
        private static bool _isInitialized;

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // Ensure ASP.NET Simple Membership is initialized only once per app start
            LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
        }

        private class SimpleMembershipInitializer
        {
            public SimpleMembershipInitializer()
            {
                try
                {
                    Database.SetInitializer<MyContext>(new MigrateDatabaseToLatestVersion<MyContext, MyProject.Model.Migrations.Configuration>());

                    using (var context = new MyContext())
                    {
                        context.Database.Initialize(force: true);
                        if (!context.Database.Exists())
                        {
                            // Create the SimpleMembership database without Entity Framework migration schema
                            ((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
                        }
                    }

                    WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
                }
            }
        }
    }
}

Set MyProject.MVC as startup project

In package manager, select project: MyProject.Models

Then run "Enable-Migrations" to create "Migrations" folder in MyProject.Models

Followed by "Update-Database" -> migrations will use connection string in Web.config from startup project to perform migration

查看更多
Fickle 薄情
5楼-- · 2020-01-27 01:10

Here is a workaround:

Add a class into MyProject(the project for migrations). Make this class inherit the dbcontext(the one in MyProject.MVC).

Then run EF migration commands on MyProject.

查看更多
登录 后发表回答