Issue with Entity Framework TPT hierarchy and relationships - entity-framework

my current EF6 implementation goes somewhat like this:
public class Parent
{
public int Id { get; set; }
public virtual string Name { get; set; }
public string Description { get; set; }
public virtual ICollection Children { get; set; }
public int OtherEntityId { get; set; }
public virtual OtherEntity OtherEntity { get; set; }
}
public class Child : Parent
{
public int Id { get; set; }
public override string Name
{
get { return Parent == null ? "Dummy" : Parent.Name; }
}
public string Description { get; set; }
public ParentId { get; set; }
public virtual Parent { get; set; }
public override int OtherEntityId
{
get { return Parent == null ? default(int) : Parent.OtherEntityId; }
}
}
The fluent configuration for the Child goes like this:
this.ToTable("Child")
.HasRequired(x => x.Parent)
.WithMany(x => x.Children)
.HasForeignKey(xy => xy.ParentId);
The idea is to have one entry for every Parent and Child in my Parent table.
Yet, the Child entity should not have a name of its own, but get the name from it's parent.
I also need a way to navigate from every child to its parent.
And I also need a way to navigate from every parent to its children.
This only goes over 1 level.
The reason for this implementation was due to the requirement to always get certain values from the parent, yet the Child must have the same public interface as the Parent.
Keep in mind that I can't change the Parent here, it is part of a framework I have to stick with. So only the child is actually part of my implementation.
The problem seems to be with the change tracking.
If I query for a Child, EF loads the Child first, checks all properties, and loads the Parent second. Since now the name is different, for EF the name property already changed, even though I have not changed it manually.
The solution works somewhat, as long as I load the all entities without tracking. The moment I try to load them with tracking enabled I get an exception:
A referential integrity constraint violation occurred:
A primary key property that is a part of referential integrity
constraint cannot be changed when the dependent object is Unchanged
unless it is being set to the association's principal object.
The principal object must be tracked and not marked for deletion.
Same happens if a try to attach the entity after loading, and set its state to Unchanged.
I do not change primary keys, or any keys at all, so it is not clear to me, why this error happens.
It seems that the error happens during the relationship fixup.
I fear its the "OtherEntityId" but I can't verify it, since I have no idea how to debug it.
Anyone has ideas what the problem might be or how I can debug that properly?
I tried to get the internal values with something like:
https://www.exceptionnotfound.net/entity-change-tracking-using-dbcontext-in-entity-framework-6/
But
ChangeTracker.Entries().Where(p => p.State ==
EntityState.Modified).ToList();
also seams to force a relationship fixup which leads to the error.

Related

Deleting association between one or zero to one entities with EntityFramework

