NHibernate Cascade.None still updates parent entity - c#

I have some entities (for sake of simplicity it looks like that):
class DbEntity
{
public Guid Id {get;set;}
}
class BaseEntity: DbEntity
{
public string Name {get;set;}
public ParentEntity Parent {get;set;}
}
class ParentEntity: BaseEntity
{
List<BaseEntity> Children {get;set;}
}
class ChildEntity: BaseEntity
{
}
Now, my fluent mappings look like that:
class DbEntityMap<T>: ClassMap<T> where T: DbEntity
{
public DbEntityMap()
{
Id(x => x.Id).GeneratedBy.GuidComb();
}
}
class BaseEntityMap: DbEntityMap<BaseEntity>
{
UseUnionSubclassForInheritanceMapping();
Map(x => x.Name).Not.Nullable();
References(x => x.Parent).Cascade.None();
}
class ParentEntityMap: SubclassMap<ParentEntity>
{
Abstract();
HasMany(x => x.Children)
.ForeignKeyCascadeOnDelete()
.Inverse()
.Not.KeyUpdate()
.Cascade.None()
.LazyLoad();
}
class ChildEntityMap: SubclassMap<ChildEntity>
{
Abstract();
//some other stuff
}
Now, I have client desktop application that sends request to WebAPI.
The first request is to create all entities, like for example:
ParentEntity parent = new ParentEntity();
parent.Name = "New name";
ChildEntity child1 = new ChildEntity();
ChildEntity child2 = new ChildEntity();
parent.Children.Add(child1);
parent.Children.Add(child2);
//of course children know their parent:
//child.Parent = parent;
Now I am building some dto from that (because my model is much more complicated and creating a dto is good idea) to build a json, and send this json to WebAPI. Entities in database are created properly. Great.
But now I have to modify some child entity, for example:
child1.SomeValue = newValue;
Now, while creating DTO I don't add parent entity to that, just parent id and it looks more less like that:
class ChildDto
{
public Guid DbId {get;set;}
public Guid ParentDbId {get;set;}
public int SomeValue {get;set;}
}
Now, when my WebAPI receives such dto it recreates a model like that:
ParentEntity fakeParent = new ParentEntity();
fakeParent.Id = childDto.ParentDbId;
ChildEntity child = new ChildEntity();
//assign other child values and then parent:
child.Parent = fakeParent;
Now when I do Update(just session.Update(obj, id)) my child entity updates properly, BUT parent entity updates also. As I didn't set Name property in parent entity, Name field in database becomes empty.
I thought that setting Cascade to None would prevent NHibernate from updating parent entity. But no Cascade setting works. If I set the parent binding to:
References(x => x.Parent).ReadOnly()
then my ChildEntity is updated without parent id.
I know at least couple of solutions for that like:
1. Transform also whole parent entity in DTO - but this can make other cascade updates and if not - there will be two updates instead of one.
2. Select parent entity just before update - but this creates unnecessary SELECT.
What I would like to achieve is:
- update ONLY childEntity - without updating parent nor any other entity.
How can I acomplish that?
[Some words about actual application]
In reality my ParentEntity could hold BaseEntities. ParentEntity and ChildEntity have also some common properties.
What's more ChildEntity can hold other DbEntities.
Children of child entity also hold other DbEntities, it looks like that:
ParentEntity -> ChildEntity -> EntityA -> EntityB -> EntityC
(like a tree)
(every derives from DbEntity).
And I even managed to solve the problem using session.Merge, but when I try to UPDATE for example EntityA the problem is the same - ChildEntity loses connection with ParentEntity :|

I thought that setting Cascade to None would prevent NHibernate from updating parent entity. But no Cascade setting works
You are expecting that Cascade.None will prevent updating associations. Though this is true, this is not the case in your code. The parent entity is being updated because of your strange inheritance hierarchy. I do not understand need of multi-level inheritance. If you remove BaseEntity, what you are willing to achieve will happen without need to call Load.
The scenario you are working is explained here:
10.4.2. Updating detached objects
Many applications need to retrieve an object in one transaction, send it to the UI layer for manipulation, then save the changes in a new transaction. ........
This approach requires a slightly different programming model to the one described in the last section. NHibernate supports this model by providing the method ISession.Update().
You should also have look at this:
10.10. Lifecycles and object graphs
The precise semantics of cascading operations are as follows:
If a parent is saved, all children are passed to SaveOrUpdate()
If a parent is passed to Update() or SaveOrUpdate(), all children are passed to SaveOrUpdate()
If a transient child becomes referenced by a persistent parent, it is passed to SaveOrUpdate()
If a parent is deleted, all children are passed to Delete()
If a transient child is dereferenced by a persistent parent, nothing special happens (the application should explicitly delete the child if necessary) unless cascade="all-delete-orphan" or cascade="delete-orphan", in which case the "orphaned" child is deleted.
Notice the highlighted point above. Your both ParentEntity and ChildEntity derive from BaseEntity. Further, BaseEntity holds the association property ParentEntity Parent. When you Update child entity, your parent entity is passed to SaveOrUpdate. As you have not specified Name there, it updates the database with empty name.
If I set the parent binding to:
References(x => x.Parent).ReadOnly()
then my ChildEntity is updated without parent id.
This is obvious; you explicitly set it to read only.

