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
Related
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;
I am using EF6 with Generic Repository pattern. Recently I experienced a problem trying to delete a composite entity in a single go. Here is a simplified scenario:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("Parent")]
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
For deleting the Parent entity with related Children I am doing something like this:
public virtual T GetById(int id)
{
return this.DBSet.Find(id);
}
public virtual void Delete(T entity)
{
DbEntityEntry entry = this.Context.Entry(entity);
if (entry.State != EntityState.Deleted)
{
entry.State = EntityState.Deleted;
}
else
{
this.DBSet.Attach(entity);
this.DBSet.Remove(entity);
}
}
First I find the parent object by ID and then pass it to the delete method to change it's state to deleted. The context.SaveChanges() finally commits the delete.
This worked fine. The find method only pulled up Parent object and Delete worked since I have a cascade on delete enabled on Children.
But the moment I added another property in Child class:
[ForeignKey("Gender")]
public int GenderId { get; set; }
public virtual Gender Gender { get; set; }
For some reason EF started pulling related Children on the Parent.Find() method. Because of this I get the following error:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
Even after reverting the changes (removing the Gender property) the problem still exists. I am not able to understand this weird behavior!!
All I want to do is Delete the Parent object along with the Children.
There are some solutions around it but none really serves my purpose:
Turn LazyLoading to false - this.Configuration.LazyLoadingEnabled = false; This works but in my real application I need this property to true.
Iterate all children first and Delete them and then delete the Parent. This seems at best a workaround and is very verbose.
Use Remove() rather than just changing the EntityState to Deleted. I need to track Changes for Auditing so EntityState helps there.
Can someone explain why EF is loading related Entities even when I am not using them?
It seems that the problem was related to the life-cycle of context. I am using Unit Of Work and injecting it into my service layers using ninject.
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
The UnitOWork class implements IDisposable.
public bool DeleteView(int viewId)
{
// This is a workaround. It seems ninject is not disposing the context.
// Because of that all the info (navigation properties) of a newly created view is presisted in the context.
// Hence you get a referential key error when you try to delete a composite object.
using (var context = new ApplicationDbContext())
{
var repo = new GenericRepository<CustomView>(context);
var view = repo.GetById(viewId);
repo.Delete(view);
context.SaveChanges();
}
//var model = _unitOfWork.CustomViews.GetById(viewId);
//_unitOfWork.CustomViews.Delete(model);
//_unitOfWork.Save();
return true;
}
The commented code throws and error, while the un-commented one (using block) works. A controller method before this call loads the CustomView entity (which is of a similar structure as Parent with a list of children). And a subsequent user action can be triggered to delete that view.
I believe this has something to do with the context not being disposed. Maybe this has something to do with Ninject or UnitOfWork, I haven't been able to pin-point yet. The GetById() might be pulling the whole entity from context cache or something.
But the above workaround works for me. Just putting it out there so that it might help somebody.
I was trying to create a generic method to update an Entity and all it's collection properties from a detached object. For example:
public class Parent{
public int Id { get; set; }
public string ParentProperty { get; set; }
public List<Child> Children1 { get; set; }
public List<Child> Children2 { get; set; }
}
public class Child{
public int Id { get; set; }
public string ChildProperty { get; set; }
}
So, my first intention was to use something like this:
Repository<Parent>.Update(parentObj);
It would be perfect have a magic inside this method that update Parent properties and compare the list of Children of the parentObj to the current values in database and add/update/remove them accordingly, but it's too complex to my knowledge about EF/Reflection/Generic... and so I tried a second more easier way like this:
Repository<Parent>.Update(parentObj, parent => parent.Children1
parent => parent.Children2);
This method would be a little harder to use, but yet acceptable. But how I think the second parameter had to be params Expression<Func<TEntity, ICollection<TRelatedEntity>>>[] relatedEntities I had problems to specify multiple TRelatedEntity. So my try was to 3rd step with no success yet...
Now I tried to call a method to update Parent and a sequence of methods to update Childreen, like this:
Repository<Parent>.Update(parentObj);
Repository<Parent>.UpdateChild(parentObj, parent => parent.Id, parent => parent.Children1);
Repository<Parent>.UpdateChild(parentObj, parent => parent.Id, parent => parent.Children2);
And the code:
public virtual void Update(TEntity entityToUpdate)
{
context.Entry(entityToUpdate).State = EntityState.Modified;
}
public virtual void UpdateChild<TRelatedEntity>(TEntity entityToUpdate, Func<TEntity, object> keySelector, Expression<Func<TEntity, ICollection<TRelatedEntity>>> relatedEntitySelector) where TRelatedEntity: class
{
var entityInDb = dbSet.Find(keySelector.Invoke(entityToUpdate));
var current = relatedEntitySelector.Compile().Invoke(entityToUpdate);
var original = relatedEntitySelector.Compile().Invoke(entityInDb);
foreach (var created in current.Except(original))
{
context.Set<TRelatedEntity>().Add(created);
}
foreach (var removed in original.Except(current))
{
context.Set<TRelatedEntity>().Remove(removed);
}
foreach (var updated in current.Intersect(original))
{
context.Entry(updated).State = EntityState.Modified;
}
context.Entry(entityInDb).State = EntityState.Detached;
}
First problem was to get original values, because when I call dbSet.Find the entity is already in context (context.Entry(entityToUpdate).State = EntityState.Modified;).
So I tried to change order calling first Child:
Repository<Parent>.Update(parentObj);
Repository<Parent>.UpdateChild(parentObj, parent => parent.Id, parent => parent.Children1);
Repository<Parent>.UpdateChild(parentObj, parent => parent.Id, parent => parent.Children2);
And now I have the error:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
In summary, it would be very nice the first way, but I would be satisfied with the second/third too.
Thanks very much
Edit 1
Please, I need a native solution or using Automapper (which we already use in the project), because my customer don't like external dependencies and if we need to adapt something to the project, like working with Attached objects to update their related entities, so GraphDiff mencioned in the comments doesn't fit our needs (and VS 2015 RC crashed when I tried to install the package for tests)
Have you considered getting the object from the DB and using AutoMapper to modify all the property values?
I mean:
var obj = GetObjectFromDB(...);
AutoMapObj(obj, modifiedObj);
SaveInDb();
So perhaps I'm addressing this problem the wrong way, but I wanted to get the opinion from you fine people on StackOverflow about how to more correctly do this.
I've got a program that has to retrieve information from a repository around an Entity Framework 6.0 code-first context, do some work on the information contained and then it adds a new record to the database.
Anyway, here's the simplified look at the class I'm retrieving from EF through the repository:
public class Product
{
public int Id { get;set; }
public virtual ProductCategory Category { get;set; }
public string Name { get;set; }
}
I then build a ProcessedProduct with the following definition and pass in the previously retrieved Product as the BaseProduct:
public class ProcessedProduct
{
public int Id { get;set; }
public virtual Product BaseProduct { get;set; }
}
I use a repository layer that I saw on an EF lesson on Pluralsight and have purposed here. I've added all the relevant bits below:
public class MyContext : BaseContext<MyContext>, IMyContext
{
//Lots of IDbSets for each context
public void SetModified(object entity)
{
Entry(entity).State = EntityState.Modified;
}
public void SetAdd(object entity)
{
Entry(entity).State = EntityState.Added;
}
}
public class MyRepository : IMyRepository
{
private readonly IMyContext _context;
public MyRepository(IUnitOfWork uow)
{
_context = uow.Context as IMyContext;
}
public ProcessedProduct FindProcessedProduct(int id)
{
return _context.ProcessedProducts.Find(id);
}
public ProductCategory FindCategory(int id)
{
return _context.Categories.Find(id);
}
public int AddProcessedProductWithoutProduct(ProcessedProduct newRecord)
{
newRecord.Product = null;
Save();
return newRecord.Id;
}
public int UpdateProcessedProductWithProductButWithoutChildProperties(int processedProductId, int productId)
{
var processedProduct = FindProcessedProduct(processedProductId);
processedProduct.BaseProduct = FindProduct(productId);
processedProduct.BaseProduct.Category = null;
_context.SetModified(product);
Save();
return processedProduct.Id;
}
public int UpdateProductChildren(int processedProductId, int categoryId)
{
var processedProduct = FindProcessedProduct(processedProductId);
var category = FindCategory(categoryId);
processedProduct.BaseProduct.Category = category;
_context.SetModified(product);
Save();
return processedProduct.Id;
}
}
And finally, here's the portion that pulls it all together:
try
{
//Create the processed product without the product instance
var processedProductId = repo.AddProcessedProductWithoutProduct(finishedProduct);
//Now, update this processed product record with the product. This way, we don't create a
//duplicate product.
processedProductId = repo.UpdateProcessedProductWithProductButWithoutChildProperties(processedProductId, product.Id);
//Finally, update the category
processedProductId = repo.UpdateProductChildren(processedProductId, product.Category.Id);
//Done!
}
When I attempt to insert this ProcessedProduct into EF, it correctly creates the ProcessedProduct record, but it also creates a new Product and new Category row. I've tried manually changing the change tracking for each object so ProcessedProduct would be 'added' and the others would be either 'modified' or 'unchanged', but I would get foreign key reference exceptions thrown by Entity Framework.
My "fix" was to simply break this up into a number of different calls:
I create the new ProcessedProduct record, but I assign the Product value to null.
I query for that ProcessedProduct record with the Id, query for the appropriate Product with its Id and assign that Product to the newly retrieved ProcessedProduct record. However, I have to null out the Category property or else this will add a new duplicate Category record. I save and the ProcessedProduct record is modified.
Finally, I query the ProcessedProduct once more as well as the ProductCategory and then assign that ProductCategory to the Category property of the ProcessedProduct.BaseProduct. I can save once more and now I've created all the records I need without making any of the duplicates.
However, this approach seems quite convoluted since all I originally wanted to do is save the new parent record and simply not create duplicate child records. Is there a better way to go about doing this that I'm missing? Thanks!
Edit: And I guess the larger question is say I have a complex object with a whole bunch of these child complex objects. What's the easiest way to create a new parent without having to go through the entire graph of child objects to update the parent with them one layer at a time?
I highly recommend not setting Product & Category as navigation properties when editing. As you saw when you add the graph of processed product (with a product & category attached) to the EF context, it's marking everything in the graph as added and does inserts on everything.
The pattern I always recommend (and Nikolai also suggested in his comment, so up-vote his comment like I did :)) is to include the FK IDs in your entity and set those values, not the navigations. e.g.
newRecord.ProductId=theProductIdValue.
I've had many people cry "but foreign keys? ewwww! They will make my classes so dirty and impure!" but after they see how much easier it is to code things without tangling with the navigations in these scenarios, they have come back to say "okay, it was worth it!"
BTW if you are talking about my EF in the Enterprise course, I have a whole module about dealing with this problem...it's called something bout graphs in disconnected scenarios. :)
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).