I have entities set up something like this:
public class MyThing
{
public int Id { get; set; }
public virtual MyOtherThing { get;set; }
}
public class MyOtherThing
{
public int Id { get; set; }
public virtual MyThing MyThing { get; set; }
}
My intention is that 'MyThing' can have one or none of MyOtherThing, and I also want a navigation link from MyOtherThing to it's parent.
I have configured the following EntityBaseConfiguration for the 'MyOtherThing' entity:
this.HasOptional(x => x.MyThing)
.WithOptionalPrincipal(x => x.MyOtherThing);
I can assign and modify MyOtherThing to MyThing no problem, but when I want to unassign 'MyOtherThing' from 'MyThing', how do I do this?
I tried the following:
myThing.MyOtherThing = null;
and then editing the entity by setting the EntityState.Modified state, but this didn't remove the association between the entities.
I tried adding the following to my MyThing entity, but this resulted in an EF 'Multiplicity is not valid' error when updating my database model:
public int? MyOtherThingId{ get; set; }
Thanks in advance!
I tried the following:
myThing.MyOtherThing = null;
If you want to remove an optional dependent entity (here: MyOtherThing) from a principal entity (here MyThing) by setting it to null, you have to pull the entity from the database with the dependent entity included, for example:
var mything = context.MyThings.Include(m => m.MyOtherThing)
.Single(t => t.Id == idValue);
(It's also OK when the belonging MyOtherThing is loaded into the context later, for example by lazy loading).
Without Include, myThing.MyOtherThing already is null and EF doesn't detect any change. Note that the statement myThing.MyOtherThing = null; doesn't execute lazy loading, which is a bit confusing because with collections the behavior is different.
By the way, the dependent entity can also be removed from the database directly, which is more efficient.
var ot = context.Set<MyOtherThing>().Find(idValue);
context.Set<MyOtherThing>().Remove(ot);
context.SaveChanges();

An alternative way of implemening navigation properties in Entity Framework

The official approach to defining navigation properties for complex entities is:
public class SuperEntity
{
public int Id { get; set; }
//Other properties
}
public class LowerEntity
{
public int Id { get; set; }
public int SuperEntityId { get; set; }
public virtual SuperEntity SuperEntity { get; set; }
//Other properties
}
The main thing here is that a class that references (allows navigation to linked super entity) has both public SuperEntity SuperEntity { get; set; } property, as well as it's Id in public int SuperEntityId { get; set; }.
I have gone a few days into my entities design ommiting the public int SuperEntityId { get; set; } property in the "lower entities". So I am navigating only by virtual SuperEntity property. And everything works fine! But I had people on SO telling me that it creates an excessive tables in the DB. I've checked, and that is not true. When I use my approach, the DB tables has the SuperEntityId column and just populates it with the referenced entity Id automatically. What's the point in this public int SuperEntityId { get; set; } field then?
Or, perhaps, what I am doing became available in a "fresh" versions of EF like 4.3?
The point of SuperEntityId is that it is sometimes easier to use a foreign key property in apps where your context isn't alive the entire time, e.g. a webapp.
In such a situation, it's a lot easier to just use a foreign key property, than to try to attach object B to object A.
As far as I know, with nav properties, EF uses an object to track the relation between 2 objects. So if you want to couple object B to object A, in a disconnected app, it's not enough to just set the property on object A, you also have to fiddle with the entry of object A in the changetracker to register the relation between B and A.
Setting a foreign key property is the equivalent of this fiddling.
When we were just beginning with EF and didn't know about all of this, every time we wanted to connect 2 objects, e.g. B to A, and B already existed in the DB, the context thought that B was a new object instead of an existing one, and duplicated the record in the DB.
It won't create excessive tables, but it will probably generate extra, or longer, queries on that database. But that depends on how you're using these entities.

EF code first related entities not loading at all

I haven't been able to find someone else with this issue specifically so here goes.
I have a simple model where one entity simply references another as a parent-child or one-to-many relationship defined like this:
public class Parent
{
public int ID { get; private set; }
public string Name { get; private set; }
}
public class Child
{
public int ID { get; private set; }
public string Name { get; private set; }
public virtual Parent Parent { get; private set; }
}
I am creating speicific mapping files for each, which work great for all the normal properties except for the related entity. It is always coming up null. No matter whether i have the virtual/private accessors on the property it will not load UNLESS i pull a copy of the parent separately from the context first. My mapping looks like this:
HasRequired(t => t.Parent).WithMany().Map(t => t.MapKey("ParentID")).WillCascadeOnDelete();
Is there anything I am doing wrong with this? I cannot for the life of me figure this out. Just so I cover all the bases, I am loading the entity like this:
Context.Set<Child>().FirstOrDefault(x => x.ID == 1);
And lastly here are some constraints I have:
I cannot have the foreign keys in my model as properties.
I cannot have a collection of children from the parent.
I finally figured it out. After much trial and error I noticed that having a parameterless constructor marked as internal, EF cannot create its dynamic proxy class of your type and therefore disables all lazy loading. I have two contructors, one for EF to hydrate objects, and another with parameters requires for callers to create my entity. Once I changed the signature to protected internal it started working. So I changed this:
internal Child() {}
to
protected internal Child() {}
May be you hasn't enable lazy loading .Try this,
Context.Set<Child>().FirstOrDefault(x => x.ID == 1).Include(c=>c.Parent);

Entity Framework Code First update error with unloaded required navigation property

I found out that, if i set a navigation property as required ( using the Required attribute ) and use lazy-loading proxies, when i load the parent entity and do nothing but try to save in the same context, an EntityValidationError occurs which is like "xxx field is required".
With hibernate in Java and NHibernate in .NET, it is possible to just fetch an entity without its navigation properties ( all lazy loaded ) , update it and save again. The framework realizes that nothing changed with the navigation references and do not throw any errors.
The example is below
[Table("Address")]
public class Address
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AddressId { get; set; }
[Required, StringLength(512)]
public virtual string AddressLine1 { get; set; }
}
[Table("User")]
public class User
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
[Required]
public string Name {get; set;}
[Required]
public virtual Address Address {get; set;}
}
void LoadAndSaveUser() {
var user = Context.Users.First();
user.Name = "foo";
// if i comment out this line
// ( probably EF fetches the lazy loaded entiy )
// the code works. it is strange though because i don't access any property/method of the Address
// var dummy = user.Address
Context.SaveChanges();
}
When i try this without the "Required" attribute on the Address property, no error occurs. With the Required attribute, i get "Address field is required!". Since each user should have an address, i want the attribute to create a consistent model.
In some forums, i found posts suggesting to include the navigation property while loading the parent entity ( eager load in other words ) but it is not a feasible approach if we have too many navigation properties.
Am i doing something wrong or is there any other way to implement such funcionality ?
Define public int AddressId in your User class. It loads whenever a user is loaded. I think that will solve the problem.
Update:
Well, I have reproduced your problem. You have a couple of options to prevent this. But one thing's for sure. You have to remove the [Required] attribute from your Address property. This is the source of your problem (which is rational since you are telling EF "I want the Address object (not the foreign key but the navigation property) to be present. if it's null, throw an exception").
Also, define AddressId property of type int in your User class.
Notice that you can also define public virtual ICollection<User> Users { get; set; } in your Address entity.
You can tell EF about required attribute either by putting [Required] on AddressId, or by using fluent api:
in your user-mapping class:
this.HasRequired(t => t.Address)
.WithMany()
.HasForeignKey(d => d.AddressId);