You should session.Load existing objects that you don't want to update (do not confuse it with session.Get - it wouldn't issue SELECT statement for lazy entities).
So you need to change your DTO -> Entity conversion logic to something like this:
ParentEntity fakeParent = session.Load<ParentEntity>(childDto.ParentDbId);
ChildEntity child = new ChildEntity();
//assign other child values and then parent:
child.Parent = fakeParent;

Related

Intermittent Issue With Entity Framework Creating Unwanted Rows

I have an MVC application that uses Entity Framework v6. We have a class
public class ChildObject
{
public string Name { get; set; }
....
}
that maps to a table in the database. This table has 6 rows that are never changed. Neither will there ever be any additions. We have a second class defined along the lines of the following:
public class ParentClass
{
public int ChildObjectId { get; set; }
public ChildObject ChildObject { get; set; }
....
}
Whenever a ParentClass object is created or updated the logic only references the ChildObjectId property. The ChildObject property is only referenced when data is pulled back for viewing. However about once per month an extra row appears in the ChildObject table that is a duplicate of an existing row. This obviously causes issues. However I can't see how this could happen seeing as we only ever save using the Id value. Any thoughts on how this could be occurring would be very much appreciated.
The typical culprit for behavior like you describe is when a new child entity is composed based on existing data and attached to the parent rather than the reference associated to the context. An example might be that you load child objects as a set to select from, and send the data to your view. The user wants to change an existing child reference to one of the 6 selections. The call back to the server passes a child object model where there is code something like:
parent.ChildObject = new ChildObject{ Name = model.Name, ... }
rather than:
var child = context.Children.Single(x => x.Id = model.ChildObjectId);
parent.ChildObject = child;
Depending on how your domain is set up you may run into scenarios where the EF context creates a new child entity when a navigation property is set. Check with a FindUsages on the ChildObject property and look for any use of the setter.
In general you should avoid combining the use of FK properties (ChildObjectId) with navigation properties (ChildObject) because you can get confusing behavior between what is set in the navigation reference vs. the FK. Entities should be defined with one or the other. (Though at this time EF Core requires both if Navigation properties are used.)
A couple notables from your example:
Mark the navigation property as virtual - This ensures that EF assigns a proxy and recognizes it.
Option A - Remove the FK child ID property. For the parent either use an EntityTypeConfiguration or initialize the DbContext to map the FK column:
EntityTypeConfiguration:
public class ParentClassConfiguration : EntityTypeConfiguration<ParentClass>
{
public ParentClassConfiguration()
{
ToTable("ParentTable");
HasKey(x => x.ParentObjectId)
.Property(x => x.ParentObjectId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.ChildObject)
.WithMany()
.Map(x => x.MapKey("ChildObjectId"));
.WillCascadeOnDelete(false);
}
}
or on context model generation: (Inside your DbContext)
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ParentObject>().HasRequired(x => x.ChildObject).WithMany().Map(x => x.MapKey("ChildObjectId")).WillCascadeOnDelete(false);
}
or Option B - Ensure the FK is linked to the reference, and take measures to ensure that the two are always kept in sync:
public class ParentClassConfiguration : EntityTypeConfiguration<ParentClass>
{
public ParentClassConfiguration()
{
ToTable("ParentTable");
HasKey(x => x.ParentObjectId)
.Property(x => x.ParentObjectId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.ChildObject)
.WithMany()
.HasForeignKey(x => x.ChildObjectId));
.WillCascadeOnDelete(false);
}
}
or on context model generation: (Inside your DbContext)
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ParentObject>().HasRequired(x => x.ChildObject).WithMany().HasForeignKey(x => x.ChildObjectId)).WillCascadeOnDelete(false);
}
Option B is the only one currently available with EF Core to my knowledge, and it may help mitigate your issue but you still have to take care to avoid discrepancies between the navigation property and the FK. I definitely recommend option A, though it will likely require a bit of change if your code is commonly accessing the FK column.

