1:1 self relation in EF - c#

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

Related

EF Core 2.0 - Navigation properties can only participate in a single relationship

Let's say I have these models:
public class Component
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public List<ComponentUpdate> Updates { get; set; }
public ComponentUpdate LastUpdate { get; set; }
}
public class ComponentUpdate
{
public int Id { get; set; }
public DateTime Timestamp { get; set; }
public Component Component { get; set; }
public string Message { get; set; }
}
The reason I'm saving the LastUpdate field instead of manually pulling it according to the highest 'TimeStamp' is because of speed. It would be faster to store a reference instead of checking the entire list every request.
When I'm trying to migrate the DB it throws an error saying I cannot have my properties participate in more than a single relationship.
I'm mapping the relationships in my context class and I don't think I'm doing it right since I have ComponentUpdate.Component mapped twice.
I've looked on several solutions but some were outdated and some just did not fit this scenario.
Thanks for helping.
Edit
Mapping accordingly:
modelBuilder.Entity<Component>().HasMany(c => c.Updates).WithOne(u => u.Component);
modelBuilder.Entity<ComponentUpdate>().HasOne(u => u.Component).WithOne(c => c.LastUpdate);

Independent child object with references from multiples parents in EF

I'm having quite the issue right now while trying to learn Entity Framework.
Let's say I have this entity:
public class BuildingGroup {
public int ID { get; set; }
public string NameOfManager { get; set; }
public virtual ICollection<Building> Buildings { get; set; }
}
And also this entity.
public class Architect {
public int ID { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public virtual ICollection<Building> BuildingsBeingWorkedOn { get; set; }
}
These two entities are completely unrelated. Here's the Building entity:
public class Building {
public int ID { get; set; }
public string Address { get; set; }
}
My problem happens when I try to add a building to, say a BuildingGroup. In my domain model, I can modify the equivalent collection of buildings, by adding, modifying or removing buildings. However, when I try to update BuildingGroup through a repository, the buildings will not be updated.
public void Update(BuildingGroup buildingGroup) {
var buildingGroupEntity = _context.BuildingGroups.Single(b => b.ID == buildingGroup.ID);
// This will not map the Building collection
context.Entry(buildingGroupEntity).CurrentValues.SetValues(buildingGroup);
// My attempt at mapping the buildings
buildingGroupEntity.Buildings.Clear();
buildingGroup.Buildings.ToList().ForEach(b => buildingGroupEntity.Buildings.Add(_context.Buildings.Single(x => x.ID == b.ID)));
_context.Entry(buildingGroupEntity).State = EntityState.Modified;
}
This fails if the building were not saved in the database prior to the call to Update(), which is normal since buildings can live independently. It must also be done for every child collection of BuildingGroup (if there were more), and for child collections of these children, well...
I have noticed other people use a foreign key constraint in the child object (here, Building), but I can't really do that since many unrelated entities can point to a building: I'd have a lot of navigation properties.
Is there a graceful way to manage referencing objects that can also live independently from those who hold references to them?
If all the entities have to exist independently, yet have relationships with each other, it's better to use many-to-many relationship.
Change your model classes as follows, the Building should contain a couple of collections for architects and groups.
public class BuildingGroup
{
public int ID { get; set; }
public string NameOfManager { get; set; }
public virtual ICollection<Building> Buildings { get; set; }
}
public class Architect
{
public int ID { get; set; }
public string Email { get; set; }
public string Name { get; set; }
public virtual ICollection<Building> BuildingsBeingWorkedOn { get; set; }
}
public class Building
{
public int ID { get; set; }
public string Address { get; set; }
public virtual ICollection<Architect> Architects { get; set; }
public virtual ICollection<BuildingGroup> BuildingGroups { get; set; }
}
If you use entity type configuration, you could define the relationship as follows:
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Building>().HasMany(it => it.Architects).WithMany(it => it.BuildingsBeingWorkedOn);
modelBuilder.Entity<Building>().HasMany(it => it.BuildingGroups).WithMany(it => it.Buildings);
base.OnModelCreating(modelBuilder);
}
}

How to check if an object is a child or a child of a child

I am coding a C# Web API 2 webservice where an object has a foreign key int reference to specify the parent object.
How is the best way to check that an object being edited is not a child of the same object, or is not a child of a child?
I am using EF6.
Here is an example of the DbSet object:
public class User : IDbSet, IParent
{
[Key]
public int id { get; set; }
public string name { get; set; }
public int parentId { get; set; }
}
Add a Parent navigation property:
public class User : IDbSet, IParent
{
[Key]
public int id { get; set; }
public string name { get; set; }
// notice that parent's id should be
// optional [at some point your parent will have to be null],
// thus we make the foreign key nullable
public int? parentId { get; set; }
[ForeignKey("parentId")]
public virtual User Parent { get; set; }
}
And then you can do your checks:
// I am not parent of myself
if(editedUser.Parent != editedUser) { ... }
or:
bool AmIIntheChain()
{
var curUserParent = editedUser.Parent;
while(curUserParent != null)
{
if(curUserParent == editedUser)
return false;
curUserParent = curUserParent.Parent;
}
return true;
}
This would need sanity checks not to make and endless loop depending on the data you may have, but you get the idea
You could also improve this by having also a Children property:
public class User : IDbSet, IParent
{
[Key]
public int id { get; set; }
public string name { get; set; }
public int? parentId { get; set; }
public virtual User Parent { get; set; }
public virtual ICollection<User> Children { get;set; }
}
And on your model configuration:
modelBuilder.Entity<User>()
.HasMany(t => t.Children)
.WithOptional(t => t.Parent)
.HasForeignKey(t => t.parentId);
So you could also check for children, not just "up the chain"

Creating multiple self-referencing foreign keys to the same table

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

