Model backing the Db context has changed but run anyway. Is there a way to turn off migrations? - entity-framework-migrations

I want my windows application to warn that there are migrations to run but to give the user the option to continue without running them.
At the moment I have code first projects with no migrations that access the database
However in the project where I do the database migrations I must either allow the migration to run or delete the migration and undo my changes to the model.
For the configuration I have
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
}
For the migration check I have
using (var db = new MyDbContext())
{
var compatible = db.Database.CompatibleWithModel(false);
if (!compatible)
{
compatible= RunMigrations(db);
}
return compatible;
}
If I don't allow the migration then I get the model backing error when I try to access data
var n = db.mytable.count() // will error
If I comment out the Configuration class and code that calls it I can run the program without error.

The trick is to create a new dbContext inheriting from the usual
public class MigrationDbContext : MyDbContext
{
}
Then the configuration class should use the MigrationDContext
internal sealed class Configuration : DbMigrationsConfiguration<MigrationDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
}
and the migration check should use the MigrationDbContext

Related

MVC Asp.NET Identity Unknown Error Occurring

I'm working on an MVC site that is using ASP.Net Identity for the log in piece.
There is a user table where the pk is an FK for another table (table 2). Because of this, the user cannot be deleted. As well, there are reports that are generated from table 2. This report displays the data from table 2 as long as the user name that generated the data. Another reason why deleting the user information should not be done.
So instead of deleting the user I thought I would add a column to the user table called "Deleted". It uses a bit datatype.
The column was added to my local copy of the database using code-first migrations. When the user logs on, this value is checked in this column is checked (0 = not deleted; 1 = deleted).
Everything was working great on my local machine. I had no errors, code ran fine. I was able to log in with any user. I then deployed to our test server and added the "Deleted" column to the table on the test SQL server using a script.
When I went to log in, with any user, I couldn't. I kept getting the "Error logging in. Please re-enter credentials and try again" even though my credentials were correct.
I deleted the files on the server and deployed again. Same thing.
I backed out all of my changes and deployed again and was able to log in.
So, I started adding things a piece at a time:
I ran the script to add a Deleted column with a bit datatype and set
to not null on our test SQL server
Tested logging in and was able to
Added public bool Deleted {get; set;} to my BL Entity and deployed to
test
Tried logging in and failed
Removed the Deleted bool and re-deployed
Was able to log in
I'm not understanding why this addition is causing issues. "Deleted" columns are used in other tables in the application and have not caused any issues. And because it's working fine when I run it locally I'm having a hard time determining what the issue is.
Here is my BL Entity:
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
namespace BlahBlah.BusinessLogic.Entities
{
public class User : IdentityUser
{
public User() : base() { }
public User(string username) : base(username) { }
public Guid CompanyId { get; set; }
public Company Company { get; set; }
public bool PrimaryAccount { get; set; }
public Guid DefaultCompanyId { get; set; }
public ICollection<Transaction> Transactions { get; set; }
// public bool Deleted { get; set; }
}
}
Here is a piece of my user.js code:
...
$http(call)
.success(function (data) {
userData.isAuthenticated = true;
userData.username = data.userName;
userData.role = data.role;
userData.bearerToken = data.access_token;
userData.expirationDate = new Date(data['.expires']);
setHttpAuthHeader();
if (typeof successCallback === 'function') {
successCallback();
}
})
.error(function (data) {
if (typeof errorCallback === 'function') {
if (data.error_description) {
errorCallback(data.error_description);
}
else {
errorCallback('Unable to contact server; please, try again later.');
}
}
});
};
...
Here is a piece of my login.js code:
...
function login(common, $location, config) {
var getLogFn = common.logger.getLogFn;
var log = getLogFn(controllerId);
var vm = this;
vm.title = 'Login';
vm.credentials = {
username: '',
password: ''
};
vm.hideError = true;
vm.errorMessage = 'xx.';
vm.login = function login() {
vm.hideError = true;
common.$broadcast(config.events.spinnerToggle, { show: true });
common.user.authenticate(vm.credentials.username, vm.credentials.password, onSuccessfullLogin, onFailedLogin);
};
...
function onFailedLogin(error) {
common.$broadcast(config.events.spinnerToggle, { show: false });
console.log('error ' + error);
vm.hideError = false;
}
...
Any ideas as to what is going on or how I can find out what is going on? Thanks for your time!
UPDATE
I shouldn't have gotten too excited. After performing some testing in the application I thought I would test the reports. Everything was working beautifully up until then. When I viewed a report I got the following error:
The model backing the 'BabDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database
The reports are using SSRS. If I ran the script to create the column AND update the _Migrations table I'm not understanding why I'm getting this error. The stack trace points to the last line. I've read suggestions but where I need to add some code to the OnModelCreating method but I'm not sure if that's the way to go. There have been other deploys done by others prior to me so I'm missing something.
public class AbaDbContext : IdentityDbContext<User>, IDbContext
{
public const int UnknownCTId = 1;
private IEncryptionService _encryptionService;
private static Dictionary<Guid, string> _cipherCache = new Dictionary<Guid,string>();
public BabDbContext()
: base("BabDbContext")
{
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += new ObjectMaterializedEventHandler(ObjectMaterialized);
It looks like your context is trying to run migrations on the production server and is failing due to either insufficient permissions or that the Deleted column already exists. Your options are:
Run your production migrations by creating a script. You can do this by running the following command:
Update-Database -Script
This will create a SQL script that yuo can run against your database to create the column and, critically, insert a row into the __MigrationHistory table that tells EF that it doesn't need to run migrations again.
Create a context initialiser that effectively disables migrations. Add a class that inherits from NullDatabaseInitializer<T>:
public class DisableMigrations : NullDatabaseInitializer<YourContext>
{
}
And in your production web.config:
<entityFramework>
<contexts>
<context type="YourNamespace.Domain.YourContext, YourNamespace.Domain">
<databaseInitializer
type="YourNamespace.Domain.DisableMigrations, YourNamespace.Domain"/>
</context>
</contexts>
</entityFramework>

Update database of a deployed web app

I'm using Entity Framework and I had to add some migrations. Now I'd like to update a customer's app and database.
I considered using update-database -script -SourceMigration migrationName command which doesn't work because I create a view in one of my migrations and it gives me the following exception in VS Console.
There is already an object named 'viewName' in the database.
It seems migrate.exe can be used too, but I'm not much familiar with its function and I prefer not to install anything on the customer's computer (because of limited access).
What's the best way to apply those migrations? I prefer to do it with a SQL script, but I can't get ride of that exception.
The migration code of the view:
public override void Up()
{
string script = #"CREATE VIEW [dbo].[viewName]
AS
SELECT *
FROM dbo.existingTable
WHERE (subquery)";
using (var db = new EFDbContext())
{
db.Database.ExecuteSqlCommand(script);
}
}
public override void Down()
{
var script = #"DROP VIEW [dbo].[viewName]";
using (var db = new EFDbContext())
{
db.Database.ExecuteSqlCommand(script);
}
}
The better way to add your views is using Sql inside Up and Down which ‘Up’ applies the changes to the target database and ‘Down’ reverts them:
public override void Up()
{
Sql("EXEC ('CREATE View [dbo].[viewName] AS --etc"
}
public override void Down()
{
Sql(#"IF EXISTS (SELECT
*
FROM sys.views
WHERE object_id = OBJECT_ID(N'dbo.viewName'))
DROP VIEW dbo.viewName)")
}

Code First Migration deletes all data

I am using Code First Migrations in my Web API (Mobile Service .NET backend) and whenever my model changes, all data is deleted from my database. This is what I am doing:
I add a new property to my model
I run "Add-Migration AddTestProperty" in the Package Manager console
The new migration is added to the Migrations folder (see code below)
public partial class AddTestProperty: DbMigration
{
public override void Up()
{
AddColumn("testdb.TestTable", "Test", c => c.String());
}
public override void Down()
{
DropColumn("testdb.TestTable", "Test");
}
}
My configuration looks like this:
public Configuration()
{
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = false;
}
Now if I run my webservice, the property is added to my TestTable but all data in all my tables is deleted. What am I doing wrong?
The reason my data was deleted from my database is this: If you use the service project downloaded from the Azure portal, you will find the following code in the App_Start/WebsApiConfig:
public static class WebApiConfig
{
public static void Register()
{
// Use this class to set configuration options for your mobile service
ConfigOptions options = new ConfigOptions();
// Use this class to set WebAPI configuration options
HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options));
Database.SetInitializer(new todoInitializer());
}
}
public class todoInitializer : ClearDatabaseSchemaIfModelChanges<todoContext>
{
.....
To enable Code First Migrations on a Mobile Services .NET backend you need to replace the Database.SetInitializer(new todoInitializer()); with this:
//Database.SetInitializer(new todoInitializer());
var migrator = new DbMigrator(new Configuration());
migrator.Update();
This requires the following using statements:
using System.Data.Entity.Migrations;
using todoService.Migrations;
More information on how to enable Code First Migrations on a Mobile Service .NET backend.

Entity Framework Migrations Integration Tests

Is there a way to write test for specific versions of the migrations?
Lets say that i have done "add-migration 01 Added column...." -> "08 Deleted column...".
And in my tests i want to see how each and every migration from 01 to 08 is doing.
So, to begin with i want to run all the migrations to the version "01 Added column..." and do a test in that state.
After that i may want to upgrade the database to "05..." and do some testing there.
And last, i want to upgrade the database all the way, and do some finishing tests.
Is this posible with EF Migrations Code first?
Sorry, I don't have an access to EF right now, so my code could not work, but I hope that it can give you a general direction of what you can do.
You can create a custom database initializer:
public sealed class MigrationInitializer : IDatabaseInitializer<MyContext>
{
private readonly string _targetMigration;
public MigrationInitializer()
{
}
public MigrationInitializer(string targetMigration)
{
if (string.IsNullOrEmpty(targetMigration))
{
throw new ArgumentException("targetMigration");
}
_targetMigration = targetMigration;
}
public void InitializeDatabase(MyContext context)
{
var migrationsConfiguration = new DbMigrationsConfiguration<MyContext>
{
AutomaticMigrationsEnabled = false
};
var migrator = new DbMigrator(migrationsConfiguration);
if (_targetMigration == null)
{
migrator.Update();
}
else
{
migrator.Update(_targetMigration);
}
}
}
Then force EF to use it in your tests:
System.Data.Entity.Database.SetInitializer<MyContext>(new MigrationInitializer("MyMigration"));
To migrate your database to the latest version you simply use default constructor of MigrationInitializer class.
Note that you can perform both upgrading and downgrading of your database using this method.

Entity Framework Code First migration issues -- Dataloss

I've read every blog-post and MSDN articles(http://msdn.microsoft.com/en-us/data/jj591621.aspx) on code first migration but I am little unclear how I should use it.
Here is the Migration history on my project:
Initially I use Enable-Migrations, then Add-Migration and Update-Database.
I deployed the project
I made some minor changes to the model. rerun add-migration and update-database.
deployed the project
I added more attributes to the mode. Also, I run Disable-Migrations and run Enable-Migrations -EnableAutomaticMigration
Now when I deploy the project.. and run the app for the first time, all the existing data is gone
Old Project (step #4) -- Migrations\Configurations.cs
namespace POC_Manager.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<POC_Manager.Models.POC_ManagerContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
ContextKey = "POC_Manager.Models.POC_ManagerContext";
}
protected override void Seed(POC_Manager.Models.POC_ManagerContext 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" }
// );
//
}
}
}
New Project (step #6) -- Migrations\Configurations.cs
namespace POC_Manager.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<POC_Manager.Models.POC_ManagerContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
ContextKey = "POC_Manager.Models.POC_ManagerContext";
}
protected override void Seed(POC_Manager.Models.POC_ManagerContext 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" }
// );
//
}
}
}
Old Project (step #4) -- Output from Get-Migrations
PM> Get-Migrations
Retrieving migrations that have been applied to the target database.
201405271907443_AutomaticMigration
201404252210039_InitialCreate
New Project (step #6) -- Output from Get-Migrations
PM> Get-Migrations
Retrieving migrations that have been applied to the target database.
201407022020263_AutomaticMigration
201406262227296_AutomaticMigration
201405271907443_AutomaticMigration
201404252210039_InitialCreate
PM>
Another confusing part is that... Do I still need to run the Update-Database command after enabling automatic migrations ??
Automatic Migrations are for automatically generating migration files based on changes to your classes; you will still need to run Update-Database unless you build logic for it into an initialization strategy.
As far as your data loss, it's most likely based on the initialization strategy that you used. I would suggest that you stick with CreateDatabaseIfNotExists unless your project really needs a custom initializer; the other standard ones are not terribly useful outside of an (early) development environment.

Resources