How can I have Entity Framework return related objects with some defaults?

Say I have Project and Task EF Code first classes
public class Project
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
}
public class Task
{
public int ID { get; set; }
public string Name { get; set; }
public int ProjectId { get; set; }
public bool IsDeleted {get; set;}
public virtual Project Project { get; set; }
}
Say I have
public void SomeAction()
{
Project p= repository.GetById(1);
var tasks = p.Tasks;
//var tasks = p.Tasks.Where(t=>t.IsDeleted==false);
}
I would like that my Tasks property on the Project class will always perform that filter on IsDeleted and just return that subset ... to avoid having to write that condition all over the place...
Any recommendations?
Edit:
Im using EF Code First
Add a discriminator to your model in the OnModelCreating method
modelBuilder.Entity<TEntity>().Map(m => m.Requires("IsDeleted").HasValue(false));
Caveats
You can no longer load deleted items (unless you map IsDeleted true to another entity, then you may lose your automatic filtering)
The poco class cannot have the IsDeleted property (discriminators cannot be mapped)
because the IsDeleted cannot be mapped you need to run raw SQL to delete the entity in the first place.
EF Code first = NO WAY. Just one from long list of features which is available in EDMX and it is completely missing in code first. Mapped condition from EDMX does this but it is still problematic because it is hardcoded and cannot be changed (= you will never be able to load deleted entities even if you want to unless you use another EDMX). The solution would be implementation of global filters in EF but EF doesn't have anything like that despite the fact that old Linq-to-entities have them at least for relations (DataLoadOptions.AssociateWith).
This is much more painful in relations where you cannot use eager or lazy loading without loading deleted entities to your application as well and do filtering in your application's memory.
In the Model Designer, select your Task entity, and bring up the Mapping Details window. This should show you the database table your entity is mapped to, and all the columns. Just under where it says "Maps to [YourTable]" you should see an option <Add a Condition>. This should let you set a condition like what you're looking for.