Entity framework code first migration not always working well - entity-framework

I have created a migration for my recent changes to my model that resulted in the following script,
DropIndex("dbo.InboundActions", new[] { "ReferredFrom_Id1" });
DropIndex("dbo.InboundCopyActionLogs", new[] { "InboundReferToEmployee_Id" });
DropIndex("dbo.InboundCopyActions", new[] { "ReferredFrom_Id1" });
DropColumn("dbo.InboundActions", "ReferredFrom_Id");
DropColumn("dbo.InboundCopyActions", "ReferredFrom_Id");
RenameColumn(table: "dbo.InboundCopyActionLogs", name: "InboundReferToDivision_Id", newName: "InboundRefer_Id");
RenameColumn(table: "dbo.InboundCopyActions", name: "ReferredFrom_Id1", newName: "ReferredFrom_Id");
RenameColumn(table: "dbo.InboundCopyActionLogs", name: "InboundReferToEmployee_Id", newName: "InboundRefer_Id");
RenameColumn(table: "dbo.InboundActions", name: "ReferredFrom_Id1", newName: "ReferredFrom_Id");
RenameIndex(table: "dbo.InboundCopyActionLogs", name: "IX_InboundReferToDivision_Id", newName: "IX_InboundRefer_Id");
Now when I try to update the database I get the following errors,
The index 'IX_ReferredFrom_Id' is dependent on column 'ReferredFrom_Id'.
The object 'FK_dbo.InboundActions_dbo.Divisions_ReferredFrom_Id' is dependent on column 'ReferredFrom_Id'.
ALTER TABLE DROP COLUMN ReferredFrom_Id failed because one or more objects access this column.
This is not the first time I find this kind of errors with migrations, which forces to me drop all migrations and database and start with brand new migrations which of course isn't practical, is there a problem with EF or me?!

Related

EF Core 5 migrations - update composite primary and foreign keys

I am using EF Core code first migrations. I have two tables (models), TableA has 4 columns which are set as composite primary key and TableB has the same 4 columns set as primary key and foreign key to TableA.
On of the four columns is of type int and I need to change it to long. I changed the type to long in both models and added a new migration. However, I am getting the following error when trying to update the database to apply the migration:
The object 'PK_TableA' is dependent on column 'Number'.
The object 'FK_TableB_TableA_Column1_Column2_Column3_Column4' is dependent on column 'Number'.
ALTER TABLE ALTER COLUMN Number failed because one or more objects access this column.
The column Number (column #3) in the error above is the one which has the type of int.
The generated migration is as follows:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<long>(
name: "Number",
table: "TableA",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AlterColumn<long>(
name: "Number",
table: "TableB",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "int");
}
I checked a number of topics here but none of them includes a solution for a "composite primary and foreign keys".
I found this article about manually changing the generated migration code. But I am not sure if it s going to work with a composite primary and foreign key. Or, if it is the right way to fix this issue.
Any advise will be appreciated.
Just in case someone was looking for an answer to this question, this is how I solved the issue.
I edited the generated migration code, as follows, to manually remove the foreign key first in TableB and then the two composite primary keys in TableA and TableB and then added them after changing the column type to bigint (according to the generated migration code):
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_TableB",
table: "TableB");
migrationBuilder.DropPrimaryKey(
name: "PK_TableA",
table: "TableA");
migrationBuilder.DropPrimaryKey(
name: "PK_TableB",
table: "TableB");
migrationBuilder.AlterColumn<long>(
name: "Number",
table: "TableA",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AlterColumn<long>(
name: "Number",
table: "TableB",
type: "bigint",
nullable: false,
oldClrType: typeof(int),
oldType: "int");
migrationBuilder.AddPrimaryKey(
name: "PK_TableA",
table: "TableA",
columns: new[] { "Column1", "Column2", "Column3", "Column4" });
migrationBuilder.AddPrimaryKey(
name: "PK_TableB",
table: "TableB",
columns: new[] { "Column1", "Column2", "Column3", "Column4" });
migrationBuilder.AddForeignKey(
name: "FK_TableB",
table: "TableB",
columns: new[] { "Column1", "Column2", "Column3", "Column4" },
principalTable: "TableA",
principalColumns: new[] { "Column1", "Column2", "Column3", "Column4" },
onDelete: ReferentialAction.Cascade);
}
The same code was added for the Down method.

