I'm using Entity Framework code first, It has been working ok updating the database with db migrations up until recently...
I've added a anew property to the AspNetUser table
public partial class AspNetUser
{
....
public ICollection<Feed> Feeds { get; set; }
}
This is my new table
public class Feed
{
public int Id { get; set; }
public string UserId { get; set; }
public AspNetUser User { get; set; }
public MessageType Type { get; set; }
public string Data { get; set; }
public DateTime DateCreated { get; set; }
}
And this the is DBMigration script generated
public override void Up()
{
CreateTable(
"dbo.Feeds",
c => new
{
Id = c.Int(nullable: false, identity: true),
UserId = c.String(nullable: false, maxLength: 128),
Type = c.Int(nullable: false),
Data = c.String(),
DateCreated = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AspNetUsers", t => t.UserId, cascadeDelete: true)
.Index(t => t.UserId);
}
In the Context class:
modelBuilder.Entity<Feed>().HasRequired(x => x.User).WithMany(x => x.Feeds).HasForeignKey(x => x.UserId);
This created the table fine, on localhost, but when i deployed and run the migration on staging, the error i get is:
Column 'dbo.AspNetUsers.Id' is not of same collation as referencing column 'Feeds.UserId' in foreign key 'FK_dbo.Feeds_dbo.AspNetUsers_UserId'.
Could not create constraint or index. See previous errors.
What must I do... I've gone with the code first approach, thinking this would be easier, but this is really frustrating.
Note: I'm using sql Azure
The collation of both columns 'dbo.AspNetUsers.Id' and 'Feeds.UserId' should be the same, To make them the same you can modify the collation of one of those columns using below sample code:
context.Database.SqlCommand("ALTER TABLE MyTable ALTER COLUMN MyColumn VARCHAR(50) COLLATE SQL_Latin1_General_CP1_CS_AS NULL");
Hope this helps.
Regards,
Alberto Morillo
This Is how I got round my problem, but is by no means a solution I would have wanted.
Export the Backpac of the db from azure (via the azure portal)
Import the backpac into SSMS (right click databases > import.. follow wizard)
Change the collation here, by right clicking the db > properties > options> collation dropdown.
export this backpac using SSMS
import the backpac (from step 4) onto azure server (I used SSMS to do this)
Point the connection strings to the new db.
The data comes through too, so no loss there.
I've tried a few db migrations and they seem to work too.
With EF Core 5 (and possibly classic EF6) you can declare the FK field yourself.
.Property("UserId").UseCollation("SQL_Latin1_General_CP1_CS_AS");
Keep your .WithMany() invocation as-is.
Related
I am new to EF and previously engineered custom ORMs that use TIMESTAMP fields for concurrency and also determining records for synchronization to other databases.
Why does EF (Core) use nvarchar(max) to store what looks like a Guid?
i.e. why does EF do work that the DB could be doing instead?
The obvious thing is at some point (maybe when scaling up to multiple servers/databases) we want to store multiple Guids in there, and/or maybe it is simply because ROWVERSION/TIMESTAMP is not consistently implemented on the DBs targeted by EF?
(on a similar note why is the ID field nvarchar(450)?)
UPDATE:
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false),
ConcurrencyStamp = table.Column<string>(nullable: true),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
This seems like a questionable design decision of ASP.NET Core Identity, not a problem in Entity Framework Core. They use public virtual string ConcurrencyStamp { get; set; }, but for RowVersion/Timestamp columns, Entity Framework uses byte[] with an additional annotation or mapping to make sure EF understands the value should be re-read after updates. From one of EF's own test files:
public class Two
{
[Key]
public int Id { get; set; }
[StringLength(16)]
public string Data { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
public virtual C NavC { get; set; }
}
If you use EF yourself, you should be able to use RowVersion/Timestamp columns without any issues.
Table EMPLOYEE has MST_SQ (master-sequence) as both it's primary key, and as an FK to the primary key of table MASTER, which is also named MST_SQ. This table is used to join several other tables as well so that they all have the same PK. That is as far as my understanding goes.
I need to defined a 1 to 1 relationship in my model between class Employee and class Master, but I simply cannot find a way to do this. It seems only relationships with multiplicty allow an FK field to be speficied, and those that look like for 1 to 1, e.g. has optional(...)..WithRequiredPrincipal(....) has no FK space.
I could do some manual coding to link EMPLOYEE and MASTER when the are loaded, but how could I tell they were loaded. Is there any event that signals a POCO being populated from the DB? Or, the real question, how do I define this relationship in code?
From Relationships and Navigation Properties :
When working with 1-to-1 or 1-to-0..1 relationships, there is no
separate foreign key column, the primary key property acts as the
foreign key
From Configuring a Required-to-Optional Relationship (One-to–Zero-or-One) :
because the name of the property does not follow the convention the
HasKey method is used to configure the primary key
public class Master
{
public int MST_SQ { get; set; }
public virtual Employee Employee { get; set; }
}
public class Employee
{
public int MST_SQ { get; set; }
public virtual Master Master { get; set; }
}
The Employee has the MST_SQ property that is a primary key and a foreign key:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Master>().HasKey(m => m.MST_SQ);
modelBuilder.Entity<Employee>().HasKey(e => e.MST_SQ);
modelBuilder.Entity<Employee>()
.HasRequired(e => e.Master) //Employee is the Dependent and gets the FK
.WithOptional(m => m.Employee); //Master is the Principal
}
Generated migration code:
CreateTable(
"dbo.Employees",
c => new
{
MST_SQ = c.Int(nullable: false),
})
.PrimaryKey(t => t.MST_SQ)
.ForeignKey("dbo.Masters", t => t.MST_SQ)
.Index(t => t.MST_SQ);
CreateTable(
"dbo.Masters",
c => new
{
MST_SQ = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.MST_SQ);
So you don't need the "FK space" because EF makes it the foreign key without you having to specify it
I have a table which has an optional FK to another table and want to change that FK to a required relationship.
I have Automatic Migrations enabled and enabled destructive changes for this update. All entities in the database also have this key populated.
I changed this:
modelBuilder.Entity<Blog>().HasOptional(b => b.AuthorSecurable).WithMany().Map(b => b.MapKey("AuthorSecurableId"));
to:
modelBuilder.Entity<Blog>().HasRequired(b => b.AuthorSecurable).WithMany().Map(b => b.MapKey("AuthorSecurableId"));
and got the following error:
'FK_dbo.Blogs_dbo.Securables_AuthorSecurableId' is not a constraint.
Could not drop constraint. See previous errors.
There are no previous errors I could see (no inner exception ect.)
This post says you can get around this error with the following:
ALTER TABLE [dbo].[Blogs] NOCHECK CONSTRAINT [FK_dbo.Blogs_dbo.Securables_AuthorSecurable_Id]
so i did:
public override void Up()
{
Sql("ALTER TABLE [dbo].[Blogs] NOCHECK CONSTRAINT [FK_dbo.Blogs_dbo.Securables_AuthorSecurable_Id]");
DropForeignKey("dbo.Blogs", "AuthorSecurableId", "dbo.Securables");
DropIndex("dbo.Blogs", new[] { "AuthorSecurableId" });
AlterColumn("dbo.Blogs", "AuthorSecurableId", c => c.Int(nullable: false));
AddForeignKey("dbo.Blogs", "AuthorSecurableId", "dbo.Securables", "Id", cascadeDelete: true);
CreateIndex("dbo.Blogs", "AuthorSecurableId");
}
But still got the same error
EDIT:
the full code is avaliable here and a minimal models are below:
public class Blog
{
public int Id { get; set; }
public Securable AuthorSecurable { get; set; }
}
public class Securable
{
public int Id { get; set; }
}
Say, we are using EF Code First and we have this simple model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace EFCodeFirstIdentityProblem.Models
{
public class CAddress
{
public int ID { get; set; }
public string Street { get; set; }
public string Building { get; set; }
public virtual CUser User { get; set; }
}
public class CUser
{
public int ID { get; set; }
public string Name { get; set; }
public string Age { get; set; }
[Required]
public virtual CAddress Address { get; set; }
}
public class MyContext : DbContext
{
public DbSet<CAddress> Addresses { get; set; }
public DbSet<CUser> Users { get; set; }
}
}
Like this, CAddress would be principal end of this 1:0..1 relationship.
Next we add connection string to Web.Config (I use MSSQL 2008 R2), make a controller that uses this model, run. EF Code First creates tables for us as expected:
So, let's assume we made a mistake, and in fact we want CUser to be principal end of this 0..1:1 relationship. So we make changes:
...
[Required]
public virtual CUser User { get; set; }
...
...
public virtual CAddress Address { get; set; }
...
Build, then in Package Manager Console run and add some migration:
PM> Enable-Migrations
Checking if the context targets an existing database...
Detected database created with a database initializer. Scaffolded migration '201208021053489_InitialCreate' corresponding to existing database. To use an automatic migration instead, delete the Migrations folder and re-run Enable-Migrations specifying the -EnableAutomaticMigrations parameter.
Code First Migrations enabled for project EFCodeFirstIdentityProblem.
PM> Add-Migration ChangeDependency
Scaffolding migration 'ChangeDependency'.
The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration 201208021157341_ChangeDependency' again.
PM>
Here what we`ve been given for "ChangeDependency" migration:
namespace EFCodeFirstIdentityProblem.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class ChangeDependency : DbMigration
{
public override void Up()
{
DropForeignKey("dbo.CUsers", "ID", "dbo.CAddresses");
DropIndex("dbo.CUsers", new[] { "ID" });
AlterColumn("dbo.CAddresses", "ID", c => c.Int(nullable: false));
AlterColumn("dbo.CUsers", "ID", c => c.Int(nullable: false, identity: true)); //identity: true - this is important
AddForeignKey("dbo.CAddresses", "ID", "dbo.CUsers", "ID");
CreateIndex("dbo.CAddresses", "ID");
}
public override void Down()
{
DropIndex("dbo.CAddresses", new[] { "ID" });
DropForeignKey("dbo.CAddresses", "ID", "dbo.CUsers");
AlterColumn("dbo.CUsers", "ID", c => c.Int(nullable: false));
AlterColumn("dbo.CAddresses", "ID", c => c.Int(nullable: false, identity: true));
CreateIndex("dbo.CUsers", "ID");
AddForeignKey("dbo.CUsers", "ID", "dbo.CAddresses", "ID");
}
}
}
Importand part is:
AlterColumn("dbo.CUsers", "ID", c => c.Int(nullable: false, identity: true));
So CUsers.ID must now become Identity in DB. Let's commit this changes to DB:
PM>
PM> Update-Database -Verbose
Using StartUp project 'EFCodeFirstIdentityProblem'.
Using NuGet project 'EFCodeFirstIdentityProblem'.
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Target database is: 'EFTest' (DataSource: (local), Provider: System.Data.SqlClient, Origin: Configuration).
Applying code-based migrations: [201208021157341_ChangeDependency].
Applying code-based migration: 201208021157341_ChangeDependency.
ALTER TABLE [dbo].[CUsers] DROP CONSTRAINT [FK_dbo.CUsers_dbo.CAddresses_ID]
DROP INDEX [IX_ID] ON [dbo].[CUsers]
ALTER TABLE [dbo].[CAddresses] ALTER COLUMN [ID] [int] NOT NULL
ALTER TABLE [dbo].[CUsers] ALTER COLUMN [ID] [int] NOT NULL
ALTER TABLE [dbo].[CAddresses] ADD CONSTRAINT [FK_dbo.CAddresses_dbo.CUsers_ID] FOREIGN KEY ([ID]) REFERENCES [dbo].[CUsers] ([ID])
CREATE INDEX [IX_ID] ON [dbo].[CAddresses]([ID])
[Inserting migration history record]
Running Seed method.
PM>
There is no SQL instructions given by Migrations of CUsers.ID becoming Identity column in DB. So, because of this there is a problem:
(updated database)
So, User is principal end now, and has to have ID Identity: "YES" flag, but Identity is still "NO". And Address is dependent end, has to have ID Identity "NO", but is still "YES". So I can't add new User to User table, because new ID is not generated for new instance.
If I drop whole database, EF Code First creates new tables from scratch properly, so this is a problem only of Migrations.
What do I do in this situation? Is this EF Migrations bug?
I'm not sure if it is a bug because there is another problem - you cannot alter existing column to identity or remove identity. I can imagine that this is considered as fully manual migration to make it clear that you must move data.
I'm pretty sure it's something regarding hidden conventions, but I always get an error when trying to map a many-to-many relation to an existing database.
Here is the simplest example:
[Table("ALRole", SchemaName = "AL")]
public class Role
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
[Table("ALUser", SchemaName = "AL")]
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
I got the usual three tables in the db: the first two are obvious, and the third is created with this script:
CREATE TABLE AL.ALUsersRoles
(
RoleID int NOT NULL,
UserID int NOT NULL,
CONSTRAINT PK_ALUserRole PRIMARY KEY(RoleID, UserID),
CONSTRAINT FK_ALUserRole_RoleID FOREIGN KEY(RoleID) REFERENCES AL.ALRole(ID),
CONSTRAINT FK_ALUserRole_UserID FOREIGN KEY(UserID) REFERENCES AL.ALUser(ID)
)
Now I try to map the many-to-many relation, with code like this:
// ...I'm in the EntityTypeConfiguration-derived class (User)
HasMany(u => u.Roles)
.WithMany(r => r.Users)
.Map(m =>
{
m.MapLeftKey(u => u.ID, "UserID");
m.MapRightKey(r => r.ID, "RoleID");
ToTable("ALUsersRoles", "AL");
});
I tried all the possibile combinations and variations in this code, but I always get the error:
{"Invalid column name 'Name'.\r\nInvalid ...and so on...
So I think it must be the table that is not created correctly.
Any ideas?
Thanks in advance
Andrea
P.S.: I stripped down some of my code, so maybe there can be some small typo...
well, this works for me same as OP.
//many-to-many between *Users -> Web_User_Rol <- Web_Rol*
modelBuilder.Entity<Users>()
.HasMany(u => u.Web_Rols).WithMany(r => r.Users)
.Map(t=>t.MapLeftKey("user_id")
.MapRightKey("roleID")
.ToTable("Web_User_Rol"));
There is nothing wrong with your object model or fluent API code. I've used them and they perfectly created the desired schema without any exception. I think your problem comes from another entity (perhaps one with a "Name" property) unrelated to what you've shown here. To find that, drop (or rename) your existing database and let Code First create one for you and then compare the 2 databases and see what is different.