Reflection To Update Source Entity with Children To Target With Children

For all you Reflection experts I need some help. I am working on a gneric method to be able to use hierarchical data assignments in an MVC application using CodeFirst and EF5. A user would apply changes to the parent object (source) and this method would populate the child object(s) (target). It's not working as I would expect. Any pointers would be greatly appreciated.
The below method "CopyEntity" is intended to take a source entity that has child entities and update a target entity of the same type and structure. We are only looking to update those entities that inherit a base type of 'Association' and also do not have a 'NoInherit' attribute on the members. Not every property would be updated from the 'Association'
public class ClassCode : Inheritable<ClassCode>
{
// Base Properties ----------------------------------
public int LobId { get; set; }
public virtual LOB LOB { get; set; }
public bool IsVirtual { get; set; }
//public string Name { get; set; }
public string Description { get; set; }
[Required(ErrorMessage = "Select Code")]
public string Code { get; set; }
//enable to force recreation of the database
public string IID { get; set; }
public virtual ICollection<ClassSubjectivityAssoc> Subjectivities{ get; set; }
}
public class ClassSubjectivityAssoc : Association
{
[Column("SubjectivityId")]
public override int AssociationId { get; set; }
[ForeignKey("AssociationId")]
public virtual Subjectivity Subjectivity { get; set; }
public int ClassCodeId { get; set; }
[ForeignKey("ClassCodeId")]
public virtual ClassCode ClassCode { get; set; }
}
public abstract class Association : BaseEntity
{
public DateTime Start { get; set; }
public DateTime? End { get; set; }
public bool IsCurrent { get; set; }
public int Order { get; set; }
public bool BreakInheritance { get; set; }
public int? InhId { get; set; }
public virtual ClassCode InhClass { get; set; }
public int CodeId { get; set; }
[ForeignKey("ClassCodeId")]
public virtual Code ClassCode { get; set; }
public abstract int AssociationId {get; set;}
}
public class BaseEntity
{
private static DateTime CREATE_DATE
{
get { return DateTime.Now; }
}
[NoInherit]
public int Id { get; set; }
[NoInherit]
public string CreatedBy { get; set; }
[NoInherit]
public DateTime Created { get; set; }
[NoInherit]
public string LastModifiedBy { get; set; }
[NoInherit]
public DateTime LastModified { get; set; }
public bool IsActive { get; set; }
}
protected void CopyEntity(Inheritable<T> source, Inheritable<T> target)
{
Type sourceType = source.GetType();
Type targetType = target.GetType();
// ensure that the types can be assigned to one another
//if (!targetType.IsAssignableFrom(sourceType)) return;
// loop through the properties of the source system
foreach (PropertyInfo sourceMember in sourceType.GetProperties().Where(sourceMember => sourceMember.GetCustomAttribute(typeof(NoInheritAttribute)) == null))
{
var targetMember = targetType.GetProperty(sourceMember.Name);
// if the property doesn't exist in the target, let's get out
if (targetMember == null) continue;
// check if this property is a Collection
bool isCollection = (sourceMember.PropertyType.IsGenericType && sourceMember.PropertyType.GetInterfaces().Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IEnumerable<>)));
// if we are not dealing with associations, let's get outta herre
if (!(isCollection
? sourceMember.PropertyType.GetGenericArguments().Single()
: sourceMember.PropertyType)
.IsSubclassOf(typeof(Association))) continue;
if (isCollection)
{
IEnumerable<Association> targetCollection = (IEnumerable<Association>) targetMember.GetValue(target);
// Loop through the collection and evaluate
foreach (Association sourceAssociation in (IEnumerable) sourceMember.GetValue(source))
{
Association targetAssociation =
targetCollection.SingleOrDefault(t => t.AssociationId == sourceAssociation.AssociationId);
CopyAssociation(sourceAssociation, targetAssociation);
}
}
else
{
Association sourceAssociation = (Association) sourceMember.GetValue(source);
Association targetAssociation = (Association) targetMember.GetValue(target);
CopyAssociation(sourceAssociation, targetAssociation);
}
}
}
The problem is: It does not seem to be traversing the child objects properly and does not update the target as I would have expected. Looking for input and suggections as I am not as proficient in Reflection as I would like.
UPDATE: 12/06/2012
Ok, So I believe I've found the problem but still looking for a solution. This problem came to me from another dev on my team with the assumption that the object model was sound. However, it seems that the entity that we are currently testing does not retrieve the seeded values from the db.
There is nothing defined in the OnModelCreating() method defining this association although the tables are being properly created from the entity model and the data is being loaded properly from the dbSeed() method. I can run a select statement from the db and get the proper data. It seems that the entity model foreign key associations might be invalid and thus preventing the correct data retrieval. Any help would be greatly appreciated.
UPDATE: Ok, finally retrieving the correct data. This finally did it.
modelBuilder.Entity<ClassSubjectivityAssoc>()
.HasRequired(c => c.ClassCode)
.WithMany(u => u.Subjectivities)
.HasForeignKey(f => f.ClassCodeId);
I'm not really sure if I understand the question, but it seems that you just need to make sure that sourceMember is either an Association (or descendant) or a collection of Association.
You could do that in a quick and dirty way with casting the resulting value to Association, and checking if the cast succeeded, like this for simple properties:
Association sourceAssociation = sourceMember.GetValue(source) as Association;
if (sourceAssociation != null) // the source is a valid Association
{
Association targetAssociation = targetMember.GetValue(target) as Association;
if (targetAssociation != null) // the destionation too
{
CopyAssociation(sourceAssociation, targetAssociation);
}
}
and with similar code for the collections as well
The method you have shown will not descend a object tree, so it will only work on Association objects that are directly attached to the Entity.
See original post "UPDATES" for solution.

Categories

Resources