If I have the below class:
public class Foo()
{
public int PropertyIWantUpdated {get; set;}
public int PropertyIDontWantUpdated (get; set}
public ICollection<Bar> Bars {get; set;}
}
When saving to my db, instead of
context.Entry(thisFoo).State = EntityState.Modified;
I'm using
context.Entry(thisFood).Property(tf => tf.PropertyIWantUpdated).IsModified = true;
how can I also save changes to Bars?
It depends what you are trying to update. First of all let me clarify one important fact - if you have detached entity graph (more entities with relation) and you want to pass all changes to EF you are responsible for telling EF what has changed in every entity and every relation - EF will not help you with it.
If you are trying to update just Bar
instances and you didn't change relations (= you didn't add new Bar
to Foo
or removed Bar
from Foo
) you just need to iterate Bars
and set them to Modified
state.
If you also changed content of Bars
collection the whole process become really complicated and the approach depends on the way how you defined your entities = If you are using independent or foreign key association.
In case of foreign key association (Bar
has FK as property = in Bar
you have something like FooId
) you follows similar approach as at the beginning. You iterate Bars
and set state to:
Modified
if an existing Bar
was assigned to Foo
Added
if a new Bar
was assigned to Foo
There is one big issue. If you removed some Bar
instances from Bars
collection you must also attach them to the context and set their state accordingly:
Modified
if FK should be set to null
Deleted
if Bar
should be deleted
This all is only for one-to-many relations.
If you thought that previous approach was complicated be prepared that in case of independent association (Bar
doesn't have FK property - always the case for many to many relations) the process is even worse. Independent associations have their own object tracking the state = setting the state on Bar
entity doesn't persist new relation. The first problem is that this object is not directly accessible from DbContext API - you must convert DbContext
to ObjectContext
and use ObjectStateManager
to get the access to ObjectStateEntry
representing the relation. After that you must correctly set its state which is not as easy as it looks like because relation cannot be in Modified
state - it can be only in Unchanged
, Added
or Deleted
. It means that if you changed the Bar's relation from one to another Foo
you must first find the old relation and set it as deleted and then you can set the new relation as added. If you have many-to-many relation and you also want to add, delete and update related objects (not only relations) this can be really "big fun" - especially the fact that you must somewhere keep the information what has changed to be able to set all states correctly.
More discussion about this problem (global in EF) is here - it is not related to the DbContext API but because the new API is just wrapper of the old ObjectContext API same problems remain.
Do you think it is feasible? I don't think so. Because of that you should try to avoid this. There are some ways how to avoid it:
- Make changes on attached object graph - it means that you will first attach the original state of the entity graph to the context and then you will do all your changes.
- Load the original object graph and manually merge all changes from the new graph to the loaded (and attached) one.
- In case of ObjectContext API you can use Self-tracking entities which are able to track the state and automatically set everything with applied to the context. They have some other disadvantages and limitations (for example they don't support lazy loading) and they are not available for DbContext API.