I have only 2 days experience with EF Migrations, so please be kind...
I have a fairly large existing WPF WCF MVVM EF 4.1 solution which needs to be migrated to EF 4.3.1 and begin using Migrations. The solution's "Services" project contains four DbContext's, each in its own namespace, and each associated with its own database.
Before I began modifying the large solution, I did some experimentation with a small sample console app with only a single project and two DbContexts, mostly based on the example provided by "e10"
(EF 4.3 Auto-Migrations with multiple DbContexts in one database). The sample app works well, and I can do add-migration and update-database separately for the two contexts (by specifying the -configuration parameter).
But when I tried to replicate the same approach with the "real" (large) solution - with four DbContexts - I ran into a problem: when I invoke add-migration in PMC and specify any of the four configuration names, add-migrations gets an exception saying it can't load the Services assembly.
Here's what I did with the large solution:
1) Added the EF 4.3.1 NuGet package to my Core, Services and UI projects (this last bit may be important).
2) created a Migrations folder in my Services project and manually created a Configuration.cs file containing four classes which inherit from DbMigrationsConfiguration<type>, where type is App, Catalog, PortfolioManagement or Scheduler. (code is below)
3) added a property to one of the model classes associated with the App DbContext, so there would be something to migrate
4) from the PMC, invoked add-migration:
PM> add-migration App_AddNewProperty -config App
Note that I didn't do "Enable-Migrations" because, as e10 said in his post:
" You dont need to enable migration since you already did with the ... classes above" (referring to the classes in Configurations.cs).
5) add-migration gets exception: Could not load file or assembly 'MyApp.Services' or one of its dependencies
I enabled binding-failure logging, and the failure log shows that it's trying to locate the Services assembly in the UI's bin/debug folder, rather than in the Services project).
And it fails the same way even if I have the Default Project in the PMC set to the Services project (Default Project defaults to the UI project).
I suspect this is caused by the UI not having a reference to the Services assembly (it has a WCF Service Reference, but not an assembly reference). But if this is the problem, how do I force PMC to not start at the UI project? Or can I "unassociate the UI project from the EF package"?
Thanks!
DadCat
Configurations.cs:
namespace MyApp.Services.Migrations
{
internal sealed class App : DbMigrationsConfiguration<Geophysical.Skimmer.Services.App.Repository.ModelContainer>
{
public App()
{
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "MyApp.Services.App.Repository.ModelContainer";
}
protected override void Seed(MyApp.Services.App.Repository.ModelContainer context)
{
... no code here
}
}
internal sealed class Catalog : DbMigrationsConfiguration<Geophysical.Skimmer.Services.Catalog.Repository.ModelContainer>
{
public Catalog()
{
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "MyApp.Catalog.Repository.ModelContainer";
}
protected override void Seed(MyApp.Services.Catalog.Repository.ModelContainer context)
{
... no code here
}
}
internal sealed class PortfolioManagement : DbMigrationsConfiguration<Geophysical.Skimmer.Services.PortfolioManagement.Repository.ModelContainer>
{
public PortfolioManagement()
{
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "MyApp.PortfolioManagement.Repository.ModelContainer";
}
protected override void Seed(MyApp.Services.PortfolioManagement.Repository.ModelContainer context)
{
... no code here
}
}
internal sealed class Scheduler : DbMigrationsConfiguration<Geophysical.Skimmer.Services.Scheduler.Repository.ModelContainer>
{
public Scheduler()
{
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "MyApp.Services.Scheduler.Repository.ModelContainer";
}
protected override void Seed(MyApp.Services.Scheduler.Repository.ModelContainer context)
{
... no code here
}
}
}
Related
I Am wondering what i am doing wrong because EnsureCreated() works as i want but Migrate() doesn't work because no migrations are ever found.
Here is some code:
public SqliteContext(string FileName)
{
this.FileName = FileName;
Database.EnsureCreated();
}
Works
But when i do the same but with:
public SqliteContext(string FileName)
{
this.FileName = FileName;
Database.Migrate();
}
It doesn't. So i checked and it seems no migrations are ever found. But i have my migrations in the same project as my SqliteContext/dbContext. (In map Migrations) So i assume i don't have to specify a migration assembly but i did that as well but it still didn't work.
For some extra information:
My migrations are generated by Microsoft.EntityFrameworkCore.Tools.
I am using:
Microsoft.EntityFrameworkCore.Tools 3.1.12
Microsoft.EntityFrameworkCore.Sqlite 3.1.12
Xamarin.Forms 5.0.0.2012
Xamarin.Essentials 1.6.1
FileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "narco.db"
Seems i found the solution.
public class DbMigrationContext : SqliteContext
{
public DbContext() : base(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "narco.db"))
{
}
}
I added this class to my Core project so if there were any changes in my context i use in my project they auto reflect in the context i use to create my migrations.
[DbContext(typeof(DbMigrationContext))]
Is put on top of my migration so its not found. So when i changed it to
[DbContext(typeof(SqliteContext))]
It now works as expected. So i guess from now on i will just disconnect the reference to my project with my SqliteContext and just copy it over to avoid issues like this.
Hopefully whoever has this issue will find this useful.
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.
In Entity Framework by using Enable-Migrations a Migrations folder is created containing a Configuration inherited from DbMigrationsConfiguration like this:
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
...
}
All the created migrations which are created using Add-Migration are placed in the Migrations folder too.
public partial class Init: DbMigration
{
public override void Up()
{
...
}
public override void Down()
{
...
}
}
I didn't find any code that relates these two together ( for example having a configuration property in migrations). The only relation I found is that both are placed in same folder. If I have more than 1 DbContext and consequently more than 1 Configuration, I'm wondering how these DbMigrations are distinguished?
Question: How DbMigration classes are related to a Configuration?
They are related by convention. By default, it will store the migrations in a root folder called Migrations. You can override this in the constructor of the config (https://msdn.microsoft.com/en-us/library/system.data.entity.migrations.dbmigrationsconfiguration(v=vs.113).aspx) or when you enable-migrations:
public Configuration()
{
AutomaticMigrationsEnabled = true;
MigrationsDirectory = #"Migrations\Context1";
}
For multiple contexts, create a different config and folder for each by using -ContextTypeName ProjectName.Models.Context2 -MigrationsDirectory:Migrations\Context2. Here is a walkthrough: http://www.dotnettricks.com/learn/entityframework/entity-framework-6-code-first-migrations-with-multiple-data-contexts
When you run the update-database command, the database operations in the up() method in the latest DbMigration derived classes is performed. If that is successful, the commands in the Configuration class are executed. One of those methods is the seed() method where you can optionally add code to plug values into your tables after a migration. When you specify a target migration (presumably earlier than the latest), the migration works through the chain of down() methods in the migration classes to get to the version you wanted.
I have a bit of an unusual problem that I will try to explain as best I can.
I have 1 WebAPI project, which makes use of Entity Framework migrations. This is all working perfectly well with the below setup. The webapi project has a reference to my Repositories project, which houses MyContext, all my migration files and all the migration configuration
Global.asax.cs in WebAPI project
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Repositories.Migrations.Configuration>());
Configuration.cs in Repositories project
public sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = true;
}
public Configuration(DbConnectionInfo _info)
{
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = true;
TargetDatabase = _info;
}
protected override void Seed(MyContext _context)
{
MyContextSeeder.Seed(_context, logger);
base.Seed(_context);
}
}
I also have a method which can create a new database with a new context depending on the connection string passed in. This works perfectly well from within my WebAPI project, and can call it on the fly to create databases and migrate them to latest.
public static MyContext UpdateDatabase(string _connectionString)
{
try
{
var context = new MContext(_connectionString);
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());
DbMigrator migrator = new DbMigrator(new Configuration(new DbConnectionInfo(context.Database.Connection.ConnectionString, "System.Data.SqlClient")));
migrator.Update();
return context;
}
catch (Exception err)
{
logger.Error("Error updating database", err);
throw;
}
}
The task I am now trying to achieve is to run these migrations from another, totally separate application.
So far I have managed to get the migrations running, and the database is being created and migrations run (but it should be noted that the seed does not appear to be getting called), but it is reporting an error of:
Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration. Set DbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enable automatic migration.
Within this separate application, which is a windows form app, all I have essentially done is include references to my repositories project (and other related projects required e.g. Models) and called the UpdateDatabase method detailed above.
So the code in the forms app can be watered down to:
private void btnSubmit_Click(object sender, EventArgs e)
{
MyContext.UpdateDatabase(newConnectionString);
}
Has anyone had any experience in developing something similar before, or am I going about this the wrong way? It seems simple enough what I am trying to achieve, but just not quite there.
I've creating an Asp.Net MVC 5 website. I will need to add customized fields in ApplicationUser and associate (add foreign keys) it with other models. I think I should just use one context type. However, the code scaffold already generate the following ApplicationDbContext class. Can I just put all my public DbSet<...> ... { get; set; } in the class? Or is there a better pattern?
namespace MyApp.Models
{
// You can add profile data for the user by adding more properties to your User class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : User
{
}
public class ApplicationDbContext : IdentityDbContextWithCustomUser<ApplicationUser>
{
}
}
There is an excellent video explaining that matter.
Just check the free ASP.NET MVC 5 Fundamentals course by Scott Allen.
The exact answer is here (starts at 3:30).
I would advise keeping them separate. There is really no reason to couple two parts of the system together. To add another DbContext just add a file to models called YourContext.cs.
public class YourContext: DbContext
{
public YourContext() : base("name=YourContext")
{
}
// Add a DbSet for each one of your Entities
public DbSet<Room> Rooms { get; set; }
public DbSet<Meal> Meals { get; set; }
}
Then in the root web.config
<add name="YourContext" connectionString="Data Source=(localdb)\v11.0; Initial Catalog=YourContext; Integrated Security=True"" providerName="System.Data.SqlClient" />
When you run enable-migrations in the package manager console you will be asked which dbcontext you want to migrate. Pick YourContext.
EDIT: No need to add repos / unit of work the Entity Framework does this for you.
Please note: This was written as in beta2 where ALLOT has changed! Hopefully most of it will stick but there are no guarantees until RC.
DO NOT USE NuGET package manger (until RC) as it does NOT pick-up on the .NET 5 packages required and it will install EF 6 and mess up your project. (We are after EF 7)
In the projects.json you need to have the following dependencies. (or beta2 when its out, or the latest on RC)
"EntityFramework": "7.0.0-beta1",
"EntityFramework.Relational": "7.0.0-beta1",
"EntityFramework.Commands": "7.0.0-beta1",
"EntityFramework.Migrations": "7.0.0-beta1",
"EntityFramework.SqlServer": "7.0.0-beta1"
Add a new folder DBContexts and add a c sharp file with your new context stuff.
public class BlaBlaDB : DbContext
{
public DbSet<Models.MyOtherModel> MyOtherModels { get; set; }
protected override void OnConfiguring(DbContextOptions options)
{
options.UseSqlServer();
}
}
and in your config.json make sure to add a connection string, the exact same as the IdentityDB just with you new name. Then in startup.json register your databse.
services.AddEntityFramework(Configuration)
.AddSqlServer()
.AddDbContext<DataContexts.IdentityDB>()
.AddDbContext<DataContexts.BlaBlaDB>();
This has to compile because k will run this project and use the startup to inject your context and then execute everything you need. As of now VS2015 Beta does NOT have all/ or they do not work, the command for EF.
You need to go and install KRE for Windows.
Open command prompt, browse to your project directory, enter the solution and enter the following commands.
k ef context list
k ef migration add -c (context.from.above) initial
k ef migration apply -c (context.from.above)
You now have multi context migration. Just keep on adding context and repeat this as you need it. I used this on localdb, as the default project set-up so that it can work stand alone in any environment, like Linux.
Please Note: You still need to create a Service, containing the Interface and Implementation and then register that in startup.json More information here