EF5 Database Migrations: How to move data - entity-framework

I'm using EF5 code first and now I need to update my database so I enabled database migrations and added a migration but the generated code was not what I needed. This is the code:
public override void Up()
{
CreateTable(
"dbo.HistoricalWeightEntities",
c => new
{
PatientMedicalDataId = c.Guid(nullable: false),
Id = c.Guid(nullable: false),
Weight = c.Single(nullable: false),
Date = c.DateTime(nullable: false),
})
.PrimaryKey(t => new { t.PatientMedicalDataId, t.Id })
.ForeignKey("dbo.PatientMedicalDataEntities", t => t.PatientMedicalDataId, cascadeDelete: true)
.Index(t => t.PatientMedicalDataId);
AddColumn("dbo.PatientDataEntities", "PatientDataFilePath", c => c.String());
//Here I need to move data from the old Weight column to the Weight column on the newly
//created table and create the id (Guid) and the foreing key before the old
//column is dropped
DropColumn("dbo.PatientMedicalDataEntities", "Weight");
}
What I need to do is to add some sql script that move data from the 'Weight' column in the dbo.PatientMedicalDataEntities to the Weight column in the newly created table dbo.HistoricalWeightEntities and also insert the Id value (key) which is a Guid and the corresponding foreign key before the column is dropped.
Can somebody show me how to do this is sql?
Thank you in advance

It should be something like that (donnow what you wanna do with the Date column)
Sql("INSERT INTO HistoricalWeightEntities(Id, Weight, PatientMedicalDataId) "+
"SELECT newid(), Weight, <theForeignKeyColumn> from PatientMedicalDataEntities");

Related

How to change a clustered index in Entity Framework 6.1 Code First model and apply it to an Azure database

