2

I have following problem. I have typical master/detail UI scenario. I set main object (CommissionPlan) as a DataContext. Then I bind grid to CommissionPlanItems (this is collection of child items)

Everything works great on additions/updates. When I try to delete existing row - I get following EF error: 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.

I found bunch of recepies on how to handle this. But I'm not sure how to do it in my case since deletion happens auto-magically, via deleting row from a grid. What should I modify in this case?

katit
  • 17,375
  • 35
  • 128
  • 256

4 Answers4

2

The problem is that WPF grid only removes item from CommissionPlan.CommissionPlanItems collection. In common scenario this doesn't mean that item will be deleted in database. Only relation between items will be removed and CommisionPlanItem's CommissionPlanId will be set to null. It it is not nullable you will get your exception.

The solution is to delete removed item in ObjectContext or modify your entity model to support identifying relations (I think only possible in Entity Framework 4).

Community
  • 1
  • 1
Ladislav Mrnka
  • 360,892
  • 59
  • 660
  • 670
  • Ladislav, I think I'm getting it now. Modifying to support identifying relations doesn't seem to be a good idea from DB design point of view. Is this composite keys thing even normal?! How do I remove objects from ObjectContext? Should I search for them right before SaveChanges and do this? Do you have sample available? Like I said, I don't have control over HOW it's done since grid does it. I probably need to go and "clean up" after grid. – katit Feb 21 '11 at 23:27
  • @katit: I also don't like the idea of identifying relations but it is how it works in EF. I'm not WPF developer so I'm generally not sure how to handle it. It is usually performed on `ObservableCollection` where you can handle events like `CollectionChanged`. So you should probably wrap CommissionPlanItems into ObservableCollection and handle events. – Ladislav Mrnka Feb 22 '11 at 09:05
1

Here is what I ended up doing to "clean up". I will mark it as an answer, but I will appreciate if someone gives me better way to do this. Basically, I'm checking ObjectStateManager for modified entities (is there better way to see those?) and then if I see that modification involved setting parent to "null" - I know that this entity was "detached" from my graph and I go ahead and delete it.

modified = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
                if (modified != null && modified.Count() > 0)
                {
                    foreach (ObjectStateEntry ose in modified)
                    {
                        if (
                            (ose.Entity.GetType() == typeof(CommissionPlanCustomer) && ((CommissionPlanCustomer)ose.Entity).CommissionPlan == null)
                            ||
                            (ose.Entity.GetType() == typeof(CommissionPlanItemEligibleUser) && ((CommissionPlanItemEligibleUser)ose.Entity).CommissionPlanItem == null)
                            ||
                            (ose.Entity.GetType() == typeof(CommissionPlanItem) && ((CommissionPlanItem)ose.Entity).CommissionPlan == null)
                           )
                        {
                            context.DeleteObject(ose.Entity);
                        }
                    }
                }
katit
  • 17,375
  • 35
  • 128
  • 256
0

Here is another suggestion: You can modify the entity model to support an identifying relation without changing the database design. Open then edmx file in a XML editor and add referencing field to the key fields. In this case it would look something like:

<edmx:StorageModels>
<Schema ...>
  ...
  <EntityType Name="CommissionPlanItems">
    <Key>
       <PropertyRef Name="CommissionPlanItemsId" />
       <PropertyRef Name="CommissionPlanId" /> <!-- This line added -->
    </Key>
  ...
  </Schema>
</edmx:StorageModels>
<edmx:ConceptualModels>
  <Schema ...>
      ...
     <EntityType Name="CommissionPlanItems">
        <Key>
          <PropertyRef Name="CommissionPlanItemsId" />
          <PropertyRef Name="CommissionPlanId" /> <!-- This line added -->
        </Key>
      ...
  </Schema>
</edmx:ConceptualModels>

I have tried this out, and can now use a datagrid to insert, delete and update detail items. It might need further testing. If you run "Update model from database" you probably need to edit the edmx file again.

0

You can also wire up the AssociationChanged event to detect when the DataGrid has removed an EF relationship. In the event you call DeleteObject on your entity. A full explanation is provided by Julie Lerman in this article titled Drag & Drop Databinding with the Entity Framework and WPF. Her accompanying video explains the technique quite well (advance the video to the 25 minute mark).

DeveloperDan
  • 4,626
  • 9
  • 40
  • 65