I am trying to run migrations from code. I created my models and enabled-migrations and then added initial migration, this initial migration contains all the create tables etc
public partial class Initial : DbMigration
{
public override void Up()
{
CreateTable(..........
}
public override void Down()
{
DropTable(...........
}
}
I then tried the Update-Database command from the visual studio which works fine, creates the database and runs the initial migration.
I then delete the database from the Sql Studio. Then I run the console app that calls the Migration Manager class
// MigrationManager class
public static bool PerformMigration(string migrationId)
{
bool success = false;
try
{
DbMigrationsConfiguration<MyDbContext> config = new Configuration();
....
DbMigrator migrator = new DbMigrator(config);
migrator.Configuration.AutomaticMigrationsEnabled = false;
if (string.IsNullOrEmpty(migrationId))
migrator.Update(); --> fails saying pending migration
else
migrator.Update(migrationId);
success = true;
}
catch (Exception e)
{
success = false;
LastException = e.Message;
}
return success;
}
The Update() fails with the following error:
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.
//Configuration.cs
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(WorkflowConfigurationDbContext context)
{
SeedData(context);
}
private void SeedData(){...}
}
public class MyDbContext : DbContext
{
public MyDbContext()
{
}
public DbSet....
}
When I step through the Update() call, it goes into Configuration() constructor and MyDbContext() constructor but fails after that, it seems like its not trying the Initial migration at all.
Make sure the database initialization strategy is correct in your EF context's constructor, like:
public partial class YourContext: DbContext
{
static YourContext()
{
Database.SetInitializer<YourContext>(new CreateDatabaseIfNotExists<YourContext>());
}
}
This is executed the first time the database is accessed.
EDIT: A second issue may be about security: does the user that is executing the migration have the required permissions?
Found the issue, it was incorrect namespace.
DbMigrationsConfiguration<MyDbContext> config = new Configuration();
config.MigrationsNamespace = "Correct Namespace";
Related
I'm trying to create an schema independent model with EntityFramework Codefirst and an Oracle database but EF uses as defaults for migrations dbo as schema.
I overridden OnModelCreating method on my DBContext to solve this and use the user in the connectionString instead
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema(string.Empty);
}
The problem is that __MigrationHistory ignores this default schema and I get this error when running first migration:
ORA-01918: User 'dbo' does not exist
Tried this msdn entry to customize the schema for this table.
CustomHistoryContext:
public class CustomHistoryContext : HistoryContext
{
public CustomHistoryContext(DbConnection dbConnection, string defaultSchema)
: base(dbConnection, defaultSchema) {}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema(String.Empty);
}
}
And DBConfiguration:
public sealed class Configuration :
DbMigrationsConfiguration<Model.MyDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
SetHistoryContextFactory("Oracle.ManagedDataAccess.Client",
(connection, defaultSchema) => new CustomHistoryContext(connection, defaultSchema));
}
protected override void Seed(Model.Model1 context)
{
}
}
And is working fine for the first migration. But when I modify my entity model and try to reflect this change with add-migration command I get the following error:
Unable to generate an explicit migration because the following
explicit migrations are pending: [201706281804589_initial,
201706281810218_pp2]. Apply the pending explicit migrations before
attempting to generate a new explicit migration.
Looks like EF gets lost and can't find migrations history at this point.
When I comment the SetHistoryContextFactory instruction in Configuration it works for subsequent add-migration commands but this workaround isn't enough for scenarios when I want to run all migrations from scratch like deploying.
Does anyone knows if I'm in the good way to accomplish this or if there is a better workaround for this?
Go to Migrations -> Configuration.cs and below mentioned code. This fix worked for me!
class Configuration : DbMigrationsConfiguration<CodeFirstOracleProject.Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
var historyContextFactory = GetHistoryContextFactory("Oracle.ManagedDataAccess.Client");
SetHistoryContextFactory("Oracle.ManagedDataAccess.Client",
(dbc, schema) => historyContextFactory.Invoke(dbc, "YourSchemaName"));
}
}
Try runnig Enable-Migrations -force again. Then run Add-Migration SomeDescription –IgnoreChanges. After that, run "Update-Database - Verbose". This worked to me
I successfully tried the following inside the Configuration : DbMigrationsConfiguration class for Oracle, in order to change the history schema to "Test":
var historyContextFactory = GetHistoryContextFactory("Oracle.ManagedDataAccess.Client");
SetHistoryContextFactory("Oracle.ManagedDataAccess.Client",
(dbc, schema) => historyContextFactory.Invoke(dbc, "Test"));
So basically, instead of trying to register a custom history context with unchanged default schema, I tried to register the default history context with changed default schema.
The result: when I run Update-Database -Script, the resulting script contains the new schema for creation of the __MigrationHistory table as well as for inserting the new history values:
create table "Test"."__MigrationHistory"
-- ...
insert into "Test"."__MigrationHistory"("MigrationId", "ContextKey", "Model", "ProductVersion") ...
However, lets be perfectly clear: I just tried what I expected to work by intuition and it did work for me. I didn't find any reliable documentation to support this solution.
open Migrations -> Configuration.cs and add
public Configuration()
{
AutomaticMigrationsEnabled = false;
var historyContextFactory = GetHistoryContextFactory("Oracle.ManagedDataAccess.Client");
SetHistoryContextFactory("Oracle.ManagedDataAccess.Client",
(dbc, schema) => historyContextFactory.Invoke(dbc, "your_schema_name_hear"));
}
I use EF 6 and code first in a project
I try to understand using 'enable-migrations' command.
DbContext and Initializer examples are in simplest form like below .
When I give the command 'enable-migrations' package-manager console outputs an error like below :
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.
But If I do not call InitializeDatabase();method from MyDbContext constructor no error occures and no data imports or seed method does not run.
Only database creates.
I want to learn what's the reason and what's the mean of this error If I use InitializeDatabase() method.
Thank you
public class MyDbContext : DbContext
{
public MyDbContext():base("TestDb")
{
Database.SetInitializer(new DbContextInitializer());
InitializeDatabase();
}
protected virtual void InitializeDatabase()
{
if (!Database.Exists())
{
Database.Initialize(true);
}
}
public DbSet<TestModel> TestModels { get; set; }
}
public class DbContextInitializer : CreateDatabaseIfNotExists<MyDbContext>
{
protected override void Seed(MyDbContext context)
{
base.Seed(context);
context.TestModels.Add(new TestModel() {
Name = "Lorem",
Surname = "Ipsum"
});
}
}
Your initializer is inheriting from CreateDatabaseIfNotExists which is not a logical choice for migrations. You have some pending model changes that are not being applied because the model has changed and your initializer is only going to run when the database does not exist.
You could delete your database so it reinitializes with the changes, or switch your initializer to MigrateDatabaseToLatestVersion. Here is a good article on initializers and seeding.
I'm trying to automatically generate my database if it doesn't exists and run the Seed() method to populate the data. In my Database Context constructor I have this:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDBContext, Configuration>());
This works great, my database is automatically created with all the tables as I want, but it seems like the Seed() method is not being called, my database is empty. This is my class:
internal sealed class Configuration : DbMigrationsConfiguration<Context.MyDBContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(Context.MyDBContext context)
{
context.Users.AddOrUpdate(
new Entities.User() { Email = "default#default.com", Password = "", Language = "en", CreatedDate = DateTime.Now }
);
base.Seed(context);
}
}
When I run Update-Database in the Nuget console the data is populated after database creation, but with MigrateDatabaseToLatestVersion the Seed() method is not called.
What can be happening? I tried manually running migrations as taken from here:
var configuration = new MyDbContextConfiguration();
configuration.TargetDatabase = new DbConnectionInfo(
database.ConnectionString, database.ProviderName);
var migrator = new DbMigrator(configuration);
migrator.Update();
But also doesn't work.
EDIT:
Ok, after some more testing I found that the Seed() method runs but only when the database already exists, that is, on the first run when the database is created for the first time the Seed() method is not executed, but when I run my app the second time Seed() get's executed. I also had to add context.SaveChanges() in order for it to work (thanks to #DavidG in the comments):
protected override void Seed(Context.MyDBContext context)
{
context.Users.AddOrUpdate(
new Entities.User() { Email = "default#default.com", Password = "", Language = "en", CreatedDate = DateTime.Now }
);
context.SaveChanges();
base.Seed(context);
}
So maybe I can manually call Seed() inside Configuration() and do some checking to avoid adding duplicating data or modifying data that already exists.
I ended up with this Configuration class:
public class Configuration : DbMigrationsConfiguration<Context.MyDBContext>
{
private readonly bool _pendingMigrations;
public Configuration()
{
AutomaticMigrationsEnabled = true;
// Check if there are migrations pending to run, this can happen if database doesn't exists or if there was any
// change in the schema
var migrator = new DbMigrator(this);
_pendingMigrations = migrator.GetPendingMigrations().Any();
// If there are pending migrations run migrator.Update() to create/update the database then run the Seed() method to populate
// the data if necessary
if (_pendingMigrations)
{
migrator.Update();
Seed(new Context.MyDBContext());
}
}
protected override void Seed(Context.MyDBContext context)
{
// Microsoft comment says "This method will be called after migrating to the latest version."
// However my testing shows that it is called every time the software starts
// Run whatever you like here
// Apply changes to database
context.SaveChanges();
base.Seed(context);
}
}
So on this way the Seed() method is called when the database is created and also when there are pending migrations.
This is my MyDBContext class:
public class MyDBContext: DbContext
{
public MyDBContext() : base(AppSettings.DBConnectionString)
{
}
public static MyDBContext Create()
{
return new MyDBContext();
}
public DbSet<User> Users { get; set; }
public DbSet<Entries> Entries { get; set; }
}
I want to migrate stored procedures and views in my DB. Since I always migrate to the latest version, a source-control-friendly approach is to drop/recreate all procedures/views during migration process (with this approach there is one file per procedure, instead of one-per-version).
Since old procedures/functions/views might not be compatible with new schema changes, I want to do drop before all migrations, then do the create after all.
Previously I used a customized FluentMigrator, but now I am researching Entity Framework Code First Migrations. I see that I can use Seed to always run code after all migrations.
Is there something I can use to always run code before all migrations?
If you want some code to run before migrations kick in, you can specify a custom database initializer:
public class AwesomeEntity
{
public int Id { get; set; }
}
public class AwesomeDbContext : DbContext
{
static AwesomeDbContext()
{
Database.SetInitializer(new AwesomeDatabaseInitializer());
}
public IDbSet<AwesomeEntity> Entities { get; set; }
}
public class AwesomeDatabaseInitializer : MigrateDatabaseToLatestVersion<AwesomeDbContext, AwesomeMigrationsConfiguration>
{
public override void InitializeDatabase(AwesomeDbContext context)
{
// TODO: Run code before migration here...
base.InitializeDatabase(context);
}
}
public class AwesomeMigrationsConfiguration : DbMigrationsConfiguration<AwesomeDbContext>
{
public AwesomeMigrationsConfiguration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(AwesomeDbContext context)
{
// TODO: Seed database here...
}
}
This sets the custom initializer to a custom AwesomeDatabaseInitializer, which inherits from MigrateDatabaseToLatestVersion. If you want to drop and rebuild the database every time, you should use the DropCreateDatabaseAlways as base class instead, though I'm not sure this lets you run migrations.
In the initializer, you can override the InitializeDatabase method, where you can run code before you call base.InitializeDatabase, which will trigger the database initialization and in turn the Seed method of the migration configuration, AwesomeMigrationsConfiguration.
This is using EF6. I'm not sure if there is an equivalent in earlier versions of entity framework.
I have a solution that is pretty horrible, but works for migrate.exe.
Here is the idea:
Use Seed for AfterAll, as suggested by #khellang.
For BeforeAll, register a custom IDbConnectionInterceptor in MigrationsConfiguration constuctor to capture first connection after the MigrationsConfiguration has been created, then make it unregister itself. Obviously this is absolutely not thread-safe and only OK in application startup or migrate.exe.
Example code:
public class DbMigrationsInterceptingConfiguration<TContext> : DbMigrationsConfiguration<TContext>
where TContext : DbContext
{
public DbMigrationsInterceptingConfiguration() {
BeforeFirstConnectionInterceptor.InterceptNext();
}
protected override void Seed(TContext context) {
Console.WriteLine("After All!");
}
}
internal class BeforeFirstConnectionInterceptor : IDbConnectionInterceptor {
public static void InterceptNext() {
DbInterception.Add(new BeforeFirstConnectionInterceptor());
}
public void Opened(DbConnection connection, DbConnectionInterceptionContext interceptionContext) {
// NOT thread safe
Console.WriteLine("Before All!");
DbInterception.Remove(this);
}
// ... empty implementation of other methods in IDbConnectionInterceptor
}
I am not sure I would be actually using it though.
I have a DatabaseInitializer class
public class DatabaseInitializer : CreateDatabaseIfNotExists<DatabaseContext>
{
protected override void Seed
(
DatabaseContext databaseContext
)
{
// Seed the hash methods.
var defaultHashMethod = new HashMethod
{
Description = "Default",
CreateDate = DateTime.Now
};
databaseContext.HashMethod.Add(defaultHashMethod);
databaseContext.SaveChanges();
}
}
In my DatabaseContext class I set the initializer
public DatabaseContext() : base("DatabaseContext")
{
InitializeDatabase();
}
private void InitializeDatabase()
{
Database.SetInitializer(new DatabaseInitializer());
if (!Database.Exists())
{
Database.Initialize(true);
}
}
As far as I can understand the seed method is only invoked once you perform an operation such as a query. My database is created successfully and I'm querying the table, but the seed method is never called.
Update:
It seems like the problem is caused because of a class that is inheriting from my DatabaseContext class, when using this class to perform database operations, the seed method is not called. When using my DatabaseContext class, everything works as expected
public DbSet<TestEntity> TestEntity { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
You need to call Update-Database from the Package Manager Console.
The only way I could get this to work was to call the seed method myself
Here are the methods for my DatabaseContext class
public DatabaseContext() : base("DatabaseContext")
{
InitializeDatabase();
}
public DatabaseContext(string connectionString) : base(connectionString)
{
Database.Connection.ConnectionString = connectionString;
InitializeDatabase();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
Here I changed my InitializeDatabase method from
private void InitializeDatabase()
{
Database.SetInitializer(new DatabaseInitializer());
if (!Database.Exists())
{
Database.Initialize(true);
}
}
to
protected virtual void InitializeDatabase()
{
if (!Database.Exists())
{
Database.Initialize(true);
new DatabaseInitializer().Seed(this);
}
}
This can happen if your Update-Database command does not run successfully, and this does not necessarily mean that it errors out. There might be changes that EF recognizes as "outstanding" that need to be added to a migration.
Try calling "Add-Migration {migrationNameHere}" and then try "Update-Database" again.
to get Seed method to be called when you are not using AutomaticMigration, you should use MigrateDatabaseToLatestVersion initializer for your code-first database.
like this:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<YourContext,YourConfiguration>());
this way, Seed method will be called every time the migration is done successfully.
I had this issue and the problem was my Context constructor did not use the same name as in my web.config.
If you are using Code-First then you can populate the data when the application runs for the first time.
Create a DbInitializer
public class MyDbInitializer : IDatabaseInitializer<MyDbContext>
{
public void InitializeDatabase(MyDbContext context)
{
if (context.Database.Exists())
{
if (!context.Database.CompatibleWithModel(true))
{
context.Database.Delete();
}
}
context.Database.Create();
User myUser = new User()
{
Email = "a#b.com",
Password = "secure-password"
};
context.Users.AddOrUpdate<User>(p => p.Email, myUser);
context.SaveChanges();
}
}
Register this DbInitializer in your Global.asax.cs Application_Start method
Database.SetInitializer(new My.namespace.MyDbInitializer());
My seed was not being executed either. However, it was because I added a column to a model that I had no intention of using in my actual database and forgot to use the [NotMapped] annotation.
[NotMapped]
public string Pair { get; set; }
There was no error message relating to this being the cause at all. Just a null reference to my repository obj when I tried to query data that should have been there.