C# - Foreign key references invalid table - Basic Migration not working - c#

Trying to make my first project in C#. However, it's very frustrating so far. This is a problem I can't solve, since I don't see anything wrong.
I'm trying to just do a simple migrate in my DB.
Users migration file
public override void Up()
{
CreateTable(
"dbo.Users",
c => new
{
user_id = c.Int(nullable: false, identity: true),
first_name = c.String(),
last_name = c.String(),
email = c.String(),
role = c.String(),
})
.PrimaryKey(t => t.user_id);
}
Locations migration file
public override void Up()
{
CreateTable(
"dbo.Locations",
c => new
{
loc_id = c.Int(nullable: false, identity: true),
loc_name = c.String(),
})
.PrimaryKey(t => t.loc_id);
CreateTable(
"dbo.UserLoc",
c => new
{
ul_id = c.Int(nullable: false, identity: true),
fk_user_id = c.Int(nullable: false),
fk_loc_id = c.Int(nullable: false),
})
.PrimaryKey(t => t.ul_id);
AddForeignKey("dbo.UserLoc", "fk_user_id", "dbo.Users", "user_id");
AddForeignKey("dbo.UserLoc", "fk_loc_id", "dbo.Locations", "loc_id");
}
Models:
User.cs
public class User
{
[Key]
public int user_id { get; set; }
public string firs_tname { get; set; }
public string last_name { get; set; }
public string email { get; set; }
public string role { get; set; }
}
Location.cs
public class Locations
{
[Key]
public int loc_id { get; set; }
public int loc_name { get; set; }
}
UserLoc.cs
public class UserLoc
{
[Key]
public int ul_id { get; set; }
[ForeignKey("Users")]
public int fk_user_id { get; set; }
public virtual User user { get; set; }
[ForeignKey("Locations")]
public int fk_location_id { get; set; }
public virtual Locations location { get; set; }
}
Every time I want to migrate I get the same error
Foreign key 'FK_dbo.UserLoc_dbo.Users_fk_user_id' references invalid
table 'dbo.Users'. Could not create constraint or index. See previous
errors.
What am I doing wrong?

First I recommend you to change the FK name that you are using in the ForeignKey attributes for the navigation property names:
public class UserLoc
{
[Key]
public int ul_id { get; set; }
[ForeignKey("user")]
public int fk_user_id { get; set; }
public virtual User user { get; set; }
[ForeignKey("location")]
public int fk_location_id { get; set; }
public virtual Locations location { get; set; }
}
This way you are telling it which navigation property represents the relationship it is a foreign key for.
After that, remove the old migration and try to run again Add-Migration command to regenerate it. You should see now that CreateIndex method is called before AddForeignKey method:
CreateIndex("dbo.UserLoc", "fk_user_id");
AddForeignKey("dbo.UserLoc", "fk_user_id", "dbo.Users", "user_id");
CreateIndex("dbo.UserLoc", "fk_loc_id");
AddForeignKey("dbo.UserLoc", "fk_loc_id", "dbo.Locations", "loc_id");

I had this issue recently.
I deleted all migrations, ran remove-migration a few times to clean up some of the default ASP.Net identity migrations, then recreated the migrations from scratch.
Entity Framework Core was confusing the default scaffolding of the IdentityUser class with my custom implementation, hence it could not find the table ASPNetUsers when I went to create the table that relates to it, in this case, Referrals.
Be sure to clear out any default migrations and run remove-migration when using EF Core with identity built into the database.
Hope this helps anyone that is confused.

Related

Code First Foreign Key error in Dependent and Principal Roles

