Reputation: 297
I'm trying to use migrations in EF7 with entityframework.commands. But my DbContext is in different assembly with Start-up project (asp.net mvc is an start-up project and Core.Implementation has a DbContex).
dnx . ef migration add MyMigration -c MyContext
System.InvalidOperationException: No DbContext named 'MyContext' was found.
I've tried to use namespace to point to other assembly but it didn't work either. Is it possible at all? Or I just have to put my context in assembly where ef7 command is?
Upvotes: 18
Views: 9353
Reputation: 228
I have a workaround, but i'm using EFCore 1.0.0 so you must migrate. Below the codesnippets you need to make it work:
The idea is to have a central datacontext: MigrationsDataContext
to handle whole database migrations. So you can place this migration in a separated project dedicated only for migrating database. You only must make MigrationsDataContext
see others projects containing required datacontexts. This way, you can instantiate each other datacontext and call OnModelCreating
method manually passing current DbContext's ModelBuilder
as parameter.
public class MigrationsDataContext : DbContext
{
private readonly Dictionary<Type, DbContextOptions> dbCtxOpts;
private readonly IEnumerable<Type> dbCtxTypes = new[] {
typeof(CoreDataContext),
typeof(DbContext1),
typeof(DbContext2),
};
public MigrationsDataContext(DbContextOptions<MigrationsDataContext> options,
IEnumerable<DbContextOptions> dbCtxOpts)
:base(options)
{
this.dbCtxOpts = dbCtxOpts
.GroupBy(o => o.ContextType)
.ToDictionary(g => g.Key, g => g.First());
}
public MigrationsDataContext()
{ }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
foreach (var db in dbCtxTypes.Select(t => new { Type = t, Instance = Activator.CreateInstance(t, dbCtxOpts[t]) }))
{
var configurator = db.Type.GetMethod(nameof(OnModelCreating), BindingFlags.NonPublic | BindingFlags.Instance);
configurator.Invoke(db.Instance, new[] { builder });
}
}
}
In Order to Receive all DbContextOptions from constructor, you meed to register the whole bundle of DbContextOptions<T>
as DbContextOptions
this is achieved by calling the following method in ConfigureServices
in Startup.cs
:
public static IServiceCollection AddBundle<T>(this IServiceCollection services)
{
var baseType = typeof(T);
var bundle = services
.Where(d => baseType.GetTypeInfo().IsAssignableFrom(d.ServiceType))
.Select(d => d.ServiceType)
.ToList();
foreach (var ctxType in bundle)
services.AddScoped(baseType, s => s.GetService(ctxType));
return services;
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services
.AddEntityFrameworkSqlServer()
.AddDbContext<CoreDataContext>(ConfigEf)
.AddDbContext<DbContext1>(ConfigEf)
.AddDbContext<DbContext2>(ConfigEf)
.AddDbContext<MigrationsDataContext>(ConfigEf)
.AddBundle<DbContextOptions>()
.AddBundle<DbContext>();
...
That's all. Now you manage migrations this way:
move to you Migrations project, adjacent to MyWebApp project,
`$> dotnet ef --startup-project ../MyWebApp migrations add Initial --context Migrations.MigrationsDataContext`
Hope this help.
Upvotes: 0
Reputation: 19156
For ASP.NET Core 1.0 and EF Core 1.0
Suppose we want to move our context to Core1RtmEmptyTest.DataLayer
project.
First enable EF tools for this sub project by modifying its project.json
file:
{
// same as before
"tools": {
"Microsoft.EntityFrameworkCore.Tools": {
"version": "1.0.0-preview2-final",
"imports": [
"portable-net45+win8"
]
}
},
// same as before
}
Then run the command from the root of this new project (and not from the root of the main project)
D:\path\Core1RtmEmptyTest\src\Core1RtmEmptyTest.DataLayer>dotnet ef --startup-project ../Core1RtmEmptyTest/ migrations add InitialDatabase
Here we should specify the startup-project
too.
And finally to update the database
D:\path\Core1RtmEmptyTest\src\Core1RtmEmptyTest.DataLayer>dotnet ef --startup-project ../Core1RtmEmptyTest/ database update
Upvotes: 2
Reputation: 14306
EDIT
Since 1.0.0-rc1-final (maybe earlier) this workaroud is unnecessary
App.DataAccess/Startup.cs
(just delete it if you used the workaround below)App.Web
)-p
paramater) containing migrations:cd App.Web
dnx ef migrations add NewMigration -p App.DataAccess
If you have multiple database contexts you also have to specify which one to use (-c
parameter)
cd App.Web
dnx ef migrations add NewMigration -p App.DataAccess -c YourDbContext
END OF EDIT
I found out a workaround for that
Suppose you have 2 projects: App.Web
and App.DataAccess
You can add a very basic Startup class to your App.DataAccess
:
>App.Web
-->Startup.cs // your main startup
>App.DataAccess
-->path/to/ApplicationDbContext.cs
-->different/path/to/YourModels.cs
-->Startup.cs // add a simple startup class
-->project.json
The simple Startup class (App.DataAccess\Startup.cs
):
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Data.Entity;
using Microsoft.Dnx.Runtime;
using Microsoft.Framework.Configuration;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
namespace App.DataAccess
{
public class Startup
{
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
.AddJsonFile("../App.Web/config.json"); // path to your original configuration in Web project
Configuration = builder.Build();
}
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
}
}
}
Than modify your project.json in App.DataAccess (App.DataAccess/project.json
):
{
"version": "1.0.0-*",
"description": "App.DataAccess Class Library",
"authors": [ "Author" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"frameworks": {
"dnx451": { }
},
"dependencies": {
"EntityFramework.Commands": "7.0.0-beta7",
"Microsoft.Framework.Configuration.Json": "1.0.0-beta7",
},
"commands": {
"ef": "EntityFramework.Commands" // this line is important, it will give you access to 'dnx ef' in command line
}
}
Tha all you have to do is go to App.DataAccess
and use dnx ef
:
cd App.DataAccess
dnx ef migrations add NewMigration
dnx ef migrations remove
dnx ef database update
dnx ef ...
Upvotes: 23