Question

This is a simplified version of the problem I am trying to solve:

There are two entities:

Item

ItemID (PK)

Other simple properties...

WorkItem (navigation property)

WorkItem

ItemID (PK)

Other Simple Properties...

Item (Navigation property)

 

I need to create an Item, a WorkItem for it and I need to set both the navigation properties so the the two entities can point to each other, before saving.

This I can do:

  Item newItem = Item.CreateItem(0, blah,blah,blah);
  service.AddToItems(newItem);


  WorkItem newWorkItem = new WorkItem();           
  service.AddToWorkItems(newWorkItem);

  //set the navigation properties
  newItem.WorkItem = newWorkItem;
  newWorkItem.Item = newItem;

Unfortunately, when it comes to saving, this fails. I believe EF, when trying to set WorkItem's Item association, tries to set WorkItem's primary key.

Can anybody enlighten me as to the correct way to achieve this please?

 
Update:  
 
So, I have tried building the model using inheritance. The model builds and validates.

Unfortunately, adding a WCF Data Service for my model and trying to view the service in a browser gives me this:

  ..... <m:message xml:lang="en-US">An error occurred while processing this request.    </m:message>
<m:innererror>
  <m:message>Navigation Properties are not supported on derived entity types. Entity Set 'app_Items' has a instance of type 'tempmodel.app_CostItem', which is an derived entity type and has navigation properties. Please remove all the navigation properties from type 'tempmodel.app_CostItem'.</m:message>
  <m:type>System.InvalidOperationException</m:type>
  <m:stacktrace>   at System.Data.Services.Serializers.SyndicationSerializer.WriteObjectProperties(IExpandedResult expanded, ......

CostItem being another entity, like WorkItem, which derives from Item.

Was it helpful?

Solution 2

I eneded up creating a dummy property in each of my client side partial "sub"-classes:

    private Item _BaseItem;
    [AtomIgnore]
    internal Item BaseItem
        {
        get
            {
            if (_BaseItem != null)
                return _BaseItem;
            else
                return this.Item;                
            }
        set
            {
            _BaseItem = value;
            OnPropertyChanged("Item");
            }
        }

Code on the client side uses this BaseItem property to traverse the object graph in reverse. So the code to set up the relationships now looks like this:

 Item newItem = Item.CreateItem(0, blah,blah,blah);
 service.AddToItems(newItem);


 WorkItem newWorkItem = new WorkItem();           
 service.AddToWorkItems(newWorkItem);

 //set the navigation properties
 newItem.WorkItem = newWorkItem;
 newWorkItem.**BaseItem** = newItem;

Note, the BaseItem property is declared internal; this is so that the serialiser will not attempt to serialise the property and send it to the server.

[AtomIgnore] is an attribute I created and look for in my handler for DataServiceContext.WritingEntity. The handler strips out properties marked with this attribute before they are sent to the server. This technique doesn't seem to work if the property is a ref type though, hence the use of internal.

Declaring it internal also means it cannot be bound to from XAML, fortunately i only needed to bind to a couple of properties so that work around was straightforward.

OTHER TIPS

Based on the example you gave, I believe you should use inheritance. The WorkItem entity should inherit from the Item entity. Then, in your code, you will only need to work with a WorkItem entity and it will have all of the properties of the Item entity as well. Plus, the WorkItem entity will use the key from the Item entity.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top