3

I am trying to attach an object tree to my Entity Framework context that was supplied via a WCF service call. The object in question has a collection of child items, and each child item has a property that is another tracked object type. However, there is only a small set of these last items, so most of the collection items share them. Something like this (except, obviously, with EntityObjects):

public class Product
{
  public int Id;
  public string Name;
  public decimal Cost;
}

public class Order
{
  public int Id;
  public List<Detail> details;
}

public class Detail
{
  public int Id;
  public Product Product;
  public int Quantity;
}

Whenever I have an Order that has more than one Detail for the same Product, the Entity Framework complains during the attach because it's trying to attach multiple copies of the same Product key. This information is coming in from a WCF ServiceOperation, so it's being deserialized as discrete instances of Product, even though in the client they are a set of shared objects.

Is there any way to tell the EF object context to re-use the tracked entities when the Attach happens? Note that I'm not attaching Products directly, so tricks like checking TryGetObjectStateEntry won't work.

Any suggestions?

EDIT:

I ran across the following article about self-tracking entities (which I have switched over to using), which includes a couple of client-side options that can be used as alternatives to Slauma's answer, which keeps the reconciliation code on the server side. (The self-tracking entities eliminate the need to use Attach() but the issue of duplicate keys still exists.)

http://blogs.msdn.com/b/diego/archive/2010/10/06/self-tracking-entities-applychanges-and-duplicate-entities.aspx

Michael Edenfield
  • 28,070
  • 4
  • 86
  • 117

1 Answers1

1

No, there is no way. You basically need to make the object references unique per key before you attach, something like:

void PrepareForAttach(Order order)
{
    var dict = new Dictionary<int, Product>();
    foreach (var detail in order.Details)
    {
        Product firstProduct;
        if (dict.TryGetValue(detail.Product.Id, out firstProduct))
            detail.Product = firstProduct;
        else
            dict.Add(detail.Product.Id, detail.Product);
    }
}

EF maps key identities to object reference identities and it doesn't allow to have two or more objects with the same key in the context. Nor does it have a feature to make the references unique which would mean to select somehow one of your products with the same key as the correct product for that key. (You could have two products with the same key but different other properties. Which is the "correct" product for that key? EF refuses to decide that (and doesn't compare property by property if they all have identical values) and choses the easy and safe mode: throwing an exception.)

Slauma
  • 175,098
  • 59
  • 401
  • 420