EF Core migration taking old migrations changes along with new columns

I have added 2 more columns in aspnetusers table and added migrations. But it's failing and showing error stating FK_Projects_AspNetUsers_UserId' is not a constraint.
Could not drop constraint. See previous errors.
public string NavbarBackGroundImagePath { get; set; }
public string NavbarBackGroundColorCode { get; set; }
Added these 2 columns.
migrationBuilder.DropForeignKey(
name: "FK_Projects_AspNetUsers_UserId",
table: "Projects");
migrationBuilder.DropIndex(
name: "IX_Projects_UserId",
table: "Projects");
migrationBuilder.DropColumn(
name: "UserId",
table: "Projects");
migrationBuilder.AddColumn<int>(
name: "WorkspaceId",
table: "Projects",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<string>(
name: "NavbarBackGroundColorCode",
table: "AspNetUsers",
maxLength: 10,
nullable: true);
migrationBuilder.AddColumn<string>(
name: "NavbarBackGroundImagePath",
table: "AspNetUsers",
maxLength: 200,
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Projects_WorkspaceId",
table: "Projects",
column: "WorkspaceId");
migrationBuilder.AddForeignKey(
name: "FK_Projects_Workspaces_WorkspaceId",
table: "Projects",
column: "WorkspaceId",
principalTable: "Workspaces",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
But in migration file it's taking all these things.
According to the error message, it seems that this error happens when you want to drop the foreign key constraint in the Projects table, if the Projects table doesn't contain the "FK_Projects_AspNetUsers_UserId" constraint or doesn't contain the "UserId" property, it will show this error.
So, please check the Projects table in the database, whether you have removed the UerId column. And whether the Projects table have the foreign key constraint with the AspNetUsers table. If the Projects table doesn't contain this constraint, try to remove the DropForeignKey related code:
migrationBuilder.DropForeignKey(
name: "FK_Projects_AspNetUsers_UserId",
table: "Projects");

Entity Framework change Key Type

I have created a model with various values but stupidly used a GUID for my key, I am currently attempting to change that to an Int but am getting an error when I do so.
I have run the enable migration command:
Enable-Migrations -Force -ContextTypeName project.Models.MyContext
This creates the migration I would expect but when I run:
Update-Database -Force
The error I'm getting is:
Operand type clash: uniqueidentifier is incompatible with int
I don't care about the data currently contained within the database since it is just using a SQL Server Express database just now, but I would prefer to find a way to migrate this instead of just having to drop the DB altogether, what's the best way to do this?
I have already got
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>());
in Global.asax.
I would expect that the generated migration is using AlterColumn to try and change the type of the field from guid to int. This is not possible, so you'll need to modify the generated migration yourself:
Assuming your table is dbo.People and the key is called Id, you probably have this at the moment:
DropPrimaryKey("dbo.People");
AlterColumn("dbo.People", "Id", c => c.Int(nullable: false, identity: true));
AddPrimaryKey("dbo.People", "Id");
Change it to:
DropPrimaryKey("dbo.People");
DropColumn("dbo.People", "Id");
AddColumn("dbo.People", "Id", c => c.Int(nullable: false, identity: true));
AddPrimaryKey("dbo.People", "Id");
Note that if you've got this key referenced elsewhere, this technique will not work if you've got any data present, as the keys are all being regenerated.
Update for EF Core generated migrations:
dotnet : System.Data.SqlClient.SqlException: Operand type clash: int is incompatible with uniqueidentifier
change
migrationBuilder.AlterColumn<Guid>(
name: "VATID",
schema: "catalogue",
table: "Products",
nullable: false,
oldClrType: typeof(int));
into
migrationBuilder.DropColumn(
name: "VATID",
schema: "catalogue",
table: "Products");
migrationBuilder.AddColumn<Guid>(
name: "VATID",
schema: "catalogue",
table: "Products",
nullable: false);
Of course, this will destroy your data for the certain column. But they obviously cannot be converted into GUID.
I am trying to add something to the selected answer since I don't have enough reputation to add comment there :
Please also add code to drop and recreate indexes if any else it will fail to drop column. e.g.
DropPrimaryKey("dbo.People");
DropIndex("IX_...") // If exists before
DropColumn("dbo.People", "Id");
AddColumn("dbo.People", "Id", c => c.Int(nullable: false,
identity: true));
AddPrimaryKey("dbo.People", "Id");
CreateIndex("IX_...", unique:(true/false)) // If existed before
The AlterColumn will Drop and Add keys (primary,foreign etc) but will not touch indexes. I have faced this.

Entity Framework 5 and Code First - Can I get it to do a Rename instead of Drop and Create?

I'm using EF 5 Code First. I have the following Fluent API code that sets a navigation property for 'SaleZipCode'.
private void MyTable(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyType>()
.HasRequired(a => a.SaleZipCode)
.WithMany()
.Map(map => map.MapKey("SaleZipCodeId"));
}
I realize I misnamed 'SaleZipCode' and it should be 'ZipCode'. However, when I do this, EF attempts to drop the SaleZipCode column and add a ZipCode column when I do an Update-Database, instead of just doing a rename. This doesn't work because I have existing data in the table. Is there a way I can get EF to do a rename and not a drop and recreate?
Renaming property in your entity should not affect database, because there is no SaleZipCode column in database. Foreign key is mapped to SaleZipCodeId column, and only changing this foreign key column mapping will affect database. E.g. if you will change mapping to
modelBuilder.Entity<MyType>()
.HasRequired(a => a.Foo) // changing this will not affect database
.WithMany()
.Map(map => map.MapKey("ZipCodeId")); // column name changed
Then following migration will be generated
public override void Up()
{
RenameColumn(table: "dbo.MyTypes",
name: "SaleZipCodeId", newName: "ZipCodeId");
}
public override void Down()
{
RenameColumn(table: "dbo.MyTypes",
name: "ZipCodeId", newName: "SaleZipCodeId");
}
Simple renaming columns, data will be preserved.

How to avoid DROP DEFAULT statements with Doctrine 2 Migrations diff on first run?

I had an existing PostgreSQL database with a table created like this:
CREATE TABLE product (id SERIAL PRIMARY KEY, name VARCHAR(100) DEFAULT NULL)
This table is described in a YML Doctrine2 file within a Symfony2 project:
Acme\DemoBundle\Entity\Product:
type: entity
table: product
fields:
id:
id: true
type: integer
nullable: false
generator:
strategy: SEQUENCE
name:
type: string
length: 100
nullable: true
When I run for the first time the Doctrine Migrations diff task, I should get a versioning file with no data in the up and down methods. But what I get instead is this :
// ...
class Version20120807125808 extends AbstractMigration
{
public function up(Schema $schema)
{
// this up() migration is autogenerated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != "postgresql");
$this->addSql("ALTER TABLE product ALTER id DROP DEFAULT");
}
public function down(Schema $schema)
{
// this down() migration is autogenerated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != "postgresql");
$this->addSql("CREATE SEQUENCE product_id_seq");
$this->addSql("SELECT setval('product_id_seq', (SELECT MAX(id) FROM product))");
$this->addSql("ALTER TABLE product ALTER id SET DEFAULT nextval('product_id_seq')");
}
}
Why are differences detected? How can I avoid this? I tried several sequence strategies with no success.
A little update on this question.
Using Doctrine 2.4, the solution is to use the IDENTITY generator strategy :
Acme\DemoBundle\Entity\Product:
type: entity
table: product
id:
type: integer
generator:
strategy: IDENTITY
fields:
name:
type: string
length: 100
nullable: true
To avoid DROP DEFAULT on fields that have a default value in the database, the default option on the field is the way to go. Of course this can be done with lifecycle callbacks, but it's necessary to keep the default value in the database if this database is used by other apps.
For a "DEFAULT NOW()" like default value, the solution is the following one:
Acme\DemoBundle\Entity\Product:
type: entity
table: product
id:
type: integer
generator:
strategy: IDENTITY
fields:
creation_date:
type: datetime
nullable: false
options:
default: CURRENT_TIMESTAMP
Doctrine 2.0 does not support the SQL DEFAULT keyword, and will always try to drop a postgres default value.
I have found no solution to this problem, I just let doctrine handle the sequences itself.
This is a opened bug registered here :
http://www.doctrine-project.org/jira/browse/DBAL-903