context.database.create() throws exception invalid column name "Branch_Id" - entity-framework

I have a migration that got added once and applied successfully:
public partial class AddedTablesForBranchData : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.SalesArea",
c => new
{
PostalCode = c.Int(nullable: false, identity: true),
Location = c.String(),
Branch_Id = c.String(maxLength: 128),
})
.PrimaryKey(t => t.PostalCode)
.ForeignKey("dbo.SalesBranch", t => t.Branch_Id)
.Index(t => t.Branch_Id);
CreateTable(
"dbo.SalesBranch",
c => new
{
Id = c.String(nullable: false, maxLength: 128),
Name = c.String(),
Contacts = c.String(),
})
.PrimaryKey(t => t.Id);
}
public override void Down()
{
DropForeignKey("dbo.SalesArea", "Branch_Id", "dbo.SalesBranch");
DropIndex("dbo.SalesArea", new[] { "Branch_Id" });
DropTable("dbo.SalesBranch");
DropTable("dbo.SalesArea");
}
}
Sadly the the PostalCode is an integer. I had to change it to a string due to localization...
Thus some migrations later I added a new migration:
public partial class ReCreateSalesTables : DbMigration
{
public override void Up()
{
DropForeignKey("SalesArea", "Branch_Id", "SalesBranch");
DropIndex("SalesArea", new[] { "Branch_Id" });
DropTable("dbo.SalesArea");
DropTable("dbo.SalesBranch");
CreateTable("SalesBranch",
c => new
{
Id = c.String(false, maxLength: 128),
Name = c.String(),
Contacts = c.String()
})
.PrimaryKey(t => t.Id);
CreateTable("SalesArea",
c => new
{
Id = c.Int(false, true),
PostalCode = c.String(maxLength: 32),
Location = c.String(),
BranchId = c.String(nullable: false, maxLength: 128)
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.SalesBranch", t => t.BranchId)
.Index(t => t.PostalCode, unique: true);
}
public override void Down()
{
throw new Exception("It has never been our intention to use a down migration, else data might be lost...");
}
}
Then I run into the "a table con not have multiple identity columns" problem due to PostalCode being a identity column and in the new migration I have Id being a column Identity
Thus I had to drop both tables in the new migration and re-create those tables with the new schema.
There seems to be no problem on my local machine/development environment But when I run the integration tests or before any test is run I do this:
[TestClass]
public sealed class InitializeDatabase
{
[AssemblyInitialize]
public static void AssemblyInit(TestContext x)
{
using (var context = new LeadContext())
{
// Create database outside of the test transactions else you get a nice exception...
context.Database.Delete();
context.Database.Create();
new Configuration().FillEnums(context);
}
}
}
It is deleting the old database and creating a new database using all migrations. This AssemblyInit method runs fine when I debug it, but after leaving the method some second later I can see this output in my integration test:
Result Message: Initialization method IntegrationTests.SalesDataTests.Init threw exception. System.Data.Entity.Core.EntityCommandExecutionException: System.Data.Entity.Core.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Invalid column name 'Branch_Id'..
I am not able to debug my integration test directly as I never get there, so the problem must be the context.database.create() method.
Why is EF complaining that the old/former foreign key column 'Branch_Id' is invalid?
I do not understand that scenario.
Can anybody help please :-)
UPDATE
Question:
What changed between AddedTablesForBranchData and migration ReCreateSalesTables ?
Answer:
I introduced a property Id (identity column/string) and changed the property PostalCode to integer/unique:true.
UPDATE 2
There exist no fluent configurations about any SalesX table.
Model
[Table("SalesBranch")]
public class SalesBranch
{
[Key]
public string Id { get; set; }
public string Name { get; set; }
public string Contacts { get; set; }
public virtual ICollection<SalesArea> SalesAreas { get; set; }
}
[Table("SalesArea")]
public class SalesArea
{
public int Id { get; set; }
public string PostalCode { get; set; }
public string Location { get; set; }
public virtual SalesBranch Branch { get; set; }
public int BranchId { get; set; }
}

By annotating the property:
public virtual SalesBranch Branch { get; set; } with [ForeignKey("BranchId")] it fixed the problem!
It seemed the model were out of sync with the migrations!

Related

Combating Migration Bloat in Entity Framework Code First Applications

Now that some of us have Code First projects that have been running in production for years and have been accumulating lots of Migrations, has anyone run into problems having a large number of them? Is there such a thing as "too many migrations?"
If so, what is the remedy? Some caveats:
- Obviously we can't delete & rescaffold the production db.
- We can't delete all migrations, __MigrationHistory, and create a new Initial (in my case) because many of our migrations have data seeds/updates, and even tweaks to the generated commands.
Is there a way/tool to combine migrations into fewer migrations? Would that even make a difference?
Thanks!
From ISHIDA's suggestions, I have created an example of one way to combine migrations. This is by no means the only/correct solution, nor does it answer the question of whether migration bloat is a problem, but it's a darn good start.
To test this, I have a console app with 2 tables, which are:
public class Account
{
[Required]
[StringLength(100)]
public string Id { get; set; }
[Required]
[StringLength(10)]
public string AccountNumber { get; set; }
public virtual List<Policy> Policies { get; set; }
}
public class Policy
{
[Required]
[StringLength(100)]
public string Id { get; set; }
[Required]
public int PolicyNumber { get; set; }
[Required]
public string AccountId { get; set; }
public virtual Account Account { get; set; }
}
There are 4 migrations which created these tables, added data, and changed the datatype of PolicyNumber from string to int. Pretend this program is live and all of these have run in a production environment.
public partial class InitialCreate : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Accounts",
c => new
{
Id = c.String(nullable: false, maxLength: 100),
AccountNumber = c.String(nullable: false, maxLength: 10),
})
.PrimaryKey(t => t.Id);
}
public override void Down()
{
DropTable("dbo.Accounts");
}
}
public partial class SeedAccounts : DbMigration
{
readonly string[] accountIds = new string[] { "IdAcct101", "IdAcct102" };
public override void Up()
{
Sql($"INSERT INTO Accounts (Id, AccountNumber) VALUES ('{accountIds[0]}','101')");
Sql($"INSERT INTO Accounts (Id, AccountNumber) VALUES ('{accountIds[1]}','102')");
}
public override void Down()
{
Sql($"DELETE FROM Accounts WHERE ID = '{accountIds[0]}'");
Sql($"DELETE FROM Accounts WHERE ID = '{accountIds[1]}'");
}
}
}
public partial class AddPolicyTable : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Policies",
c => new
{
Id = c.String(nullable: false, maxLength: 100),
PolicyNumber = c.String(nullable: false, maxLength: 100),
AccountId = c.String(nullable: false, maxLength: 100),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Accounts", t => t.AccountId, cascadeDelete: true)
.Index(t => t.AccountId);
}
public override void Down()
{
DropForeignKey("dbo.Policies", "AccountId", "dbo.Accounts");
DropIndex("dbo.Policies", new[] { "AccountId" });
DropTable("dbo.Policies");
}
}
public partial class ChangeAndSeedPolicies : DbMigration
{
readonly string[] accountIds = new string[] { "IdAcct101", "IdAcct102" };
readonly string[] policyIds = new string[] { "IdPol101a", "IdPol101b", "IdPol102a" };
public override void Up()
{
AlterColumn("dbo.Policies", "PolicyNumber", c => c.Int(nullable: false));
Sql($"INSERT INTO Policies (Id, AccountId, PolicyNumber) VALUES ('{policyIds[0]}', '{accountIds[0]}', '10101')");
Sql($"INSERT INTO Policies (Id, AccountId, PolicyNumber) VALUES ('{policyIds[1]}', '{accountIds[0]}', '10102')");
Sql($"INSERT INTO Policies (Id, AccountId, PolicyNumber) VALUES ('{policyIds[2]}', '{accountIds[1]}', '10201')");
}
public override void Down()
{
Sql($"DELETE FROM Policies WHERE ID = '{policyIds[0]}'");
Sql($"DELETE FROM Policies WHERE ID = '{policyIds[1]}'");
Sql($"DELETE FROM Policies WHERE ID = '{policyIds[2]}'");
AlterColumn("dbo.Policies", "PolicyNumber", c => c.String(nullable: false, maxLength: 100));
}
}
Here is the code in the project's Main:
using (var dc = new DataContext())
{
foreach (var account in dc.Accounts.OrderBy(q => q.AccountNumber).ToList())
{
Console.WriteLine("Account " + account.AccountNumber);
foreach (var policy in account.Policies)
Console.WriteLine(" Policy " + policy.PolicyNumber);
}
}
DataContext Class:
public class DataContext : DbContext
{
public DataContext() : base("DefaultConnection") { }
public DbSet<Account> Accounts { get; set; }
public DbSet<Policy> Policies { get; set; }
}
The output is:
Account 101
Policy 10101
Policy 10102
Account 102
Policy 10201
Very simple. Now I want to combine these migrations into one. Remember:
we don't want to drop & rescaffold because production would have data
other than what the migrations added
the prior migrations must be able to be re-run for integration testing & new environments
These are the steps I followed:
back up any environment you'll be running this against.
create a new migration (it should be blank, as nothing has changed yet)
in Package Console Manager (PMC), run "update-database" to create the __MigrationHistory record
Verify the app runs normally at this point
copy all Up methods from old migrations to the new one
copy all Down methods from old migrations to the new one in reverse order
Verify the app runs normally at this point (should detect no new migrations needed)
delete all old migrations
delete all old __MigrationHistory records (leaving only the new one)
Verify the app runs normally at this point
To verify the new migration really does everything the old ones did (for new environments or testing), simply delete all tables in the database (including __MigrationHistory), run "update-database" in PMC, and see if it runs.
This is what my new migration looks like:
public partial class CombinedMigration : DbMigration
{
readonly string[] accountIds = new string[] { "IdAcct101", "IdAcct102" };
readonly string[] policyIds = new string[] { "IdPol101a", "IdPol101b", "IdPol102a" };
public override void Up()
{
CreateTable(
"dbo.Accounts",
c => new
{
Id = c.String(nullable: false, maxLength: 100),
AccountNumber = c.String(nullable: false, maxLength: 10),
})
.PrimaryKey(t => t.Id);
Sql($"INSERT INTO Accounts (Id, AccountNumber) VALUES ('{accountIds[0]}','101')");
Sql($"INSERT INTO Accounts (Id, AccountNumber) VALUES ('{accountIds[1]}','102')");
CreateTable(
"dbo.Policies",
c => new
{
Id = c.String(nullable: false, maxLength: 100),
PolicyNumber = c.String(nullable: false, maxLength: 100),
AccountId = c.String(nullable: false, maxLength: 100),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Accounts", t => t.AccountId, cascadeDelete: true)
.Index(t => t.AccountId);
AlterColumn("dbo.Policies", "PolicyNumber", c => c.Int(nullable: false));
Sql($"INSERT INTO Policies (Id, AccountId, PolicyNumber) VALUES ('{policyIds[0]}', '{accountIds[0]}', '10101')");
Sql($"INSERT INTO Policies (Id, AccountId, PolicyNumber) VALUES ('{policyIds[1]}', '{accountIds[0]}', '10102')");
Sql($"INSERT INTO Policies (Id, AccountId, PolicyNumber) VALUES ('{policyIds[2]}', '{accountIds[1]}', '10201')");
}
public override void Down()
{
// Each prior "Down" section was added in reverse order.
Sql($"DELETE FROM Policies WHERE ID = '{policyIds[0]}'");
Sql($"DELETE FROM Policies WHERE ID = '{policyIds[1]}'");
Sql($"DELETE FROM Policies WHERE ID = '{policyIds[2]}'");
AlterColumn("dbo.Policies", "PolicyNumber", c => c.String(nullable: false, maxLength: 100));
DropForeignKey("dbo.Policies", "AccountId", "dbo.Accounts");
DropIndex("dbo.Policies", new[] { "AccountId" });
DropTable("dbo.Policies");
Sql($"DELETE FROM Accounts WHERE ID = '{accountIds[0]}'");
Sql($"DELETE FROM Accounts WHERE ID = '{accountIds[1]}'");
DropTable("dbo.Accounts");
}
}
Caveat: If any of your migrations have .NET code that creates a new DC and does some db updates, those may not work when migrations are combined. For example, if migration 1 adds the Account table, and migration 2 uses .NET code to insert records into Account, it will crash in the combined migration because Account technically hasn't been created yet. Replacing such statements with Sql('INSERT INTO...") statements will fix this.

How to save ID as string in DB with Code-First EntityFramework

I have a model:
public class Something
{
public int Id { set; get; }
public string Name{ set; get; }
}
Also I have this class:
public class SomethingConfiguration : EntityTypeConfiguration<Something>
{
public SomethingConfiguration()
{
HasKey(t => t.Id).Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
Everything works fine and the Id generates automatically after inserting to DB and save changes (commit).
Now I want to add another column IdString (Id as string instead of int), so I can use it for searching-by-Id manipulations (autocompletes and more). How can I add IdString column that will get the automatic Id as string and will be save automatically while inserting and saving? Is it possible?
In Sql Server you can define a computed column with an underlying formula.
ALTER TABLE dbo.Something Add IsString AS cast(Id as nvarchar)
These columns can be mapped in you model like this.
public partial class Something
{
public int Id { get; set; }
public string IdString { get; set; }
public string Name { get; set; }
}
this.Property(p => p.IdString)
.HasMaxLength(30)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
And then used in your query.
var data1 = db.Something.Where(p => p.IdString.Contains("123"));
Because of the DatabaseGeneratedOption.Computed definition, EntityFramework will request the current Value of the column every time you update the row.
But if you only want a translatable version of ToString() for an integer value you could just use SqlFunctions.StringConvert instead.
var data = db.Something
.Where(p => SqlFunctions.StringConvert((decimal)p.Id).Contains("12"));
Update:
Add computed column with a Migration.
public override void Up()
{
CreateTable(
"dbo.Something",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 255),
})
.PrimaryKey(t => t.Id);
Sql("ALTER TABLE dbo.Something Add IsString AS cast(Id as nvarchar)");
}
public override void Down()
{
DropTable("dbo.Something");
}

code first migration add one to many relationship

I currently have a "server" entity, defined as such :
public class EntityServer
{
public int Id { get; set; }
public string Name { get; set; }
}
I wanted to add a new "Host" entity, defined as such :
public class EntityHost
{
public int Id { get; set; }
public string Name { get; set; }
public string PublicIP { get; set; }
private ICollection<EntityServer> _servers;
public virtual ICollection<EntityServer> Servers
{
get { return _servers ?? (_servers = new HashSet<EntityServer>()); }
set { _servers = value; }
}
}
So i added
public virtual EntityHost Host { get; set; }
to my server entity to link those entities with a one to many relationship
modelBuilder.Entity<EntityHost>()
.HasMany<EntityServer>(x => x.Servers)
.WithRequired(x => x.Host);
And generated a migration acordingly :
public partial class MultiHosts : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.EntityHosts",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
PublicIP = c.String(),
})
.PrimaryKey(t => t.Id);
AddColumn("dbo.EntityServers", "Host_Id", c => c.Int(nullable: false));
CreateIndex("dbo.EntityServers", "Host_Id");
AddForeignKey("dbo.EntityServers", "Host_Id", "dbo.EntityHosts", "Id", cascadeDelete: true);
}
public override void Down()
{
DropForeignKey("dbo.EntityServers", "Host_Id", "dbo.EntityHosts");
DropIndex("dbo.EntityServers", new[] { "Host_Id" });
DropColumn("dbo.EntityServers", "Host_Id");
DropTable("dbo.EntityHosts");
}
}
I've got some troubble setting a code first migration to add all it together as it outpout me a foreign key violation error when i try to access the context (which i understand as the server entity isn't linked to a host, as required by the model, because the hosts table is empty and I can't access the hosts entities to add one because of the FK violation ....)
So, my question is : how should I insert a default host entites for the existings server ?
As a trick you could first set the Server as Optional
modelBuilder.Entity<EntityHost>()
.HasOptional(x=>x.Server)
.WitMany(x => x.Hosts);
Run
Add-Migrations set_server_optional
update-Database
Update your Database and then change the Server as Required
modelBuilder.Entity<EntityHost>()
.HasRequired(x=>x.Server)
.WithMany(x => x.Hosts);
And finally
Add-Migrations set_server_required
update-Database

