Creating multiple self-referencing foreign keys to the same table - c#

I have a class like this:
[Table("Tree")]
public class Tree
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TreeId { get; set; }
public int? TreeOneId { get; set; }
[ForeignKey("TreeOneId")]
public virtual Tree TreeOne { get; set; }
public int? TreeTwoId { get; set; }
[ForeignKey("TreeTwoId")]
public virtual Tree TreeTwo { get; set; }
}
When I create the database from this class definition, I get the error:
Unable to determine the principal end of an association between the types 'Tree' and 'Tree'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
If I remove one of the properties, and create a class definition like this:
[Table("Tree")]
public class Tree
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int TreeId { get; set; }
public int? TreeOneId { get; set; }
[ForeignKey("TreeOneId")]
public virtual Tree TreeOne { get; set; }
//public int? TreeTwoId { get; set; }
//[ForeignKey("TreeTwoId")]
//public virtual Tree TreeTwo { get; set; }
}
It does create the correct tables. How can I get by this error? I've tried specifying the column order, adding a unique index. I can work around this by using a List TreeOne, and it will create the corresponding linking table, but I would rather not have to use that solution.

The basic problem here is, that SQL will not allow you to precisely define a binary tree.
In your approach it would be possible for a tree item to have multiple parents, since nothing prevents LeftId and RightId to point to the same reference or to have another tree item pointing to the same sub-tree.
We need a defined end-point for the navigation properties on Left/Right. This is the key to a solution: define a separate inverse navigation property for left/right child items and account for the possibility to have multiple parents.
With that in mind, lets set up the model for entity framework. Note that I replaced Tree by TreeItem, since its one entry per tree-node and used Left, Right instead of your property names out of lazyness.
public class TreeItem
{
public int Id { get; set; }
public int? LeftId { get; set; }
public int? RightId { get; set; }
[ForeignKey("LeftId")]
[InverseProperty("Parent1")]
public virtual TreeItem Left { get; set; }
[ForeignKey("RightId")]
[InverseProperty("Parent2")]
public virtual TreeItem Right { get; set; }
[InverseProperty("Left")]
public virtual ICollection<TreeItem> Parent1 { get; set; }
[InverseProperty("Right")]
public virtual ICollection<TreeItem> Parent2 { get; set; }
}
Also note: I tested only in EF6, so no guarantee to work with EF5

Related

EF 6 getting parent record from DB when table has many to many relation with itself

I am trying to build an organization hierarchy where each team might contain one or many members and/or one or many sub-teams.
To do so, my model is:
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Members { get; set; }
public virtual ICollection<Team> SubTeams { get; set; }
public Employee Manager { get; set; }
}
When adding a migration and updating database, everything seems logical in the table.
EF has added an extra nullable column "Team_Id" where the Id of the parent Team gets stored.
My question is about getting the Id of the parent Team from my model.
I tried adding:
public int? Team_Id
To my model, but EF considered it as a model change and asked for another migration.
How can I get the value of column Team_Id in my model? getting this info takes too much processing when looping through teams.
I always add foreign key in my model. When it adds to the model, EF won't add Team_Id .
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Members { get; set; }
public virtual ICollection<Team> SubTeams { get; set; }
public Employee Manager { get; set; }
public int? ParentId { get; set; }
[ForeignKey("ParentId")]
public Team ParentTeam { get; set; }
}
I hope this example be helpful.

1:1 self relation in EF

I have a object of type ScheduleItem
public class ScheduleItem : EntityBase
{
[MaxLength(500)]
[Required]
public string TitleAr { get; set; }
[MaxLength(300)]
[Required]
public string TitleEn { get; set; }
public int? ParentScheduleItemId { get; set; }
public int? PreviousDestinationId { get; set; }
public int? NextDestinationId { get; set; }
public ScheduleItem ParentScheduleItem { get; set; }
[InverseProperty("ParentScheduleItem")]
public ICollection<ScheduleItem> ChildrenScheduleItems { set; get; }
public ScheduleItem PreviousDestination { get; set; }
public ScheduleItem NextDestination { get; set; }
}
This object is the parent object, inside it there's a collection of children.
Each child has an optional reference to the next child (the last child will have NextDestinationId = null) and an optional reference to the previous child (PreviousDestinationId should be null for the first child).
Model Builder code:
modelBuilder.Entity<ScheduleItem>().HasOptional(t => t.NextDestination).WithMany().HasForeignKey(t => t.NextDestinationId);
modelBuilder.Entity<ScheduleItem>().HasOptional(t => t.PreviousDestination).WithMany().HasForeignKey(t => t.PreviousDestinationId);
However, when saving I get this error
unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
What's the solution to this problem? do I have to call SaveChanges() twice?
The solution is to rethink the relationship and structure. I have no comment on the parent/child relationship as you do not explain why a parent of the same type (a recursive relationship) is necessary.
The sibling relationship is not necessary, remove that all together and replace it with a SortOrder column of numeric type. The parent should then return a sorted list/collection based on this type. The schedule items should not have to know about the next/previous schedule items that are adjacent to them, let the parent worry about that.
So replace
public int? PreviousDestinationId { get; set; }
public int? NextDestinationId { get; set; }
public ScheduleItem PreviousDestination { get; set; }
public ScheduleItem NextDestination { get; set; }
with
// sort from low-to-high in the context of a collection
public int SortOrder { get; set; }

