views:

520

answers:

2

I'm trying to create a method on an EF 4 POCO repository called AddOrUpdate.

The idea is that the business layer can pass in a POCO object and the persistence framework will add the object if it is new (not yet in the database), else will update the database (once SaveChanges() is called) with the new value. This is similar to some other questions I have asked about EF, but I'm only about 80% there in understanding this so please forgive partial duplication.

The part I'm missing is how to update the object graph in my ObjectContext/associated ObjectSet for the passed-in business object once I have determined that the business object indeed already exists in the database (and now has been loaded thanks to TryGetObjectByKey). ApplyCurrentValues sounds sort of like what I want, but it only copies scalar values and doesn't seem intended to update the object graph in the ObjectContext/ObjectSet. Because of my particular use case I don't care about concurrency right now.

public void AddOrUpdate(BO biz)
{
    object obj;

    EntityKey ek = Ctx.CreateEntityKey(mySetName, biz);
    bool found = Ctx.TryGetObjectByKey(ek, out obj);

    if (found)
    {
        // How do I do what this method name implies?  Biz is a parent with children.
        mySet.TellTheSetToUpdateThisObject(biz);
    }
    else
    {
        mySet.AddObject(biz);
    }

    Ctx.DetectChanges();
}
A: 

You have to write code for this. There is nothing built in which copies an entire graph. As you say, ApplyCurrentValues will work fine for the scalar values. The children you'll have to copy yourself.

Craig Stuntz
@Craig: So if I load a POCO parent that has children (and possibly grandchildren, etc) through EF 4, change some of the child objects (modify existing ones, add new ones, delete some), I have to manually update the object graph in the ObjectContext?
Eric J.
No. If you load from the context and the entities do some form of change tracking, this is automatic. It's only when you copy from a disconnected entity that you're on your own.
Craig Stuntz
@Craig: I did load from the context originally, but found that my changes were not being applied on the call to SaveChanges(). What change tracking does a POCO object need to do? And if it has to implement a change tracking mechanism, is it still really a POCO object?
Eric J.
Eric, "pure" POCOs do no change tracking, and hence are not well-suited for tracking changes easily. :) The EF can track changes to POCOs using snapshots, self-tracking entities, or proxy types. You can read about these options and their advantages and disadvantages in the docs: http://msdn.microsoft.com/en-us/library/bb738470.aspx You may be looking for `DetectChanges`, but like I said, you'll want to read through the section on POCO change tracking.
Craig Stuntz
@Craig: Thanks for trying to help. I have been trying to pour through the MSDN documentation and various blogs, but just can't seem to grasp exactly how EF 4 does and does not work. From the link you send `By default, the object context calls this method before saving the data to the data source` implies to me that `SaveChanges()` will call `DetectChanges()`, but I'll try explicitly calling to see if that helps.
Eric J.
Pure POCOs are much harder to understand than non-POCO methods.
Craig Stuntz
@Craig: POCO seems to be very much an afterthought based on popular demand, which is unfortunate. (N)Hibernate has worked great for me in the past, but I really want to understand "the Microsoft way" now that it appears to be gaining maturity. Thanks again for your help. I'll update the post with my results.
Eric J.
If you are used to Fluent NHibernate then try the POCO Proxy method, which is closer to FNH's "psuedo-POCOs" than pure POCOs with no change tracking are.
Craig Stuntz
A: 

The problem was that my business objects were using the new() operator to allocate child objects, rather than constructing the object from the ObjectContext. Not really POCO behavior if you can't use new(), but at least it's working after some refactoring.

Eric J.