Cascade Update in Entity Framework

I have the following scenario involving 2 classes:
public class Parent
{
[Key]
public int Id {get;set;}
//.. Other properties here
public virtual IList<Child> Children {get;set;}
}
and
public class Child
{
[Key]
public int Id {get;set;}
public int ParentId {get;set;}
//.. Other properties here
[ForeignKey("ParentId")]
public virtual Parent {get;set;}
}
I also have an DbContext with the associated DbSet Children and DbSet Parents and I want to make the following update operation:
//.. Get some Parent instance -> convert it to ParentVM -> do some operations on ParentVM in the Service //layer () -> then try to update it back using EF:
// parentVM now contains a modified version both in the primitive properties and also in the children collection: some children have new values
var parent = ConvertBackToORMModel(parentVM); //converts everything back, including the Children collection
using (var context = new ApplicationDbContext())
{
context.Set<Parent>().AddOrUpdate(parent);
//context.Set<Child>().AddOrUpdate(childModified); // If I do this here, it saves also the modified children back in the DB; but I want this to be **automatic when updating the parent**
context.SaveChanges(); //here, the primitive modified properties are saved in DB, the modified children collection remains the same
}
The problem is that, the above code snippet is generic, which means that I would need to iterate, depending on the object through the Children collection (or all virtual collections, etc.) and call context.Set().AddOrUpdate(childModified); for each one. I want this behavior to be automatic when updating the parent.
Is there any way to do this?
Thanks,
Ionut
I believe entity framework does not have cascade update feature,
but I know hibernate has it.
however you could do something like overwritething method savechanges() in ApplicationDbContext class
similar to cascade delete mentioned here
ApplicationDbContext : DbContext
{
public override int SaveChanges()
{
//sets child in ram memory entity state to modified
//if its parent entity state is modified each time you call SaveChanges()
Child.Local.Where(r => Entry(r.Parent).State == EntityState.Modified)
.ToList().ForEach(r => Entry(r).State=EntityState.Modified);
base.SaveChanges();
}
}
I think this is what you are looking for, I have not tested this

Removing orphans with one-to-many

I have the following code:
public class Parent
{
public int ParentId {get;set;}
public ICollection<Child> Children {get;set;}
}
public class Child
{
public int ChildId {get;set;}
public Parent Parent {get;set;}
}
It's mapped as one-to-many by EF without any additional efforts. When I replace Children with new collection (with 3 another items) I have old orphan entities in child-table like this:
Id | Parent_Id
1 NULL <-- orphan
2 NULL <-- orphan
3 NULL <-- orphan
4 1 <-- new
5 1 <-- new
6 1 <-- new
I want to get rid of them with identifying relationship explicitly:
modelBuilder.Entity<Child>().HasKey(x => new { x.ChildId, x.ParentId });
modelBuilder.Entity<Parent>().HasMany(x => x.Children).WithRequired().HasForeignKey(x => x.ParentId);
But I don't have ParentId property in children. I have only "Parent" pointing directly to the parent entity. If I try
modelBuilder.Entity<Child>().HasKey(x => new { x.ChildId, x.Parent });
I get an error message:
The property ... cannot be used as a key property on the entity ... because the property type is not a valid key type
Is there any way remove all unnecessary orphans?
As far as I know for identifying relationship you will need the parents primary key contained as part of the primary key of the child. EF will then allow you to remove the child from collection and delete it for you.
Just having the navigation property won't work. Identifying relationships are a DB level concept so needs an exposed Primary key id
See my question here on that subject
In my application I have a similar model. However, instead of removing orphans I do not produce them. If I want to remove some child objects I do it in the following way:
//This line attaches a parent object to the session. EF will start monitoring it.
Entry(parent).State = EntityState.Modified;
//This code is responsible for finding children to delete.
foreach (var child in parent.Children.Where(ch => ch.Deleted))
//This line says EF that given child should be removed.
//This line also causes that an object will be removed from Children collection.
Entry(child).State = EntityState.Deleted;
SaveChanges();
Deleted is a property which I set to true if I want to delete an object.

How to not repeat data access rules in .NET MVC3 using EF (Repository pattern)?