Deleting the child record deletes a parent record in entity

I am working on an asp.net application. I use Entity framework to represent my tables.
I have a model with two foreign keys. One of the seems to be giving me troubles as when I try to delete a record from the child model, the parent gets deleted as well.
Here are my models:
public class Class1
{
public Class1()
{
this.Children= new HashSet<Child>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public bool variable{ get; set; }
[ForeignKey("Class0")]
public long Class0_Id { get; set; }
public virtual CLass0 CLass0{ get; set; }
public virtual ICollection<Child> Children{ get; set; }
}
public class CHild
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public bool variable5{ get; set; }
[ForeignKey("CLass1")]
public long Class1_Id { get; set; }
public virtual Class1 Class1{ get; set; }
[ForeignKey("AnotherClass")]
public long AnotherClass_Id { get; set; }
public virtual AnotherClass AnotherClass{ get; set; }
}
my problem is that when I try to delete a record from Child class, the related Class0 record gets deleted as well
You don't have any other code posted, but I am going to take a guess anyway. Most likely, you are loading the child and the parent at the same time. Thus, the child has a reference to the parent. You may need to check the FluentAPI (Foreign Key) relationship between these two if you set it up elsewhere as it might be setup that the child is the parent instead.
The other thing you can do is right before you delete the child, just set Class1 property on the child to null. That should cause it to not automatically include it in the delete.

Entity Framework - Code First - Map results to Not Mapped properties

I have created these entities Product, Order, OrderedItem in EF using Code First.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
[NotMapped]
public int IssuedQuantity { get; set; }
[NotMapped]
public int InhandQuantity { get; set; }
public virtual ICollection<OrderedItem> OrderedItems { get; set; }
...
}
public class Order
{
public int Id { get; set; }
public string ReferenceNumber { get; set; }
public virtual ICollection<OrderedItem> OrderedItems { get; set; }
...
}
public class OrderedItem
{
public int OrderId { get; set; }
public string ProductId { get; set; }
[ForeignKey("OrderId")]
public virtual Order Order { get; set; }
[ForeignKey("ProductId")]
public virtual Product Product { get; set; }
...
}
Now I want to get all products by passing current user id to a stored procedure. It will then return all products along with total product quantity currently in user's hand.
The problem is that EF is not mapping SP results back to Product entity for NotMapped properties. i.e. all properties in product entity have values but NotMapped properties are set to NULL even when I return their values from SP.
What I want to ask is that does EF support this kind of functionality? If yes then how?
NOTE I know about Computed Properties but that will create unneccessary columns in tables and I don't want that, since these properties are calculated at run-time.
NOTE I know that I don't need to create OrderedItem entity. But I am storing some other properties in it, which are removed here for brevity.
I'm quite sure that EF does not support dynamic mapping (you could try to change the mapping metadata but is not a clean way or delete the mapping cache but then EF will be very slow). In this case the razionale is that the entity are 2 different entities because they have different data. In your case probably the best thing is to do 2 entities the ProductWithQuantities that inherits from Product.
BTW Thinking about ERPs, the model of orders/wms usually is different. Products does not contain informations about QtyOnHand or sales/buy information. Usually is another object (Inventory?) that contains this informations.
I would create a View Model of the product with all the required properties and pass that to the view instead of the Product model. Then you are not constrained by the mappings of the Product model and you do not have to use the [NotMapped] Attribute on the fields.
[NotMapped]
public class ProductVM
{
public int Id { get; set; }
public string Name { get; set; }
public int IssuedQuantity { get; set; }
public int InhandQuantity { get; set; }
public virtual ICollection<OrderedItem> OrderedItems { get; set; }
...
}
I hope that helps.

One to many relation to one table causes an error in EF modeling

I create a model in EF6 that contains several tables one of my tables is ContentGrouptable with this structure :
public partial class ContentGroup
{
public ContentGroup()
{
this.Contents = new HashSet<Content>();
this.ContentGroups = new HashSet<ContentGroup>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Visible { get; set; }
public long Ordering { get; set; }
public string CommentState { get; set; }
public int ContentGroupId { get; set; }
public virtual ICollection<Content> Contents { get; set; }
public virtual ICollection<ContentGroup> ContentGroups { get; set; }
public virtual ContentGroup ContentGroup1 { get; set; }
}
As you can see this table has a relation whit itself .One to many on Id column.It this table we have a FK-ContentGroupId that refers to itself(ContentGroup).So i create a view for this entity using MVC4(ASP) so when i want to insert a value to my table i got this error:
"Unable to determine a valid ordering for dependent operations. Dependencies may
exist due to foreign key constraints, model requirements, or store-generated
values."
So what should i do ?it because of FK?
Best regards
The problem is that you have a non-nullable ContentGroupId. This means that when you insert a ContentGroup, you have to set its parent ContentGroup as well. But this a chicken and egg problem (or: no "valid ordering"). You can't insert a parent and child at the same time, it's always a sequential operation. And then, there must always be a "mother of all parents", not having a parent itself.
So, make ContentGroupId nullable and map the associations like so:
modelBuilder.Entity<ContentGroup>()
.HasOptional(cg => cg.ContentGroup1)
.WithMany(cg => cg.ContentGroups)
.HasForeignKey(cg => cg.ContentGroupId);

Categories

Resources