How do I create a new column and move data to it with Entity Framework Code First

class Person
{
public int Id { get; set; }
//omitted for brevity
public bool IsActive { get; set; }
}
class Person
{
public int Id { get; set; }
//omitted for brevity
public int Status { get; set; }
}
enum Status
{
Active,
Inactive,
NoState,
}
Person is a database table with data. Due to new requirement I would like to remove IsActive and have a Status enum. I would also like to move the data from IsActive column to Status column without losing it. I'm using EF Code First. How do I go about it?
I finally solved this. The steps I followed are as follows;
Scaffold an Add-Migration AlterStatusEnum through package manager console.
In the Up method of the generated partial class, change the
AddColumn("dbo.Person","Status", c => c.Int(nullable:false));
to
RenameColumn("dbo.Person", "IsActive", "Status");
Add an alter statement
AlterColumn("dbo.Person", "Status", c => c.Int(nullable: false));
this got the job done.
Sample code
public partial class AlterStatusEnum : DbMigration
{
public override void Up()
{
RenameColumn("dbo.Person", "IsActive", "Status");
AlterColumn("dbo.Person", "Status", c => c.Int(nullable: false));
}
public override void Down()
{
// Removed for brevity
}
}
In a nutshell all am doing is renaming the existing column and altering its datatype.

Turn complex type into entity without losing data

We've been using a complex type CreditCardTransaction associated with our purchase orders. It really should have been it's own entity, and I am trying to fix this now.
How would you write a migration from this:
[ComplexType()]
public class CreditCardTransaction
{
public String SomeTransactionData { get; set; }
}
into this entity:
public class CreditCardTransaction
{
public int Id { get; set; }
public String SomeTransactionData { get; set; }
}
This is what is generated for me. Can I easily move the data in the original table to the other table?
public partial class test : DbMigration
{
public override void Up()
{
CreateTable(
"CreditCardTransactions",
c => new
{
Id = c.Int(nullable: false, identity: true),
SomeTransactionData = c.String()
})
.PrimaryKey(t => t.Id);
AddColumn("PurchaseOrder", "Transaction_Id", c => c.Int());
AddForeignKey("PurchaseOrder", "Transaction_Id", "CreditCardTransactions", "Id");
CreateIndex("PurchaseOrders", "Transaction_Id");
DropColumn("PurchaseOrders", "Transaction_SomeTransactionData");
}
// Down() snipped
}
I believe you can. Modify your Up method and prior to call DropColumn use Sql method with custom SQL transferring data from PurchaseOrders to CreditTransactions and fixing FK relationships.