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.
Related
I'm working on a .NET 4.5.2 project that creates an In-Memory db from an .mdf file every time we run the Integration tests.
The db works fairly well most of the time, but sometimes, when we make changes on the schema of a table, or add/delete a table altogether, we get problems like the following:
Message:
OneTimeSetUp: System.Data.SqlClient.SqlException : There is already an object named 'SomeTable' in the database
We have narrowed it down to the fact that EF migrations get all messed up when we want to update the test db schema after a merge (known issue). It seems like adding an empty migration everytime we have one of these issues fixes the problem, however, that workaround it's very tacky.
My question is: Is there a way to prevent this kind of issues? Maybe a cleaner workaround?
For some extra content, I'll describe how we're doing things (maybe we're messing up somehow):
We have a Db.mdf file that gets loaded by a LocalDbHelper before running any test:
With this code:
public static void PrepareEmptyDb()
{
var migrationConfiguration = new Configuration()
{
AutomaticMigrationsEnabled = true,
AutomaticMigrationDataLossAllowed = true
};
var migrator = new DbMigrator(migrationConfiguration);
migrator.Update();
}
This code is meant to be run in a OneTimeSetup at the IntegrationTestBase class.
And this is the code in the Configuration class (this is at our code first Persistance project, where all migrations reside):
internal sealed class Configuration : DbMigrationsConfiguration<Context.DbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(Context.DbContext context)
{
context.Settings.AddOrUpdate(
p => p.Name,
new Setting { Name = SettingNames.RoundingDirection, Value = "Up" },
new Setting { Name = SettingNames.RoundingValue, Value = "10" },
new Setting { Name = SettingNames.RateCacheLifetimeMinutes, Value = "30" });
}
}
Any help would be greatly appreciated, since this issue has really been annoying us for a while now.
EDIT: I've found this link that seem to suggest that, for older versions of .NET, this workaround was the recommended fix suggested by Microsoft themselves, but if anyone has a better way to fix it (or to automatize it) it would be greatly appreciated.
Adding an empty migration seems to be the only way to fix this. No workarounds or fixes other than upgrading to Core, sadly.
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);
}
}
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.
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
I created a DbContext like so :
public class myDB : DbContext
{
public DbSet<Party> Parties { get; set; }
public DbSet<Booking> Bookings { get; set; }
}
This generated my DataBase and the two tables above..Great
I then decided to add another DbSet into the mix & I got an error:
the model backing the 'Party' context has changed since the database was created
I'm aware of the fixes for this using modelbuilder.IncludeMetadataInDatabase = false; and Database.SetInitializer<ClubmansGuideDB>(null);
1) What's the correct way to add my new classes to the context and have them generated in the DataBase?
2) In my futile attempts to solve this myself I deleted the tables and tried to re-run the app with no success I then deleted the full database and re-run and it doesn't re-generate the DB. How can I start from scratch - is there some cache somewhere?
I believe you're looking for:
Database.SetInitializer<ClubmansGuideDB>(new DropCreateDatabaseIfModelChanges<ClubmansGuideDB>());
As of EF 4.1 and above.
Note that this assumes you have permission to even drop your database. I do this locally for ease of development but disable it in staging/production (I do a manual Schema Compare and push my changes).
By the way, for testing, if you need to force recreate the database, you can do:
using (var context = new ClubmansGuideDB()) {
context.Database.Initialize(force: true);
}
(using if you don't already have a reference to your DB)
You can let the Entity Framework recreate the whole database by using a Database Initializer or if you want to migrate data you can look at the Code First Migrations
The following would recreate your database when the model changes:
Database.SetInitializer<myDB>(new DropCreateDatabaseIfModelChanges<myDB>());
Try putting this in your Global.asax.cs Application_Start() method.
Database.SetInitializer<DatabaseContext>(null);
To reset the database from scratch on app run make a class like this
//Where myDB is your context
public class EntityBase: DropCreateDatabaseAlways<myDB>
{
}
Then in your Application_Start() method you can do this
Database.SetInitializer(new EntityBase());