Running Code First migrations from 1 project in another - c#

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.

Related

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.

Disabling migrations

I'm writing a web api attached to a database that I created using Model First.
Being new I was confusing Model First with Code First.
As it's my first EF/Web app I didn't realize that migration wasn't yet implemented by Model First and I followed the procedure to implement it. When it ran I received some errors, looked them up and only then realized I'd need to remove it again.
Following somebody's instructions I deleted the migrations folder, plus the _migrationHistory table from the database.
But now I receive the following exception whenever the _migrationhistory table is missing from the database.
protected void Application_Start()
{
// Call initializer
Database.SetInitializer(new PulseDbInitializer());
new ApplicationDbContext().Database.Initialize(true); <- error here
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
"An exception of type 'System.NotSupportedException' occurred in EntityFramework.dll but was not handled in user code
Additional information: Model compatibility cannot be checked because the database does not contain model metadata. Model compatibility can only be checked for databases created using Code First or Code First Migrations."
The following is a partial description of my initializer
public partial class PulseDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>, IPulseDbInit
//public partial class PulseDbInitializer :DropCreateDatabaseAlways<ApplicationDbContext>, IPulseDbInit
{
private ApplicationDbContext _context;
protected override void Seed(ApplicationDbContext context)
{
_context = context;
var pid = new PulseDbInitializionData(context);
pid.Init(this);
}
}// class PulseDbInitializer
It seems that the app is trying to use migrations (because it's creating the _migrationhistory table) even though I've removed the migrations subfolder.
Being new at this, I'm not sure where to go. I'm aware that I've created my initializer, see above, so am I missing something?
EDIT - Too long for a comment
Im not sure we're understanding each other.
Very simply, I don't know what's generating the _MigrationHistory table in my database as afaik if I remove the Migration subdirectory from my project and delete the database, then I thought that's all I need to do to drop migrations.
In my model first, the DropCreateDatabaseIfModelChanges is what I've always used for generating the database and is working as expected. It creates a new database, or recreates when the model changes.
Until I added the migration I didn't notice this _MigrationHistory table and I don't have a clue if the add-migration command added any code to my project that I need to delete or change (I really hate things happening and not being told exactly what was done in some kind of log. Please microsoft, for future reference)
Or am I totally wrong and the _MigrationHistory table was always there but I never noticed? It all seems to work, as long as I don't remove the table.
Your initializer is inheriting from DropCreateDatabaseIfModelChanges, which checks for model changes. Since you are not using code first you can't check for model changes in code (since your code doesn't define the model -that'd be code-first-... the model defines the code -that's model-first-)... so you need to implement your own IDatabaseInitializer which only checks (for example) that the database exists:
public partial class PulseDbInitializer : IDatabaseInitializer<ApplicationDbContext>, IPulseDbInit
{
public void InitializeDatabase(ApplicationDbContext context)
{
var exists = new DatabaseTableChecker().AnyModelTableExists(context.InternalContext);
if (exists == DatabaseExistenceState.Exists)
{
// maybe check if certain data exists and call the `Seed` method if
// it doesn't
return;
}
// Throw some error if it doesn't exist
}
protected override void Seed(ApplicationDbContext context)
{
_context = context;
var pid = new PulseDbInitializionData(context);
pid.Init(this);
}
}

ClickOnce Updates and the Entity Framework Code First

If I build an application and let code first figure out where to put the database and the user inserts data via the application, will that data get lost on a click once update ? If so, how do I approach this problem ?
Thanks
No there is No need to "lose" any data when using automatic migrations.
You migration config class should state no data loss allowed
You will need to build custom scripts/ or tweak the generated scripts when dealing with changes that result in data loss.
public override void MigrateDb() {
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MYDbContext, MYSECIALMigrationConfiguration>());
// Context = GetDefaultContext(); // check if a new context is really needed here
Context.Database.Initialize(true);
}
public class MYSPECIALMigrationConfiguration : MYBaseMigrationConfiguration<MYDbContext>{ }
public abstract class MYBaseMigrationConfiguration<TContext> : DbMigrationsConfiguration<TContext>
where TContext : DbContext{
protected MYBaseMigrationConfiguration() {
AutomaticMigrationsEnabled = true; // you can still chnage this later if you do so before triggering Update
AutomaticMigrationDataLossAllowed = true; // you can still chnage this later if you do so before triggering Update
}
how to approach migrations.
.. Thats actual a big question.
EF6 Migrations - new features and options
Great info on Migrations when working in teams.
This covers many scenarios you may recognise and thus help you understand what approach suits you best.

Entity Framework Code Based Migration not working after upgrade from EF 4.3 to EF 6

We are intensively used Entity Framework 4.3 with a code first .NET 4.0 based project within several sites, everything works smooth, fast and bugless, but to enable the last speed improvements made by the Entity Framework team we are now planning an upgrade to EF6 and .NET 4.5
After amending a couple of using clauses (due to the namespaces changes in the latest releases) everything seems to working properly if starting with an empty db, but trying to open an existing customer database will get an exception as soon as the Context() construction happens, apparently something is wrong with the code based migration.
My code based migration configuration is the following:
internal sealed class Configuration : DbMigrationsConfiguration<Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
}
With this application initialiser (program.cs)
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());
Database.DefaultConnectionFactory = new UniversalDbConnection();
Having UniversalDbConnection declared as
class UniversalDbConnection : IDbConnectionFactory
{
public DbConnection CreateConnection(string givenString)
{
switch (Preferences.DatabaseVendor)
{
case ConnectionPreset.PostgreSQL:
return new PgSqlConnection(Preferences.ConnectionString);
default:
SqlConnectionFactory factory = new SqlConnectionFactory(Preferences.ConnectionString);
return factory.CreateConnection(Preferences.ConnectionString);
}
}
}
Unfortunately an exception claiming:
The object 'FK_StockTests_FormulaSteps_Step_Id' is dependent on column 'Step_Id'.
ALTER TABLE DROP COLUMN Step_Id failed because one or more objects access this column arises

EF add-migration exception: could not load assembly

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
}
}
}

Categories