EF Code First 5.0.rc Migrations doesn`t update Identity property - entity-framework

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.

Related

"The association has been severed but the relationship is either marked as 'Required' or is implicitly required..."

I am getting the following error when trying to add a migration:
PS C:\Code\morpher.ru\Morpher.Database> dotnet ef migrations add AddQazaqFeatures --startup-project=../Morpher.Database.Design
Build started...
Build succeeded.
System.InvalidOperationException: The association between entity types 'Service' and 'Deployment' has been severed but the relationship is either m
arked as 'Required' or is implicitly required because the foreign key is not nullable. If the dependent/child entity should be deleted when a requi
red relationship is severed, then setup the relationship to use cascade deletes. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLoggin
g' to see the key values.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.HandleConceptualNulls(Boolean sensitiveLoggingEnabled, Boolean forc
e, Boolean isCascadeDelete)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.CascadeDelete(InternalEntityEntry entry, Boolean force, IEnumerable`1 fore
ignKeys)
...
My code:
public class Deployment
{
public int Id { get; set; }
public virtual Service Service { get; set; }
public int ServiceId { get; set; }
public string Host { get; set; }
public short? Port { get; set; }
public string BasePath { get; set; }
}
public class Service
{
public int Id { get; set; }
public string Name { get; set; }
public string UrlSlug { get; set; }
public virtual ICollection<Endpoint> Endpoints { get; set; }
public virtual ICollection<Deployment> Deployments { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Service>().HasData(new Service
{
Name = "Веб-сервис «Морфер»",
UrlSlug = "ws",
Id = 1
});
modelBuilder.Entity<Deployment>().HasData(new Deployment
{
Host = "ws3.morpher.ru",
ServiceId = 1,
Id = 1
});
modelBuilder.Entity<Deployment>(entity =>
{
entity.Property(e => e.Host).IsRequired().HasMaxLength(256);
entity.Property(e => e.BasePath).HasMaxLength(512);
entity.HasOne(deployment => deployment.Service)
.WithMany(service => service.Deployments)
.HasForeignKey(d => d.ServiceId)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_Deployments_Services");
});
}
There are numerous StackOverflow questions mentioning the same error (1, 2, 3), but they are mostly to do with removing entities while not having a CASCADE delete policy or a nullable foreign key.
In my case, I am trying to add new rows and I don't see why it is considering the relationship 'severed'. Is setting ServiceId = 1 not enough?
I was able to reproduce the issue in latest at this time EF Core 3.1 version (3.1.28) by first removing the model data seeding code (HasData calls), then adding migration for just creating the tables/relationships, then adding the data seeding code and attempting to add new migration.
It does not happen in latest EF Core 6.0, so apparently you are hitting EF Core 3.1 defect/bug which has been fixed somewhere down on the road. So you either need to upgrade to a newer EF Core version (with all associated burdens like retesting everything, breaking changes etc.), or use the workaround below.
The workaround is to replace the DeleteBehavior.Restrict with either ClientNoAction or NoAction. Values of that enum and documentation of what they do is kind of messy, but all these 3 values seem to generate one and the same regular enforced FK constraint (with no cascade) in the database, and differ only by client side behavior, or in other words, what does EF Core change tracker do with related tracked entities when "deleting" a principal entity. And in this particular case, `Restrict" throws exception when there are tracked (loaded) related entity instances, while the other two won't.
I know you think you are just "adding data", but EF Core model data seeding is more than that - it tries to keep that data, so in some circumstances it needs to update or delete previously added data. Which in general works, except when there are bugs in the EF Core codebase, like in this case.

EF migration with new table collation error

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.

Add-Migration not adding column to existing table in Entity Framework Core

I am trying to add a new column to my existing table in database, i am specifying new field into class and running Add-Migration command but everytime it is creating a migration class for whole table with CreateTable method instead of AddColumn. Below is the class and generated migration class codes.
public class UserSetup
{
public int Id { get; set; }
public string Name { get; set; }
public bool Age{ get; set; } // New Field Added
}
But for new field it is creating migration class for full table as shown below:
public partial class AddUserSetup_1 : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "UserSetup",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Name= table.Column<string>(nullable: false),
Age= table.Column<int>(nullable: false),
},
constraints: table =>
{
table.PrimaryKey("PK_UserSetup", x => x.Id);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserSetup");
}
}
Also in Add-Migration it is giving me the following error but even migration class is getting created.
System.UnauthorizedAccessException: Access to the path 'C:\Projects\PSM\Portal\src\Portal\Migrations\ApplicationDbContextModelSnapshot.cs' is denied.
This can happen if you don't have the { get; set; } accessors. Clearly not the case in your code example, though.
Comment your new Age field
//public bool Age{ get; set; } // New Field Added
Open the "Server Explorer" window and make sure you can see your table in "Data Connections"
Run in Package Manager Console:
Enable-Migrations
Add-Migration InitialCreate –IgnoreChanges
Update-Database
Now EF should recognize your DB
Now uncomment your new "Age" field
Run in Package Manager Console:
Add-Migration AddUserSetup_1
Update-Database
Comment:
When you write "Enable-Migrations" maybe you will need to add -Force like that:
Enable-Migrations -Force
If you are using TFS make sure to checkout for edit your 'ApplicationDbContextModelSnapshot.cs' file. It will work just fine!

Unable to query new table from asp.net core web application using EF 1.0

I have developed a new asp.net Core web application using Visual Studio 2015. I am at the point where I am adding user customization options by adding additional tables to my local database. However I have been unable to add whatever EF needs to query a new table correctly. I get the following error when attempting to query the table..
Applying existing migrations for ApplicationDbContext may resolve this issue
There are migrations for ApplicationDbContext that have not been applied to the database
•00000000000000_CreateIdentitySchema
Apply Migrations
In Visual Studio, you can use the Package Manager Console to apply pending migrations to the database:
PM> Update-Database
Alternatively, you can apply pending migrations from a command prompt at your project directory:
dotnet ef database update
My table is a simple table with a few varchar or nvarchar columns. The model looks something like...
namespace MyNamespace.ColorSchemes
{
public class ColorSchemesViewModel
{
[Required]
public string Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string bc { get; set; }
}
Table looks something like this in SQL Server...
CREATE TABLE [dbo].[ColorSchemes](
[Id] [nvarchar](50) NOT NULL,
[Name] [varchar](32) NOT NULL,
[bc] [nchar](7) NOT NULL
)
I have added the table to the application context like such...
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<ColorSchemesViewModel> Colors { get; set; }
I have also used as separate class similarly like..
public DbSet<ColorSchemes> Colors { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
I have added the context to a controller like this...
private ApplicationDbContext _context;
public MyController(IMemoryCache memoryCache, ILoggerFactory loggerFactory, ApplicationDbContext context)
{
_memoryCache = memoryCache;
_logger = loggerFactory.CreateLogger<ChordMVCController>();
_context = context;
}
I have tried to query the table in my controller like this...
var colorSchemes = (from c in _context.Colors
select c).ToList();
I have attempted to use the Package Manager to per instructions from the error...
PM> Update-Database
I always get this error...
System.Data.SqlClient.SqlException: There is already an object named 'AspNetRoles' in the database.
This doesn't make sense since this table is already in the database and the EF definition. How do I get my table added properly to the EF migrations so I can query it?
I was able to solve this myself...
I created a different context rather than trying to embed the dbset in the default ApplicationDbContext and also removed the onModelCreating method.
public class ColorSchemeDbContext : DbContext
{
public ColorSchemeDbContext(DbContextOptions<ColorSchemeDbContext> options) : base(options)
{
}
public DbSet<ColorScheme> ColorSchemes { get; set; }
}
Replaced the ApplicationDBContext with the new context in my controller class...
private readonly ColorSchemeDbContext _context;
public MyController(IMemoryCache memoryCache, ILoggerFactory loggerFactory, ColorSchemeDbContext context)
{
_memoryCache = memoryCache;
_logger = loggerFactory.CreateLogger<ChordMVCController>();
_context = context;
}
After that the query worked. I spent a lot of time attempting to use the EF migrations to create the tables from a class syntax. Nothing seemed to work. I was creating a new .NET CORE web application in VS 2015 with the template and using user authentication which creates the AspNetRoles tables in SqlLite once you do an update-database. It is very confusing how to add additional tables using a code first approach after that. A lot more documentation is needed regarding EF migrations with respect to managing projects over time. I see the benefits of having all of your database updates maintained from your VS project but it is not easy to understand.

Update optional FK to Required with Automatic Migrations

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; }
}