Using the Entity Framework 6.1 code first model, what is the best way to go about changing the clustered index on a table from the default ID to another set of columns. Azure doesn't allow a table without a clustered index.
public partial class UserProfile
{
public override Guid ID { get; set; }
[Index( "CI_UserProfiles_UserID", IsClustered = true)]
public Guid UserID { get; set; }
[Required]
public Guid FieldID { get; set; }
[Required]
[StringLength(400)]
public string Value { get; set; }
}
On the table UserProfiles, ID is already the primary key and clustered index. Adding
[Index( "CI_UserProfiles_UserID", IsClustered = true)]
to UserID creates this migration:
CreateIndex("dbo.UserProfiles", "UserID", clustered: true, name: "IX_UserProfiles_UserID");
Executing the migration generates the following error:
Cannot create more than one clustered index on table 'dbo.UserProfiles'. Drop the existing clustered index
'PK_dbo.UserProfiles' before creating another.
To solve your problem, after you generate your migration file, you must modify the generated code by disabling clustered index for your primary key by assigning false as a value of clustered parameter of PrimaryKey.
After your modifications you must have something like this into your migration file:
CreateTable(
"dbo.UserProfiles",
c => new
{
Id = c.Guid(nullable: false),
UserID = c.Guid(nullable: false),
FieldID = c.Guid(nullable: false),
Value = c.String(nullable: false, maxLength: 400),
})
.PrimaryKey(t => t.Id, clustered: false)
.Index(t => t.UserID, clustered: true, name: "CI_UserProfiles_UserID");
This is not done in OnModelCreating method by using Fluent API like Manish Kumar said, but in migration file. The file that is created when you use Add-Migration command.
Existing Database
As you say in comments, your database already exist. After executing Add-Migration command, you will have this line on your DbMigration file in your Up() method:
public override void Up()
{
CreateIndex("dbo.UserProfiles", "UserID", clustered: true, name: "CI_UserProfiles_UserID");
}
You must modify the Up() method to have this code:
public override void Up()
{
this.Sql("ALTER TABLE dbo.UserProfiles DROP CONSTRAINT \"PK_dbo.UserProfiles\"");
this.Sql("ALTER TABLE dbo.UserProfiles ADD CONSTRAINT \"PK_dbo.UserProfiles\" PRIMARY KEY NONCLUSTERED (Id);");
this.CreateIndex("dbo.UserProfiles", "UserID", clustered: true, name: "CI_UserProfiles_UserID");
}
In the code above I assumed that the created clustered index is named PK_dbo.UserProfiles in your database. If not then put at this place the correct name.
This is truly an area where EntityFramwork (Core) had to advance and it still is hard.
So, I could not use IsClustered(false) for my GUID / string Primary keys, for the simple reason, the project having DbContexts was DB - agnostic. So you needed to Add EntityFrameworkCore.SqlServer and IsClustered is available then, and only.
So, my solution was simple. Add no nuget package but this attribute.
This ONLY works on EF Core.
I have tested this on SQL. Though, not sure if the other providers would allow this string not having any meaning. (e.g. SQLite does not know clustered indexes)
p.HasKey(k => k.Id).HasAnnotation("SqlServer:Clustered", false);
You need to remove the existing clustered index from your current PK 'ID' which is created by default for any "KEY" property in code first. It can be done using fluent API:
.Primarykey(x=>x.ID,clustered:false)
Once existing clustered index is removed from ID, your migration to add the clustered index on UserID should run smoothly.
After the migration file is created, modify the generated code, disabling the clustered index for the primary key by setting the clustered property to false.
Being that Azure does not allow a table without a clustered index, and there is no utility in SQL Server to 'change' a clustered index on a table, it is necessary create a new table with the clustered index and migrate the existing data to it. The code below renames the original table, migrates the data to the new table that was created with the new clustered index and drops the original table.
RenameTable("dbo.UserProfiles", "UserProfiles_PreMigrate");
CreateTable(
"dbo.UserProfiles",
c => new
{
Id = c.Guid(nullable: false),
UserID = c.Guid(nullable: false),
FieldID = c.Guid(nullable: false),
Value = c.String(nullable: false, maxLength: 400),
})
.PrimaryKey(t => t.Id, clustered: false)
.Index(t => t.UserID, clustered: true, name: "CI_UserProfiles_UserID");
Sql(#"
INSERT [dbo].[UserProfiles]
(ID,
UserID,
FieldID,
Value)
SELECT
ID,
UserID,
FieldID,
Value
FROM dbo.UserProfiles_PreMigrate
");
DropTable("UserProfiles_PreMigrate");
Any existing table constraints will be lost in this operation, so it will be necessary to recreate and indexes,foreign keys, etc on the table.

Azure - Code First Migrations not working correctly

I am trying to initially create a sql server db schema for my azure application with code first migrations (as described here).
The basic creation of my tables works, however the azure (entitydata) specific things don't. There is no default value for CreatedAt, no UpdatedAt Trigger and also a problem with a clustered index. This happens to all of my tables. Thats what i am doing (shows the problem for 1 Table Account):
public Configuration()
{
AutomaticMigrationsEnabled = false;
SetSqlGenerator("System.Data.SqlClient", new EntityTableSqlGenerator());
}
var migrator = new DbMigrator(new Configuration());
migrator.Update();
The up-Method in my DBMigration-Class looks as follows:
CreateTable(
"dbo.Accounts",
c => new
{
Id = c.String(nullable: false, maxLength: 128),
Username = c.String(nullable: false, maxLength: 30),
Salt = c.Binary(),
SaltedAndHashedPassword = c.Binary(),
MailAddress = c.String(),
FacebookId = c.String(),
Version = c.Binary(nullable: false, fixedLength: true, timestamp: true, storeType: "rowversion"),
CreatedAt = c.DateTimeOffset(nullable: false, precision: 7),
UpdatedAt = c.DateTimeOffset(precision: 7),
Deleted = c.Boolean(nullable: false),
})
.PrimaryKey(t => t.Id)
.Index(t => t.CreatedAt, clustered: true);
I thought, that the EntityTableSqlGenerator should do these azure specific things, however it seems that it changes nothing.
I had the same issue few days ago and solved it temporarily as described below: I think Microsoft EF team should solve it permanently.
According to my temporary solution, you need to make some modifications before execute your migration file.
First, you need to cancel clustered index decalarations for CreatedAt properties for all entities which is inheriting from EntityData base class.
Id, CreatedAt and Deleted fields need to be decorated with default value declarations.
You can add following lines below each create commands in your migration files.
You can get additional ideas from here

how can create Unique Constraint with multi column with entityframework

I'm use Entityframework Code First, and an EntityTypeConfiguration using fluent API.
how can create Unique Constraint with multi column.
for example i have a table with below field
Id
CompanyId
Code
Name
i want set Code column to unique , per CompanyId
In your EntityTypeConfiguration you can do something like this:
Property(m => m.CompanyId)
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_YourUniqueIndexName", 1) { IsUnique = true }));
Property(m => m.Code)
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_YourUniqueIndexName", 2) { IsUnique = true }));
This will create a unique index on those 2 columns.
Make sure you use the same name for the unique index. Both need to be name "IX_YourUniqueIndex". If one is called "IX_Index1" and the other "IX_Index2" then it will create a unique index on each, which is not what you want

How to create index in desc order in code first?

Want to create index by EmployeeCardNumber (text) in descending order.
So the sql script should be like:
ALTER TABLE dbo.Employees ADD CONSTRAINT
IX_Employees_EmplyeeCardNumber UNIQUE CLUSTERED
(
EmployeeCardNumber DESC
)
In IndexAttribute, there is an Order property, which is an integer???
Here is my fluent api:
var indexAttr = new IndexAttribute("IX_Employees_EmplyeeCardNumber")
{
IsClustered = true,
IsUnique = true,
Order = 1 // probably should be removed since we are using a composite key.
};
Property(c => c.EmployeeCardNumber)
.HasColumnAnnotation("Index", new IndexAnnotation(indexAttr))
.HasMaxLength(8)
.IsRequired();
So the question is, how to define "DESC" in this code first approach?

EF 6 Code-Based Migration: Add not null property to existing entity

I want to add a not-null, foreing key column to an existing table.
Environment: EF 6,Code-First, Code-Based Migration
//Code from Migration class for new entity Currency
CreateTable("dbo.Currency",
c => new
{
CurrencyID = c.Int(nullable: false, identity: true),
Code = c.String(nullable: false, maxLength: 3, fixedLength: true, unicode: false),
Denomination = c.String(nullable: false, maxLength: 50, unicode: false),
})
.PrimaryKey(t => t.CurrencyID);
AddColumn("dbo.Collection", "CurrencyID", c => c.Int(nullable: false));
//Code from Seed() method in Configuration class
context.Currencies.AddOrUpdate(
new Currency
{
Code = "USD",
Denomination = "Dollar"
}
);
//Here i get an exception. Collection is the existing table
context.Database.ExecuteSqlCommand( "update collection set CurrencyID = 1 );
Exception message:
The UPDATE statement conflicted with the FOREIGN KEY constraint
"FK_dbo.Collection_dbo.Currency_CurrencyID". The conflict occurred in
table "dbo.Currency", column 'CurrencyID'.
Problem solved, here are enumerated by order the steps i followed:
Change the foreign key property mapping to Not Required
Seed only the primary key values
Update-Database
Change back the property to Required
Add new migration and seed the values for foreign key column
Update-Database