I have 2 classes with one-to-many like this:
public class Parent
{
public virtual List<Child> Children {get; set;}
}
I am also using repository classes for each model,
ie:
Parent has it's own repo class that has a Get() function that can gets an ordered list of Parents.
Child also has it's own repo class tha thas a Get() function that can gets an ordered list of Children that have a status of Active.
The problem is, the way EF works, since Parent has a reference to children, EF loads Children automatically when the parent list is loaded, but it gets active and inactive Children (also, the children list is not ordered). The check for active status is only in the Child repo.
How can I limit my repetition of "get" rules to be used wherever Children are accessed.
I know I can just modify the Parent repo to also check for Child status=Active, but then I am repeating my logic in every class that has children...
Edit:
I have a Repository class for Child that returns an Ordered list of Children:
public List<Children> GetList()
{
IQueryable<Children> query = context.Set<Children>();
return query.OrderBy(a=>a.Seq).ToList();
}
That works fine when I just need to access the Children directly. But when I need to get a Parent class, I use the repo function below:
ParentRepo GetByID:
public Parent GetByID(object id)
{
IQueryable<Parent> query = context.Set<Parent>();
Parent parentModel = context.Parents.Where(a => a.ParentId == (int)id)
.Select(a => new
{
Parent = a,
Children = a.Children.OrderBy(b => b.Seq)
}).ToList()
.Select(q => q.Parent)
.Single();
return parentModel;
}
As you can see, I had to order the Children here as well (by Seq).
This is the only way I can think to make this work, but it doesn't seem right.
public Parent GetByID(object id)
{
IQueryable<Parent> query = context.Set<Parent>();
Parent parentModel = context.Parents.Single(a => a.ParentId == (int)id);
parentModel.Children = childRepo.GetList(id);
return parentModel;
}
The repository pattern abstracts the data access layer, so that the rest of the application doesn't depend on DAL implementation details. The repository should be constructed according to the application needs. Basically you should design the repository interface from the application point of view and the repository should return domain/business objects.
If I understand correctly what you're doing, you're trying to build repositories around EF which gives you this kind of problems.
I might've misunderstood and it might be simply a EF related problem but it really seems to me that the problem is the wrong design of the repository.

Entity Framework 4.1 RTW Code First - Does POCO one to many need reference to child entity and child entities primary key?

In a situation where you have a parent class which has one child class, what is the best pattern for mapping the entities.
I have seen a lot of suggestions where the parent has a reference to both the child class, and the child class id. E.g.
public class Parent
{
public int Id
{
get;
set;
}
public int ChildId
{
get;
set;
}
public virtual Child Child
{
get;
set;
}
}
public class Child
{
public int Id
{
get;
set;
}
}
public class ParentMapping : EntityTypeConfiguration<Parent>
{
public ParentMapping()
{
HasKey(x => x.Id);
HasRequired(X => x.Child)
.WithMany()
.Map(x => x.ToTable("Parent")
.MapKey("ChildId"));
}
}
With this pattern, when saving the parent, if you want to swap out the child for a different but existing child, examples I have seen just update the ChildId and not the Child which feels wrong because the object is out of sync with itself.
The code looks neater without the ChildId but with this pattern I am having trouble saving the parent using an existing child because EF is trying to save a new child.
public class Parent
{
public int Id
{
get;
set;
}
public virtual Child Child
{
get;
set;
}
}
What is the best pattern, I would like to know if the ChildId is needed, then how is the Child property kept in sync and will it be lazy loaded from the database or not.
This is difference between foreign key and independent association. When using foreign key association you can really use just key without loading the related object. It makes the reference out of sync if you have it loaded - which is not always the case. If you want to keep the reference in sync you are almost back in the situation which you must solve with independent association.
If you expose foreign key you should use it because it makes a lot of things much more easier. If you use the independent association you should do something like:
var parent = GetUpdatedParentSomehow();
// Dummy object for the old child if the relation is not loaded
parent.Child = new Child { Id = oldChildId };
// Attach the parent
context.Parents.Attach(parent);
// Create dummy for new child (you can also load a child from DB)
var child = new Child { ID = newChildId };
// No attach the child to the context so the context
// doesn't track it as a new child
context.Childs.Attach(child);
// Set a new child
parent.Child = child;
// Set parent as modified
context.Entry(parent).State = EntityState.Modified;
context.SaveChanges();
There is very strange part where I'm creating dummy for the old child. I'm almost sure that if I don't do it before attaching the parent and setting the new child I will get some exception during saving changes (in case of independent association).

Categories