Here's an example of an entity we have inherited:
[Table("Vehicles")]
public partial class Car
{
[Key]
public int Id { get; set; }
[Required]
public Make Make { get; set; }
[Required]
public Model Model { get; set; }
}
We want to refactor our code to rename this table "Vehicle". We can change the [Table] attribute to generate a migration file that does a RenameTable(name: "dbo.Cars", newName: "Vehicles"), all well and good.
However, if we try to change the class name, the migration scaffolder tries to create and THEN delete the same table. Here's a example:
public override void Up()
{
DropForeignKey("dbo.Vehicles", "Make_Id", "dbo.Makes");
DropForeignKey("dbo.Vehicles", "Model_Id", "dbo.Models");
DropIndex("dbo.Vehicles", new[] { "Make_Id" });
DropIndex("dbo.Vehicles", new[] { "Model_Id" });
CreateTable(
"dbo.Vehicles",
c => new
{
Id = c.Int(nullable: false, identity: true),
Make_Id = c.Int(nullable: false),
Model_Id = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Makes", t => t.Make_Id)
.ForeignKey("dbo.Models", t => t.Model_Id)
.Index(t => t.Make_Id)
.Index(t => t.Model_Id);
DropTable("dbo.Vehicles");
}
Is there any way of making a change to the class name in code only (keeping the [Table] attribute the same), without causing this kind of behaviour? Or is this simply something I shouldn't be doing?
You don't have to use the migration as it is being generated. It tries to understand what you changed and create the migration that fits your work, but apparently the algorithm is confused in this case.
You can just alter the migration to execute the RenameTable method. This should work.
Neverless, please submit a bug report to the EF team at http://entityframework.codeplex.com
Related
am working on some legacy code , would to add new table to the DB
and it would have two one to zero or one relation ship and I need "for some reason" The ForeignKeys in the new table to be defined as properties and The code :
public class EconomyInfo : Entity
{
/*other props*/
[ForeignKey("LogoImage")]
[Required]
public int LogoImage_Id { get; set; }
public virtual Image LogoImage { get; set; }
[ForeignKey("Organization")]
[Required]
public int Organization_Id { get; set; }
public virtual Organization Organization { get; set; }
}
public class Image : Entity
{
/*other props*/
public virtual EconomyInfo Economyinfo { get; set; }
}
public class Organization : Entity
{
/*other props*/
public virtual EconomyInfo EconomyInfo { get; set; }
}
public class Entity
{
public int Id { get; set; }
}
Like This EF don't generate migration and give the error
EconomyInfo_LogoImage_Source: : Multiplicity is not valid in Role 'EconomyInfo_LogoImage_Source' in relationship 'EconomyInfo_LogoImage'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
and the same for the other navigation property
and if i add those to lines in the the context class:
modelBuilder.Entity<Organization>().HasOptional(o => o.EconomyInfo).WithOptionalPrincipal();
modelBuilder.Entity<Image>().HasOptional(i => i.Economyinfo).WithOptionalPrincipal();
the migration will be generated without errors but it will be strange like :
public override void Up()
{
CreateTable(
"dbo.EconomyInfoes",
c => new
{
Id = c.Int(nullable: false, identity: true),
LogoImage_Id = c.Int(nullable: false),
Organization_Id = c.Int(nullable: false),
Image_Id = c.Int(),
Organization_Id1 = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Images", t => t.LogoImage_Id, cascadeDelete: true)
.ForeignKey("dbo.Organizations", t => t.Organization_Id)
.ForeignKey("dbo.Images", t => t.Image_Id)
.ForeignKey("dbo.Organizations", t => t.Organization_Id1)
.Index(t => t.LogoImage_Id)
.Index(t => t.Organization_Id)
.Index(t => t.Image_Id)
.Index(t => t.Organization_Id1);
}
i know that i could edit the generated EF migration code to make it specify my needs , but there is no point of wasting hours dealing with EF and then give up and work around it , plus it won't be so practical to work with it later by my colleagues ,thanks in advance
I've just given up and decided to go with the work around as write my own migration for my new entity and tell the context to ignore it from the upcoming generated migration.
So my solution is this:
In the context class :
// Regarding unPossible configuration to make pre defined property as foreign key in one to zero or one relationship
// we wrote our migration and tell the context to ignore our model and its navigation property from other model classes
modelBuilder.Entity<Organization>().Ignore(o => o.EconomyInfo);
modelBuilder.Entity<Image>().Ignore(i => i.Economyinfo);
modelBuilder.Ignore<EconomyInfo>();
Organization class :
public virtual EconomyInfo EconomyInfo { get; set; }
Image class :
public virtual EconomyInfo Economyinfo { get; set; }
Manual written migration :
using System;
using System.Data.Entity.Migrations;
public partial class EconomyInfo : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.EconomyInfoes",
c => new
{
Id = c.Int(nullable: false, identity: true),
LogoImage_Id = c.Int(nullable: false),
Organization_Id = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Images", t => t.LogoImage_Id)
.ForeignKey("dbo.Organizations", t => t.Organization_Id)
.Index(t => t.LogoImage_Id)
.Index(t => t.Organization_Id);
}
public override void Down()
{
DropForeignKey("dbo.EconomyInfoes", "Organization_Id", "dbo.Organizations");
DropForeignKey("dbo.EconomyInfoes", "LogoImage_Id", "dbo.Images");
DropIndex("dbo.EconomyInfoes", new[] { "Organization_Id" });
DropIndex("dbo.EconomyInfoes", new[] { "LogoImage_Id" });
DropTable("dbo.EconomyInfoes");
}
}
You may refer to this question also for more info How do I specify the foreign key in a one-to-one/zero relationship?
Note: I applied my written migration to database works fine, try to generate new migration, also works as expected, but not tested working with my new model class from code yet. Hopefully nothing will be messed later,
I have a complex object hierarchy in an enterprise application. I'll try and keep it simple, and abstract, yet still representative of what I'm dealing with.
My project deals with several styles of the same type of object. For this, we have implemented the TPT structure for our entity objects:
public abstract class BaseWidget {
public int Id { get; set; }
// etc...
}
// About a dozen concrete implementations already exist and work great!
public class ExistingWidget : BaseWidget {
// Other properties
}
Now I have a new type that I'm doing. We have common properties on the object, but there are a few different sets of details that are required depending on the sub type. For this, I set up TPH, as the properties on that type are the same across all subtypes. The only difference is which details objects are required.
public abstract NewWidgetBase : BaseWidget {
public int EmployeeNumber { get; set; }
public DateTime EffectiveDate { get; set; }
}
public NewWidgetA : NewWidgetBase {
}
public NewWidgetB : NewWidgetBase {
}
I have this mapped in my DbContext like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<NewWidgetBase>()
.Map<NewWidgetA>(w => w.Requires("Discriminator").HasValue("a"))
.Map<NewWidgetB>(w => w.Requires("Discriminator).HasValue("b"));
At this point, I have used an integration test and successfully checked that I can save to both tables.
Now, I want to add in the details:
public class FooDetails {
public int Id { get; set; }
public int NewWidgetId { get; set; }
// ...
[ForeignKey(nameof(NewWidgetId))]
public NewWidgetBase NewWidget { get; set; }
}
public class BarDetails {
public int Id { get; set; }
public int NewWidgetId { get; set; }
// ...
[ForeignKey(nameof(NewWidgetId))]
public NewWidgetBase NewWidget { get; set; }
}
I then add those reference properties to my appropriate NewWidget objects.
public class NewWidgetA {
// ...
public FooDetails Foo { get; set; }
}
public class NewWidgetB {
// ...
public FooDetails Foo { get; set; }
public BarDetails Bar { get; set; }
}
I tried just executing this, assuming that the typical mapping would work, and got the following error:
System.Data.Entity.Infrastructure.DbUpdateException: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details. ---> System.Data.Entity.Core.UpdateException: Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
With that, I understood that it doesn't have the correct Relationship directions and keys mapped. So I went to explicitly set it within the DbContext again:
modelBuilder.Entity<NewWidgetA>()
.HasRequired(w => w.Foo)
.WithRequiredDependent();
However, that gives me the error:
System.InvalidOperationException: A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'WidgetId'.
I looked at a "some other" "questions", and none of those answers helped me.
As a last ditch effort, I tried using the overload for .WithRequiredDependent() which takes a Func. However, because it isn't the exact same type as I'm mapping because I have the property as the abstract base, it complains. Therefore, I try casting it like so:
modelBuilder.Entity<NewWidgetA>()
.HasRequired(w => w.Foo)
.WithRequiredDependent(f => (NewWidgetA)f.Widget);
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Foo)
.WithRequiredDependent(f => (NewWidgetB).Widget);
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Bar)
.WithRequiredDependent(b => (NewWidgetB).Widget);
However, this also gives an error:
The ForeignKeyAttribute on property 'Widget' on type '...Foo' is not valid. The foreign key name 'WidgetId' was not found on the dependent type 'NewWidgetA'. The Name value should be a comma separated list of foreign key property names.
This is leading me to believe that I'm unable to do what I want to do with having abstract properties. Is there a way to map this relationship that I'm missing? I don't want to have a specific reference property for each as I know there are more types coming within a month or two, and the list of properties will get unwieldy.
It's possible, but only with unidirectional (with navigation property only at Widget side) one-to-one Shared Primary Key Association, where the Widget side is the principal and the Details side is the dependent.
Start by removing the navigation and FK properties from Details entities:
public class FooDetails {
public int Id { get; set; }
// ...
}
public class BarDetails {
public int Id { get; set; }
// ...
}
and use the following fluent configuration:
modelBuilder.Entity<NewWidgetA>()
.HasRequired(w => w.Foo)
.WithRequiredPrincipal();
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Foo)
.WithRequiredPrincipal();
modelBuilder.Entity<NewWidgetB>()
.HasRequired(w => w.Bar)
.WithRequiredPrincipal();
Note the WithRequiredPrincipal() call. It's telling EF that (1) the Widget is the principal and (2) there is no navigation property from Details to Widget.
The resulting database schema is something like this:
CreateTable(
"dbo.BaseWidget",
c => new
{
Id = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.Id);
CreateTable(
"dbo.ExistingWidget",
c => new
{
Id = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.BaseWidget", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.NewWidgetBase",
c => new
{
Id = c.Int(nullable: false),
EmployeeNumber = c.Int(nullable: false),
EffectiveDate = c.DateTime(nullable: false),
Discriminator = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.BaseWidget", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.FooDetails",
c => new
{
Id = c.Int(nullable: false),
Data = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.NewWidgetBase", t => t.Id)
.Index(t => t.Id);
CreateTable(
"dbo.BarDetails",
c => new
{
Id = c.Int(nullable: false),
Data = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.NewWidgetBase", t => t.Id)
.Index(t => t.Id);
Im using Entity Framework code first and have recently created a new Repo model/table called ImportantCases.
I have set up the configuration and model just like every other however when i get to this line in my code:
public int CreateImportantCase(ImportantCase newImportantCase)
{
_context.ImportantCases.Add(newImportantCase);
_context.SaveChanges(); // <--- here
return newImportantCase.ImportantId;
}
I am getting this error:
Cannot insert the value NULL into column 'ImportantId', table
'MyDatabase.dbo.ImportantCases'; column does not allow nulls. INSERT
fails. The statement has been terminated.
My model/configuration look like this:
Model
public class ImportantCase
{
public int ImportantId { get; set; }
public int EvpId { get; set; }
public string Comment { get; set; }
public int CategoryId { get; set;}
}
EF Configuration
class ImportantCaseConfiguration : EntityTypeConfiguration<ImportantCase>
{
public ImportantCaseConfiguration()
{
HasKey(x => x.ImportantId);
}
}
Prior to calling the create method I am setting up the new ImportantCase via the Post method from the view using a view model and model binding:
if (imp != null ) // already a record, so update
{
imp.Comment = modifiedExceptionPersonViewModel.Comment;
imp.CategoryId = int.Parse(modifiedExceptionPersonViewModel.SelectedCategory);
_service.UpdateImportantCase(imp);
}
if (imp == null) //no record so create
{
ImportantCase newImportantCase = new ImportantCase();
newImportantCase.Comment = modifiedExceptionPersonViewModel.Comment;
newImportantCase.CategoryId = int.Parse(modifiedExceptionPersonViewModel.SelectedCategory);
newImportantCase.EvpId = modifiedExceptionPersonViewModel.EvpId;
_service.CreateImportantCase(newImportantCase);
}
I've inspected the newImportantCase object just before the SaveChanges and it looks as I would expect, with the ImportantId set to '0', usually EF will just create the ID once the write has completed.
One thing I have noticed however is the Identity Specification is set to No when I view the Design of that table, all the other tables that EF has created are set to Yes, what have I done differently?
note
EvpId is the Id of the viewmodel which is being returned from the view, I've just rolled the category and comment properties into this viewmodel in order to deal with them separately in the controller.
edit
I have just realised that I stupidly set the ImportantId to a string when I ran initially update-database but then added a new migration to rectify this later on, not realising EF does not retrospectively sort out the identity specification, is there a way around this?
I did the following to rectify this issue:
1.) Delete the problematic table through management studio.
2.) Make sure the model is amended to have the actual Id (the thing you want as the Identity Specification set to Yes for)
3.) Through the Package Manager Console create a new migration, something like:
add-migration "Set ImportantId as Identity Specification"
4.) If nothings changed you will see an empty Up() and Down() method
5.) Modify these with the contents of the migration that initially introduced this table but make sure you set the Id to an int this time, so this:
public partial class addedimportantcasetable : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.ImportantCases",
c => new
{
ImportantId = c.String(nullable: false, maxLength: 128),
EvpId = c.Int(nullable: false),
Comment = c.String(),
CategoryId = c.Int(nullable: false),
})
.PrimaryKey(t => t.ImportantId);
}
public override void Down()
{
DropTable("dbo.ImportantCases");
}
}
Is copied into the new migration, but slightly altered to this:
public partial class SetImportantIdasIdentitySpecification : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.ImportantCases",
c => new
{
ImportantId = c.Int(nullable: false, identity: true),
EvpId = c.Int(nullable: false),
Comment = c.String(),
CategoryId = c.Int(nullable: false),
})
.PrimaryKey(t => t.ImportantId);
}
public override void Down()
{
DropTable("dbo.ImportantCases");
}
}
In model ImportantCase change int to int?
public class ImportantCase {
public int? ImportantId { get; set; }
public int EvpId { get; set; }
public string Comment { get; set; }
public int CategoryId { get; set;} }
I have a problem with EF code first migration related to a lookup table and foreign keys. Let's say I have this two classes in my code:
public class Test
{
[Key]
public long Id { get; set; }
[Required]
public string Title { get; set; }
[Required, DisplayName("Test type")]
public TestType TestType { get; set; }
}
public class TestType
{
public int Id { get; set; }
public string Name { get; set; }
}
TestType is a typical lookup table and I usually fill them up in the Seed() method:
context.TestTypes.AddOrUpdate(
it => it.Name,
new TestType() { Name = "Drug" },
new TestType() { Name = "Educational" },
new TestType() { Name = "Other" }
);
When I create the table with the relationship I get the following migration:
CreateTable(
"dbo.TestTypes",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
AddColumn("dbo.Tests", "TestType_Id", c => c.Int(nullable: false));
CreateIndex("dbo.Tests", "TestType_Id");
AddForeignKey("dbo.Tests", "TestType_Id", "dbo.TestTypes", "Id", cascadeDelete: true);
Now, if I perform the migration of course I will get an error since the foreign key cannot be respected given the fact that the lookup table is still empty and the column created does not have a default value.
In DEVELOPMENT I am able to solve this by simply creating two migrations, the first one to create the lookup table and the second one to set the foreign key. If I run them separately then the Seed method after the first one will fill the table and I can tweak the column creation to pick up the values from the DB to prefill the column before creating the foreign key, a bit like this:
AddColumn("dbo.Tests", "TestType_Id", c => c.Int(nullable: false));
Sql("UPDATE dbo.Tests SET TestType_Id = (SELECT TOP 1 Id FROM dbo.TestTypes)");
CreateIndex("dbo.Tests", "TestType_Id");
AddForeignKey("dbo.Tests", "TestType_Id", "dbo.TestTypes", "Id", cascadeDelete: true);
Then when I run it everything works.
Now, in PRODUCTION I don't have the same luxury, since ALL the migrations are run before the Seed method is run, I will always have the same problem.
I know I could potentially run the migrations in stepped order on the production DB as well but that does not really solve the problem... Let's say a colleague of mine updates his working copy and runs the migrations, all will be run in order and he will encounter the error for sure.
I'm not sure on the current state of your database but I would define your models like this
public class Test
{
[Key]
public long Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
[ForeignKey("TestType")]
public int TestTypeId { get; set; }
[DisplayName("Test type")]
public virtual TestType TestType { get; set; }
}
public class TestType
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
Which results in the following migration when the tables don't exist already. I always find describing the foreign keys explicitly works better.
public override void Up()
{
CreateTable(
"dbo.Tests",
c => new
{
Id = c.Long(nullable: false, identity: true),
Title = c.String(nullable: false),
TestTypeId = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.TestTypes", t => t.TestTypeId)
.Index(t => t.TestTypeId);
CreateTable(
"dbo.TestTypes",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.Id);
}
The seed should then work fine as long as the Test table is empty?
I am using EF 4.3 and the migration script the come along.
But I have an issue with a property that does not get his field created.
public class Test {
[HiddenInput(DisplayValue = false)]
public int Id { get; set; }
[ScaffoldColumn(false)]
public string Author { get; set; }
[ScaffoldColumn(false)]
public DateTime UpdateUtc { get; set; }
}
When I run the command Add-Migration here is the code that is generate:
public override void Up()
{
CreateTable(
"Test",
c => new
{
Id = c.Int(nullable: false, identity: true),
Author = c.String(),
})
.PrimaryKey(t => t.Id);
}
My first thought was because of the ScaffoldColumn attribute but the Author field is correctly add. The only difference I see is that the type of the field UpdateUtc is not a primitive type.
What would cause this issue?
thanks
Try to add it by hand, using something like:
UpdateUtc = c.DateTime()
I think it should be it. Then update the database using Update command.
When you generate the database, please look at the name of the column generated and see if it is something like [UpdateUtc]. If so, then the name UpdateUtc si reserved and cannot be used.