Have been reading other SO posts on this for a while now and there's still something that isn't making sense. Making a MVC application that includes these two classes
public class ChecklistFilled
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int InternalChecklistFilledID { get; set; }
public DateTime ChecklistFilledDate { get; set; }
public int PersonnelID { get; set; }
[ForeignKey("PersonnelID")]
public virtual Personnel Personnel { get; set; }
public int EquipmentID { get; set; }
[ForeignKey("EquipmentID")]
public virtual ICollection<Equipment> Equipment { get; set; }
public virtual ItemsFilled ItemsFilled { get; set; }
}
public class ItemsFilled
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int InternalItemsFilledID { get; set; }
public bool? DeviceCondition { get; set; }
public int DeviceID { get; set; }
[ForeignKey("DeviceID")]
public virtual Device Device { get; set; }
public int ChecklistFilledID { get; set; }
[ForeignKey("ChecklistFilledID")]
public virtual ICollection<ChecklistFilled> ChecklistFilled { get; set; }
}
And the statement from my DBContext class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ItemsFilled>()
.HasRequired(x => x.ChecklistFilled)
.WithMany()
.Map(x => x.MapKey("ChecklistFilledID"));
base.OnModelCreating(modelBuilder);
}
So far I've found a lot of posts that have solutions that are fairly specific to the programmer's problem, and nothing that is straight forward enough for me to apply here as a newer developer. From what I've seen with the DBContext setup, I have it mapping the foreign key as needed from the ChecklistFilled class into the ItemsFilled class, but when I run the enable-migrations command, I get the error
ItemsFilled_Device_Target_ItemsFilled_Device_Source: : The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical
Which, taken at face value, is trying to tell me that I must have the same number of properties in each class - and that just sounds absurd to me. So I'm not sure what I need to do to solve this issue and place the ChecklistID into the ItemsFilled table as a FK
If I understood correctly, what you want is a relationship where a checklist is related to a list of items, and an item is related to one checklist.
To implement this one-to-many relationship you should redefine the properties in your entities like this:
public class ChecklistFilled
{
...
public virtual ICollection<ItemsFilled> ItemsFilled { get; set; }
}
public class ItemsFilled
{
...
public int CheckListFilledId { get; set; }
[ForeignKey("CheckListFilledId")]
public virtual ChecklistFilled ChecklistFilled { get; set; }
}
With this change you wouldn't even need an explicit mapping in OnModelInitialize. In fact, you should remove the mapping.
As a side note, in general it is better if the
base.OnModelCreating(modelBuilder); line is the first one in the method.
The tables generated by the migration look like this (I removed some non-relevant properties for clarity):
CreateTable(
"dbo.ChecklistFilleds",
c => new
{
InternalChecklistFilledID = c.Int(nullable: false, identity: true),
ChecklistFilledDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.InternalChecklistFilledID);
CreateTable(
"dbo.ItemsFilleds",
c => new
{
InternalItemsFilledID = c.Int(nullable: false, identity: true),
DeviceCondition = c.Boolean(),
CheckListFilledId = c.Int(nullable: false),
})
.PrimaryKey(t => t.InternalItemsFilledID)
.ForeignKey("dbo.ChecklistFilleds", t => t.CheckListFilledId, cascadeDelete: true)
.Index(t => t.CheckListFilledId);
Perhaps you should consider renaming your entities, the names are a bit confusing, specially when pluralized to compose the tables names. I would suggest FilledCheckList and FilledItem.
Also, the error you get states that the problematic relationship is ItemsFilled_Device_Target_ItemsFilled_Device_Source. It is about Devices and ItemsFilled. It seems that your code includes some annotation or mapping in the Device entity that is causing a problem.

Entity Framework one-to-zero-or-one foreign key association

I am in the process of changing the back-end of an existing application to use Entity Framework Code First. I've used the built-in tool in Visual Studio 2015 to generate POCO classes based on my existing database. This worked perfectly for the most part, except for two classes, with a one-to-zero-or-one relationship. These are my (simplified) classes:
public class Login
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int TeamMemberId { get; set; }
public virtual TeamMember TeamMember { get; set; }
}
public class TeamMember
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual Login Login { get; set; }
}
With the following configuration:
public class LoginTypeConfiguration : EntityTypeConfiguration<Login>
{
public LoginTypeConfiguration()
{
this.HasRequired(e => e.TeamMember)
.WithOptional(e => e.Login);
this.Property(e => e.TeamMemberId)
.HasColumnName("TeamMember_Id");
}
}
This results in the following migration:
CreateTable(
"dbo.Logins",
c => new
{
Id = c.Int(nullable: false, identity: true),
TeamMember_Id = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.TeamMembers", t => t.Id)
.Index(t => t.Id);
For some reason EF creates a foreign key on [Logins].[Id] instead of [Logins].[TeamMember_Id]. I've already tried decorating my navigation property with a ForeignKey attribute, but this did not work. Is there a way to get it to create a foreign key on [Logins].[TeamMember_Id] instead?
I ended up creating one-to-many relationship, with a [NotMapped] property for Login.
My classes:
public class Login
{
public int TeamMemberId { get; set; }
public virtual TeamMember TeamMember { get; set; }
}
public class TeamMember
{
[NotMapped]
public virtual Login Login
{
get { return Logins.FirstOrDefault(); }
}
public virtual ICollection<Login> Logins { get; set; }
}
With the following configuration:
public class LoginTypeConfiguration : EntityTypeConfiguration<Login>
{
public LoginTypeConfiguration()
{
this.Property(e => e.TeamMemberId)
.HasColumnName("TeamMember_Id");
}
}
For that you can write your code first like this. Entity Framework will generate foreign key TeamMemberId automatically for you. For more information: Entity Framework Tutorial -
Code First Conventions
public class Login
{
public int Id { get; set; } //foreign key declaration
public int TeamMemberId { get; set; }
public TeamMember TeamMember { get; set; }
}
public class TeamMember
{
public int TeamMemberId { get; set; }
public Ilist<Login> Login { get; set; }
}

Entity Framework table per hierarchy and one to one relation

I am having troubles implementing a table-per-hierarchy architecture in combination with a one-to-one relation in EF 6 code-first and SQL-Server. Entity Framework doesn't use the right column as foreign key.
I have a class Version and two inheriting classes ProductVersion and FeatureVersion.
public class Version
{
[Key]
public Int32 ID { get; private set; }
public Int32 Major { get; private set; }
public Int32 Minor { get; private set; }
public Int32 Patch { get; private set; }
..
}
The two inheriting classes implement nothing but the navigation properties.
public class ProductVersion : Version
{
[Required]
public virtual Product.Product Product { get; set; }
public Int32 ProductId { get; set; }
}
public class FeatureVersion : Version
{
[Required]
public virtual Feature Feature { get; set; }
public Int32 FeatureId { get; set; }
}
Now in my product I use a one-to-many relation and everything works fine.
public class Product
{
public Int32 ID { get; set; }
public String Name { get; set; }
public List<Versioning.ProductVersion> ProductVersions { get; set; }
...
}
In my feature I use a one-to-one relation and things get wrong.
public class Feature : ICloneable
{
public Int32 Id { get; set; }
...
public int FeatureVersionId { get; set; }
public virtual Entities.Versioning.FeatureVersion FeatureVersion { get; set; }
...
}
The resulting migration looks like this
CreateTable(
"dbo.Versions",
c => new
{
ID = c.Int(nullable: false),
...,
FeatureId = c.Int(),
ProductId = c.Int(),
Discriminator = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.ID)
.ForeignKey("dbo.Features", t => t.ID)
.ForeignKey("dbo.Products", t => t.ProductId, cascadeDelete: true)
.Index(t => t.ID)
.Index(t => t.ProductId);
As you see the foreign key to Features is using the wrong column.
When I change t => t.ID manually to t => t.FeatureId, cascadeDelete: true the foreign key gets inserted correctly into the database, but EF seems to have a problem inserting new features with an initial FeatureVersion; the new FeatureVersion's FeatureId column is always set to 0 and therefore leading to an exception (in the code I simply assign a new FeatureVersion object to the Feature object and try to save the context changes).
Everything works fine for Product with its one-to-many relation.
Interestingly when I change the reference in Feature from
public List<Versioning.ProductVersion> ProductVersions { get; set; }
to
public List<Versioning.Version> Versions { get; set; }
e.g. the mother class, everything works fine; in the migration I see the right columns joined and the INSERT works as well. But definitely, this is not what I want.
Is the inheritance somehow irritating EF? Am I missing an important point?
I also tried to change to relation for FeatureVersion to a one-to-many relation same as for Product. It works fine but this isn't the relation I need to use.

Composite Key in EF

I cant seem to understand how EF deals with composite keys. When I try to 'add-migration Initial' the below code returns "The property 'QuestionID ' cannot be used as a key property on the entity QuestionQuestionTypesModel' because the property type is not a valid key type. Only scalar types, string and byte[] are supported key types."
I also tried to set annotations instead of overriding OnModelCreating. I used [Key, Column(Order = 0)]
Can anyone give me any clues on what Im doing wrong? Or explain whats happening to better understand the problem at hand?
public class QuestionModel
{
[Key]
[HiddenInput(DisplayValue = false)]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
[Required]
[StringLength(250)]
public string Question { get; set; }
}
public class QuestionTypeModel
{
[Key]
[HiddenInput(DisplayValue = false)]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
[Required]
[StringLength(250)]
public string TypeName { get; set; }
}
public class QuestionQuestionTypesModel
{
public virtual QuestionModel QuestionID {get;set;}
public virtual QuestionTypeModel QuestionTypeID { get; set; }
}
public class InnuendoContext : DbContext
{
public IContext() : base("DefaultConnection")
{
}
public DbSet<QuestionTypeModel> QuestionTypes { get; set; }
public DbSet<QuestionModel> Questions { get; set; }
public DbSet<QuestionQuestionTypesModel> QuestionQuestionTypes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<QuestionQuestionTypesModel>().HasKey(a => new { a.QuestionID, a.QuestionTypeID });
}
}
You have to create the properties required for the table which are also the the foreign keys of the system. By setting this structure:
public class QuestionQuestionTypesModel
{
[Key, Column(Order = 1), ForeignKey("Question")]
public Guid QuestionID { get; set; }
[Key, Column(Order = 2), ForeignKey("QuestionType")]
public Guid QuestionTypeID { get; set; }
public virtual QuestionModel Question { get; set; }
public virtual QuestionTypeModel QuestionType { get; set; }
}
You get this migration:
public override void Up()
{
CreateTable(
"dbo.QuestionModel",
c => new
{
ID = c.Guid(nullable: false, identity: true),
Question = c.String(nullable: false, maxLength: 250),
})
.PrimaryKey(t => t.ID);
CreateTable(
"dbo.QuestionTypeModel",
c => new
{
ID = c.Guid(nullable: false, identity: true),
TypeName = c.String(nullable: false, maxLength: 250),
})
.PrimaryKey(t => t.ID);
CreateTable(
"dbo.QuestionQuestionTypesModel",
c => new
{
QuestionID = c.Guid(nullable: false),
QuestionTypeID = c.Guid(nullable: false),
})
.PrimaryKey(t => new { t.QuestionID, t.QuestionTypeID })
.ForeignKey("dbo.QuestionModel", t => t.QuestionID, cascadeDelete: true)
.ForeignKey("dbo.QuestionTypeModel", t => t.QuestionTypeID, cascadeDelete: true)
.Index(t => t.QuestionID)
.Index(t => t.QuestionTypeID);
}
Update
Just saw your comment. If you have just a many-to-many relationship and you don't need any other attribute, you can do this:
public class QuestionModel
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
[Required]
[StringLength(250)]
public string Question { get; set; }
//One question has many QuestionTypes
public virtual ICollection<QuestionTypeModel> QuestionTypes { get; set; }
}
public class QuestionTypeModel
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
[Required]
[StringLength(250)]
public string TypeName { get; set; }
//One QuestionType has many Questions
public virtual ICollection<QuestionModel> Questions { get; set; }
}
This will produce the same migration but makes your data layer clear.
modelBuilder.Entity<QuestionQuestionTypesModel>().HasKey(a => new { a.QuestionID, a.QuestionTypeID });
QuestionID and QuestionTypeID are both navigation properties and therefore can't be used as primary keys. It's as the Error message suggests: only these Datatypes are supported as primary keys (can be converted to key columns in the supported databases), unfortunately, QuestionModel and QuestionTypeModel are none of these.
Add Guid key values to match the key columns of QuestionModel and QuestionTypeModel.

