Entity Framework creating unwanted column - entity-framework

I have a project class which contains:
int ProjectID;
string ProjectName;
List<Item> Items;
I have a item class which contains:
int ItemID;
int? ParentID; // ID of Parent Item
string ItemName;
List<Item> Items;
I would expect my Items Table to hold the following columns:
ItemID, ParentID, ItemName
But for some reason it's adding another column ItemItemID.
I have tried several things with fluent API. (WithOptional, MapKey etc...) But I can't find a thing that is working for me. I think the problem is that the ParentID is not seen as the relation for the Item.
Please show me a solution, cause I'm already stuck for hours...

Entity Framework could not infer that the ParentID property is actually the foreign key to the parent Item object in the same table, so it decided to create a special column for that in the database. You have to tell it that ParentID is actually a foreign key by using the ForeignKey attribute.
Change the definition of the Item class to something like this:
public class Item
{
public Item()
{
Items = new HashSet<Item>();
}
public int ItemID { get; set; }
[ForeignKey("Parent")]
public int? ParentID { get; set; } // ID of Parent Item
public string ItemName { get; set; }
public virtual ICollection<Item> Items { get; set; }
public virtual Item Parent { get; set; }
}
Notice how I added a public virtual Item Parent property because the ForeignKey attribute needs to point to the property that presents the parent entity.
If you prefer to use the fluent API, then remove the ForeignKey attribute, and override the OnModelCreating method on the context class to something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder
.Entity<Item>()
.HasMany(e => e.Items)
.WithOptional(e => e.Parent)
.HasForeignKey(e => e.ParentID);
}

Related

FK to same table using nullable Id's

public class Email
{
public int Id { get; set; }
/// ...
public int? ReplyTo { get; set; }
public int? ForwardOf { get; set; }
}
I would like to configure ReplyTo and ForwardOf to be FK to Email.Id property with cascade Delete.
Tried this:
e.HasOne(nameof(Email.ReplyTo)).WithMany().OnDelete(DeleteBehavior.Cascade);
but it gives an error
The specified type 'System.Nullable`1[System.Int32]' must be a non-interface reference type to be used as an entity type.
I would prefer not to have navigation properties of type Email as they will never be used by my code.
This should allow a shadow navigation property:
.HasOne<Email>()
.WithMany()
.HasForeignKey(x => x.ReplyTo)
.OnDelete(DeleteBehaviour.Cascade);
Though I'm not sure you'd want a delete cascade on such a relationship.

EF zero-to-one FLUENT API Foreign Key on a NON Key

I have a legacy database which has broken all the rules of Codd. Here are the entities
class Item {
[Key]
public int ItemId {get;set;}
public string ItemNo {get;set; }
[ForeignKey("ItemId")]
public virtual NumericItem {get;set;} //navigation
}
class NumericItem { //This is a subset of the Item entity
[ForeignKey("ItemId")]
public Item Item {get; set;}
[Key]
public int ItemNo { get; set; } //this is a primary key, different type
public int ItemId { get; set; } //this is also a primary key and a foreign key
}
How do I tell EF Code first using Fluent API that NumericItem always has a Item and Item may or may not have a NumericItem. The cardinality is always zero/one
This is the case of the foreign unique key.
Normally, when you have a principal entity (like Item) and an optional dependent (NumericItem) in a relationship of 0 or 1, the dependent primary key is also the foreign key for the principal. In your case, since database is already like that, you could do like this:
public class Item
{
public int ItemId { get; set; }
public string ItemNo { get; set; }
public virtual NumericItem NumericItem {get;set;} //navigation
}
public class NumericItem
{ //This is a subset of the Item entity
public Item Item { get; set; }
public int ItemNo { get; set; } //this is a primary key, different type
}
public class NumericItemConfiguration : EntityTypeConfiguration<NumericItem>
{
public NumericItemConfiguration()
{
HasKey(n => n.ItemNo);
HasRequired(n => n.Item).WithOptional(i => i.NumericItem).Map(m => m.MapKey("ItemId"));
}
}
public class MyContextContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// do your stuff, and add your configuration here...
modelBuilder.Configurations.Add(new NumericItemConfiguration());
}
}
or you can do it without this NumericItemConfiguration class, doing the config directly in your OnModelCreating method:
public class MyContextContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// do your stuff, and add your configuration here...
modelBuilder.Entity<NumericItem>().HasKey(n => n.ItemNo);
modelBuilder.Entity<NumericItem>().HasRequired(n => n.Item).WithOptional(i => i.NumericItem);
}
}
Take note I had to remove your ItemId property from NumericItem class, otherwise EF would complain like this:
ItemId: Name: Each property name in a type must be unique. Property
name 'ItemId' is already defined.

EF 6 optional FK , delete still produces error

