I have Entity Framework (6.1.1) set up with migrations. I know I can run the following command in the Package Manager Console to reset the database to be completely empty:
Update-Database –TargetMigration: $InitialDatabase
But how can I do this from my code?
var configuration = new MyDbConfiguration();
configuration.TargetDatabase = new DbConnectionInfo(
"Server=MyServer;Database=MyDatabase;Trusted_Connection=True;",
"System.Data.SqlClient");
var migrator = new DbMigrator(configuration);
migrator.Update("201606030938116_InitialDatabase");
if you do not know your migration Id then you can just do:
migrator.GetDatabaseMigrations().First();
or:
migrator.GetLocalMigrations().First();
In your DbConfiguration you have to allow the auto dropping:
public class MyDbConfiguration: DbMigrationsConfiguration<MyDbContext>
{
public MyDbConfiguration()
{
this.AutomaticMigrationsEnabled = true;
this.AutomaticMigrationDataLossAllowed = true;
}
}
Update from #GTHvidsten:
Instead of getting the available migrations, you have to use this command: migrator.Update(DbMigrator.InitialDatabase);. But you also have to set the ContextKey property in MyDbConfiguration to match the one used in the Configuration object created by Package Manager. With both of these my database becomes empty.
Related
I have written a program that is running and I cannot delete the database. Now I want to add a series of new properties to the database tables. I know that I should use migration, but the point is that I have written the connection string manually with ConfigurationManager class and its functions, not in app.config. But now I have encountered a problem to add a new migration and update the database.
My question here is how to do the new migration and database update as code functions in C# instead of using package manager console and its commands i.e add-migration & update-database.
After many searches, I found a series of solutions like this link https://stackoverflow.com/questions/20216147/entity-framework-change-connection-at-runtime?answertab=modifieddesc#tab-top and using IDBConnectionIntercaptor, but in The test environment creates this database face with the error Cannot attach the file '***' as database '+++' .
my connection function is :
`
public static bool Connect(bool force = false)
{
bool result = false;
var selectedDb = new EventsDataContext();
selectedDb.ChangeDatabase
(
initialCatalog: Maincsb.InitialCatalog,
userId: "",
password: "",
dataSource: Maincsb.DataSource
);
EventsDataContext odb = null;
try
{
ConnectionInterceptor connectionInterceptor = new ConnectionInterceptor
{
connectionString = ConnectionManager.Maincsb.ConnectionString
};
DbInterception.Add(connectionInterceptor);
DbInterception.Add(new ModifyYeKeIntercpetor());
odb = new EventsDataContext();
odb.Database.Initialize(force);
Seeder(Maincsb.ConnectionString);
result = true;
}
catch (Exception e)
{
ErrorMessage.Show(e);
result = false;
}
finally
{
odb.Dispose();
odb = null;
}
return result;
}
`
And if the database has already been created, it will face the error that the database has changed and new features will not be added to the database.
I'm refactoring a project and want to move all the EF entities and the code-first migrations to a new project. I renamed the ContextKey in the _Migrations table to the new namespace. When running an Add-Migration, no new changes are detected (Up() and Down() are empty).
But when I remove the localdb, the db isn't re-created (it did before the move). Apparently only migrations created after the move are run (but it shouldn't).
How can I make sure all migrations (also the ones before the move) are run when creating a new db?
--edit--
Never mind :(
I dragged and dropped the existing migrations to the new project and renamed the namespaces in the migration.cs files, but forgot the code behind migration.Designer.cs
You can update all the ContextKey column values in the dbo._MigrationHistory table to match the new namespace and that's all.
For me i was moving all the code first models from ASP.NET MVC app to external Class library to share with other projects.
Below steps may help
check the dbo._MigrationHistory and you can see all records have
similar values which match the exact class of Configuration class
MyApp.Migrations.Configuration
2.(test step) run Update-Database from Package Manager Console with new class library selected and you will see for example below error
There is already an object named 'AspNetRoles' in the database.
update all the records in the ContextKey column of _MigrationHistory table to match the new namespace
MyApp.Domain.Migrations.Configuration
The reference table [__MigrationHistory] contains a ContextKey column. Unless otherwise valued, it maintains the value of the membership of DbContext namespace.
You can set a class that derives from dbMigrationsConfiguration and set the ContextKey value in the constructor.
public sealed class Configuration : DbMigrationsConfiguration<Your.Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
ContextKey = "PreviousValue";
}
protected override void Seed(Your.Context context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
I am using Fluent Migrator for keeping the database updated. The Up and Down functions works perfectly. Next step is that I want views to be created. These I would like to run from an .SQL file which i have. I want this to be run after all migrations has been run, everytime.
What i have currently is:
var blah = new MigrationConventions();
var maintenanceLoader = new MaintenanceLoader(_migrate, blah);
maintenanceLoader.ApplyMaintenance(MigrationStage.AfterAll);
and a class
[Maintenance(MigrationStage.AfterAll)]
public class ViewMaintenance
{
public ViewMaintenance() {
var blah = 123;
}
}
This is not fired because in maintenanceLoader there are 0 elements that it can find. I am inserting the _migrate, which is defined like this:
var runnerContext = new RunnerContext(new TextWriterAnnouncer(UpdateText));
_migrate = new MigrationRunner(
Assembly.GetExecutingAssembly(),
runnerContext,
new SqlServerProcessor(
new SqlConnection(connectionString),
new SqlServer2012Generator(),
new TextWriterAnnouncer(UpdateText),
new ProcessorOptions(),
new SqlServerDbFactory()));
Why can't the Assembly.GetExecutingAssembly() be scanned, and find the [Maintenance(MigrationStage.AfterAll)] be found?
I would also like for the ViewMaintenance class to be able to run the Execute.Sql( that the Migration classes has.
I downloaded the source code and figured it out.
In the class I want the maintenance to be run, I need to inherit from the : Migration class, just like with migrations (duh..). It will then have access to everything it has access to in migrations, including Execute.Sql(.
When it is inherited from, the Reflection in Fluent Migrator will search for it, find it, and use the attribute that is set to run it after all migrations is run.
This part is not needed:
var blah = new MigrationConventions();
var maintenanceLoader = new MaintenanceLoader(_migrate, blah);
maintenanceLoader.ApplyMaintenance(MigrationStage.AfterAll);
Neat :)
I had created my project and configured the user entities.
I had enabled migrations in my project. To test this I simple added a user and then logged into the ASP NET application through the browser. It all worked great.
I added a couple more entities and their corresponding DbSet<T>. I created a controller to manage these entities.
I went to Update-Database using this seed method:
var manager = new UserManager<ChevieUser>(new UserStore<ChevieUser>(new DefaultContext()));
var people = new List<ChevieUser>
{
new ChevieUser { FirstName = "blah", LastName = "blah", UserName="cliningt", Email="blah.blah#blah.co.uk" }
};
people.ForEach(x => manager.Create(x, "password"));
context.SaveChanges();
var projects = new List<Project>
{
new Project { Name= "MLounge", ProposedDuration = DateTime.Now.AddDays(14), Users = new List<ChevieUser>(){context.Users.FirstOrDefault()} }
};
projects.ForEach(x => context.Projects.AddOrUpdate(p => p.Name, x));
context.SaveChanges();
//var targets = new List<Target>
//{
// new Target {Name="Initial Meeting", CompletionDate = DateTime.Now.AddDays(1), ProjectId = context.Projects.FirstOrDefault().Id},
// new Target {Name="Pricing", CompletionDate = DateTime.Now.AddDays(2), ProjectId = context.Projects.FirstOrDefault().Id},
// new Target {Name="Layout", CompletionDate = DateTime.Now.AddDays(3), ProjectId = context.Projects.FirstOrDefault().Id}
//};
//targets.ForEach(x => context.Targets.AddOrUpdate(p => p.Name, x));
//context.SaveChanges();
It created a project, however, the project had an Id of a empty Guid. Even though I am using the same code to for the Id property as I have done with every other project.
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
The reason why the targets code is commented out is that it can't add three targets with the same Id because the above code isn't working properly.
So I thought I would update my MVC project with the latest nuget packages. This was successful. However, when I came to Update-Database there was an error that migrations was not enabled in my project!
Then after trying to Enable-Migrations it came back with an error that there is no context!
From everything working perfectly fine, to the whole project messing up like this is so bizarre. Out shot of it is, migrations seems to have magically disable itself, but even when it was working, the DatabaseGenerated(DatabaseGeneratedOption.Identity) wasn't working properly.
It sounds like EF is looking in the wrong project.
Make sure the "Default Project" dropdown in your Package Manager Console window is set to the project that contains your DbContext class. This tells the package manager console which project to execute commands against.
I'm using Entity Framework Code First migrations, and I have a scenario where I want to run a suite of integration tests. Each time the tests run, I want to re-create the database, and apply all migrations
The steps should be:
Drop the existing test database (if any)
Create a new test database, and apply all migrations
Seed data
This is an existing project that I've added migrations to, and I used the Enable-Migrations command to create an "InitialCreate" migration that contains code to add all the tables to my database.
The code in my custom IDatabaseInitializer is as follows:
public void InitializeDatabase(MyContext context)
{
//delete any existing database, and re-create
context.Database.Delete();
context.Database.Create();
//apply all migrations
var dbMigrator = new DbMigrator(new Configuration());
dbMigrator.Update();
//seed with data
this.Seed(context);
context.SaveChanges();
}
The Up method of my InitialCreate migration is not getting called by this code, which is not what I expected. Instead, all of the tables are created when the Database.Create() method is called. I need the InitialCreate migration to run because I have additional code in there to create stored procedures.
So my questions is, how do I programmatically create a new database and run all migrations (including the InitialCreate migration)?
The following code has allowed me to meet the needs of my integration testing scenario outlined in the question, but surely there's a better way?
public void InitializeDatabase(MyContext context)
{
//delete any existing database, and re-create
context.Database.Delete();
var newDbConnString = context.Database.Connection.ConnectionString;
var connStringBuilder = new SqlConnectionStringBuilder(newDbConnString);
var newDbName = connStringBuilder.InitialCatalog;
connStringBuilder.InitialCatalog = "master";
//create the new DB
using(var sqlConn = new SqlConnection(connStringBuilder.ToString()))
{
using (var createDbCmd = sqlConn.CreateCommand())
{
createDbCmd.CommandText = "CREATE DATABASE " + newDbName;
sqlConn.Open();
createDbCmd.ExecuteNonQuery();
}
}
//wait up to 30s for the new DB to be fully created
//this takes about 4s on my desktop
var attempts = 0;
var dbOnline = false;
while (attempts < 30 && !dbOnline)
{
if (IsDatabaseOnline(newDbConnString))
{
dbOnline = true;
}
else
{
attempts++;
Thread.Sleep(1000);
}
}
if (!dbOnline)
throw new ApplicationException(string.Format("Waited too long for the newly created database \"{0}\" to come online", newDbName));
//apply all migrations
var dbMigrator = new DbMigrator(new Configuration());
dbMigrator.Update();
//seed with data
this.Seed(context);
context.SaveChanges();
}
private bool IsDatabaseOnline(string connString)
{
try
{
using (var sqlConn = new SqlConnection(connString))
{
sqlConn.Open();
return sqlConn.State == ConnectionState.Open;
}
}
catch (SqlException)
{
return false;
}
}
Just remove the "create database" step and use the migrations on their own. I put a sample project on GitHub, but the important bit is
Configuration config = new Configuration();
DbMigrator migrator = new DbMigrator(config);
foreach (string s in migrator.GetPendingMigrations())
{
migrator.Update(s);
}