Mapping subclass values in Entity Framework TPH

I'm setting up a TPH inheritance in C# MVC for the first time, and I'm a bit confused on how to map subclass values to the table. For example, my parent entity's fluent map is declared using:
public class ProjectTaskMap : EntityTypeConfiguration<ProjectTask>
I have individual maps set up using:
Map<ProjectTL>(m => m.Requires("Type").HasValue("TL"));
As a further example: One of the subclasses needs to have a many-to-many mapping. I don't know how to configure that with TPH since I can't access the child class's properties to declare the mapping.
I can't access the subclass properties in this parent map, however (since it's calling the config of ProjectTask). I can't specify how to map a field to the table, I can't do anything with them.
What am I missing to be able to do this? This is an old system that was just upgraded to EF 6.1, so I don't have EF Designer or anything like that, only these fluent mappings.
Parent class:
public class ProjectTask : BaseEntity
{
public virtual int ProjectId { get; set; }
// various other properties
public virtual Project Project { get; set; }
public virtual ICollection<ProjectTaskFile> Files { get; set; }
}
Two child classes have none of their own properties (they're empty shells), but the third does. ECOs is part of a many-to-many relationship.
public class ProjectET : ProjectTask
{
public virtual int SalesOrderId { get; set; }
public virtual SalesOrder SalesOrder { get; set; }
public ICollection<EngChangeOrders> ECOs { get; set; }
}
When creating your model with Code First, TPH is the default strategy for the types that participate in the inheritance hierarchy. Take a look at the following structure:
public class Project
{
public int ProjectId { get; set; }
}
public class ProjectTask
{
public int ProjectTaskId { get; set; }
public virtual Project Project { get; set; }
public string SomeString { get; set; }
}
public class ProjectET : ProjectTask
{
public ICollection<Order> ECOs { get; set; }
}
public class Order
{
public int OrderId { get; set; }
public string SomeString { get; set; }
}
Mapping:
public class Context : DbContext
{
public Context() : base("Model2")
{
}
public DbSet<Project> Projects { get; set; }
public DbSet<ProjectET> ProjectETs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasKey(i => i.ProjectId);
modelBuilder.Entity<ProjectTask>()
.HasKey(i => i.ProjectTaskId);
base.OnModelCreating(modelBuilder);
}
}
Migrations Generated
CreateTable(
"dbo.ProjectTasks",
c => new
{
ProjectTaskId = c.Int(nullable: false, identity: true),
SomeString = c.String(),
Discriminator = c.String(nullable: false, maxLength: 128),
Project_ProjectId = c.Int(),
})
.PrimaryKey(t => t.ProjectTaskId)
.ForeignKey("dbo.Projects", t => t.Project_ProjectId)
.Index(t => t.Project_ProjectId);
CreateTable(
"dbo.Orders",
c => new
{
OrderId = c.Int(nullable: false, identity: true),
SomeString = c.String(),
ProjectET_ProjectTaskId = c.Int(),
})
.PrimaryKey(t => t.OrderId)
.ForeignKey("dbo.ProjectTasks", t => t.ProjectET_ProjectTaskId)
.Index(t => t.ProjectET_ProjectTaskId);
CreateTable(
"dbo.Projects",
c => new
{
ProjectId = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.ProjectId);
Does that work for you?
Your fluent config can obviously only address a single entity type at a time, but there's nothing stopping you from adding fluent config for your subclass as well. Not sure what the complication is here.
public class ProjectETMap : EntityTypeConfiguration<ProjectET>
{
public ProjectETMap()
{
HasMany(m => m.ECOs).WithMany();
}
}

Categories

Resources