So i have these two simple models
public class Blog
{
public int BlogId { get; set; }
private string _Name;
[Column("sNameColumn")]
public string Name { get { return _Name; } set { _Name = value; } }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int? blog_id { get; set; }
[ForeignKey("blog_id")]
public virtual Blog Blog { get; set; }
}
I haven't done anything unusual at the dbContext definition. Then , i try to do something like this.
db.Blogs.Remove(db.Blogs.Find(2));
db.SaveChanges();
And i get a FK violation error.Notice that the FK blog_id is null-able, so i thought that EF whould handle the delete, and make the corresponding FK Null.
Can you tell me what i am missing?
The entities have to be loaded for EF to be able to handle setting their foreign key to null.
var b = db.Blogs.Find(2);
db.Entry(b).Collection(b => b.Posts).Load();
db.Blogs.Remove(b);
db.SaveChanges();
Keep in mind that Entity Framework can only update entities it has loaded.
Of course there are ways to update database records by raw SQL statements that you can also execute through EF, but that's not the core of EF as ORM.
So if you want to use EF only, you have no choice. You have to load collections in Blogs explicitly for them to get dissociated from their parent. For instance by Include:
var b = db.Blogs.Include(b => b.Posts).Include(b => b.Comments)
.Single(b => b.BlogId == 2);
db.Blogs.Remove(b);
db.SaveChanges();
Or by Load as in the other answer.
Another option could be to use Entity Framework Extented. One of its features is batch updates, which allows one-shot update statements of records in an IQueryable given a template record. This would look like this:
using EntityFramework.Extensions;
...
db.Posts.Where(p => p.BlogId == 2)
.Update(p => new Post { BlogId = default(int?) });
db.Blogs.Remove(b);
db.SaveChanges();
Only the properties that are set in the template record are modified. To make this transactional, you should wrap all statements in a TransactionScope.

Entity Framework TPH Inheritance Data Modeling Issues

I'm new to Entity Framework and C#/.Net and trying to create a TPH inheritance model, I'm not sure if I should be or not, so if not, please advise,
Here's the model:
public abstract class Vote
{
public int VoteID { get; set; }
public int UserID { get; set; }
public bool Value { get; set; }
public DateTime DateCreated { get; set; }
public User User { get; set; }
}
public class ProjectVote_ : Vote
{
public int ProjectID { get; set; }
public virtual Project Project { get; set; }
}
public class CommentVote_ : Vote //There are three more like this, votes for different hings
{
public int CommentID { get; set; }
public virtual Comment Comment { get; set; }
}
Now the Project model (comment and model is similar)
public class Project
{
public int ProjectID { get; set; }
public string Title { get; set; }
public virtual ICollection<Vote> Vote { get; set; }
}
What happens is that ICollection creates a database column Project_ProjectID as the foreign key in the Vote table (I think) instead of using the ProjectID I defined. How do I fix it or should I model it differently. If the fluent API is the way to fix it, I don't know how to do that.
In the end I want to be able to use one table to store 5 different types of votes.
When you have related entities you don't need to have a property to store the FK in your model. Entity framework knows that it needs to make a FK to the Project table in ProjectVote when it detects Project in your ProjectVote_ model. Same thing with User and UserId and Comment and CommentId. You don't need to have a property that stores the FK in your model.
You are getting the FK column with the name you don't like "Project_ProjectID" because Entity framework is detecting that it needs to create a FK for your navigation property "Project". It's using it's own naming convention to create the column hence "Project_ProjectID".
If you want to provide your own name for the column override OnModelCreating in your DBContext class and add this fluent mapping.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Project>()
.HasMany(p => p.Vote)
.HasRequired(v => v.Project) //or .WithOptional(v => v.Project)
.Map(m => m.MapKey("ProjectId")); //or any other name you want.
}
And for the future this is a helpful reference for how to use the Fluent API. For example here is some documentation on how to custimize TPH with fluent.
Hope that helps!

Self referencing / parent-child relationship in Entity Framework

I read quite a number of posts of programmers that run into the Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values -exception when using a self-referencing relationship in Entity Framework.
I am trying to get a parent-child relationship to work:
public class Category {
public int CategoryId { get; set; }
public string Name { get; set; }
public int ParentId { get; set; }
public Category Parent { get; set; }
public List<Category> Children { get; set; }
}
This is the configuration I use (Fluent API):
Property(c => c.ParentId).IsOptional();
HasMany(c => c.Children).WithOptional(c => c.Parent).HasForeignKey(c => c.ParentId);
//HasOptional(c => c.Parent).WithMany(c => c.Children).HasForeignKey(c => c.ParentId);
Both the HasMany() and HasOptional() configurations result in a "Unable to determine a valid ordering for dependent operations..." exception when I try to save a new category like this:
context.Categories.Add(new Category { Name = "test" });
I don't understand why EF doesn't insert the Category with a null parentId. The database allows the ParentId foreign key to be null.
Would you be able to tell me how to do this?
You must define the ParentId in the category class as nullable to use it as the foreign key property for an optional relationship:
public int? ParentId { get; set; }
An int property cannot take the value null and therefore cannot represent a NULL as value in a database column.
Since someone asked in a comment about doing this with attributes. You can also utilize data annotations to set this up. Using the same example as above:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Category {
// You can also add [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
// as an attribute, if this field is to be generated by the database
[Key] // Define this as the primary key for the table
public int CategoryId { get; set; }
public string Name { get; set; }
[ForeignKey(nameof(Parent))] // Link the Parent object to the ParentId Foreign Key
public int? ParentId { get; set; }
public Category Parent { get; set; }
public List<Category> Children { get; set; }
}
This is tested and works in EF 6.