0

I am currently having difficulty tracking and adding a parent-entity object to my context after successfully adding its related child.

I have two SQL tables mapped to my entities that are related by a foreign key:

 MasterPartNumber (parent, one) --> MasterPartsList (children, many)
  (PK) pn <--short for PartNumber     (PK) listID
       pnDesc                         (FK) pn
       docNum                              parentAssyPN <-- if null, means it is the "top level assembly"
                                           findNum
                                           qty
                                           isAssy

On the left hand side of my view, I have a ListBox that displays an ObservableCollection of all the assembly, or parent entity objects. It looks like:

    public ObservableCollection<MasterPartNumber> AssyPns
    {
        get
        {
                var enumerable = this._context.MasterPartNumbers.Where(x => x.isAssy == true);

                return this._assyPns = new ObservableCollection<MasterPartNumber>(enumerable);
        }
        set 
        {
            this._assyPns = value;
            RaisePropertyChanged("AssyPns");  
        }
    }

The Selected Parent Assembly is defined by the viewmodel property:

 public MasterPartNumber SelectedTopLevelAssyPN 
 {
      get { return this._selectedTopLevelAssyPN; }
      set
      {
          this._selectedTopLevelAssyPN = value;
          RaisePropertyChanged("SelectedTopLevelAssyPN");
          RaisePropertyChanged("SelectedAssyBOMLineItems");
      }
  }

When a user clicks on one of the selected parent assembly (SelectedTopLevelAssyPN), a DataGrid displays the ObservableCollection bound to all the child entities whose parent (given by the property parentAssyPN) is the SelectedTopLevelAssyPN.

 public ObservableCollection<MasterPartsList> SelectedAssyBOMLineItems
    {
        get
        {
            if (this._selectedTopLevelAssyPN != null)
            {
                var children = _context.MasterPartsLists.Where(lineItem => lineItem.parentAssyPN == this._selectedTopLevelAssyPN.pn);
                return this._selectedAssyBOMLineItems = new ObservableCollection<MasterPartsList>(children);

            }
            return this._selectedAssyBOMLineItems;
        }
        set
        {
            this._selectedAssyBOMLineItems = value;
            RaisePropertyChanged("SelectedAssyBOMLineItems");
        }
    }

Notice how I am able to navigate through collection properties down to the entity that contains the foreign key (here is my xaml):

             <DataGrid x:Name="lineItemDataEntryGrid" Grid.Row="0"
                  Margin="15,15,15,0"
                  AutoGenerateColumns="False"
                  EnableRowVirtualization="True"
                  Width="Auto"
                  ItemsSource="{Binding SelectedAssyBOMLineItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  >
            <DataGrid.Columns>
                <DataGridTextColumn x:Name="qtyReadColumn" 
                                    Binding="{Binding qty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                    Width="SizeToHeader"
                                    Header="QTY REQ'D"/>
                <DataGridTextColumn x:Name="partOrIDNumColumn" 
                                    Binding="{Binding pn, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                    Width="SizeToHeader"
                                    Header="PART OR IDENTIFYING NUMBER"/>

                <DataGridTextColumn x:Name="partNumDescColumn" 
                                    Binding="{Binding MasterPartNumber.pnDesc, Mode= TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                    Width="SizeToHeader"
                                    Header="NOMENCALTURE OR DESCRIPTION"/>
                <DataGridTextColumn x:Name="docNumColumn" 
                                    Binding="{Binding MasterPartNumber.docNum, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                    Width="SizeToHeader"
                                    Header="INTERNAL DOCUMENTATION"/>
                <DataGridTemplateColumn x:Name="isAssyColumn" 
                                        Header="IS ASSEMBLY? "
                                        Width="30"
                                        >
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <CheckBox IsChecked="{Binding isAssy}" IsThreeState="False" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

            </DataGrid.Columns>

        </DataGrid>

And everything is displayed properly. That means I am effectively navigating down my parent entity to the child entity.

Now my problem: I am fully able to add the new items from MasterPartsList to the context, but not the MasterPartNumber objects.

The code in my SaveCommand (Relay Command) looks like:

    private void SaveLineItems()
    {
        //update the parts list if any Object properties have been edited

        foreach (MasterPartsList item in this._selectedAssyBOMLineItems)
        {
            this._context.DetectChanges();
            this._context.MasterPartsLists.AddObject(item);
          //  this._context.MasterPartNumbers.AddObject(item.MasterPartNumber); //Doesn't work: pnMatch.MasterPartNumber is null & Throws NullReferenceException
            this._context.SaveChanges();
        }
    }     

It appears that this would actually save my MasterPartsList data, if it weren't for the enforced foreign key constraint on MasterPartNumber.pn which can't be null.

I have been stuck on this for days. I was so easily able to implement .SaveChanges when I was just working with one table. Is anyone able to see why I am unable to add item.MasterPartNumber (or even get a non null object, for that matter?)

Thanks in advance. Please let me know where I need further clarification.

Rachael
  • 1,965
  • 4
  • 29
  • 55

1 Answers1

0

First of all, I wouldn't recommend loading the data in the property getter there.

Second, shouldn't the entity references such as item.MasterPartNumber be automatically added to the context when you add item? I'm sure that's the common way EF works.

You also mention an enforced foreign key constraint... that part isn't very clear. Can you provide the exception message? Also, what version/flavor you're using - DbContext or ObjectContext? Code first? Self-tracking entities? Is it the foreign key or primary key that's the problem? Is the primary key auto-incrementing and marked as such both in the database and the EDMX?

As a general advice, make sure you try to understand what Entity Framework is doing under the covers; otherwise you'll end up hating it without really understanding why (not that there aren't good reasons to). Try to visualize the object graph you have in memory and how EF tries to keep track of how everything is connected; it does that when you save the changes and tries to figure out in what order it needs to save everything so constraints aren't violated. Ah, but I'll stop waxing poetic while I can still retain a semblance of seriousness.

Alex Paven
  • 5,539
  • 2
  • 21
  • 35
  • Thanks, Alex. All the other questions about my question aside: if item.MasterPartNumber isn't auto. added to the context when I add item, what do you think could be wrong with my entity relationship? I think that is really the root cause of this whole thing. Please take a look at my SQL tables at the beginning of the question, if you don't mind. – Rachael Feb 26 '13 at 02:19
  • 1
    Really, I couldn't say just by looking at the data model; assuming you have everything set up properly there's no reason it shouldn't. You can however put some tracing code to help you: something like `_context.Entry(item.MasterPartNumber).State` to see if that's really the problem. Are you doing anything else that could interfere? Is ParentAssyPN set up to be a relationship? Without more information I can't be of much more help... – Alex Paven Feb 26 '13 at 10:02
  • Thanks, @Alex Paven. I've got some more info/am going to keep working on refining this question. Please take a look later when it is updated if you feel like it--you seem to be a good resource. – Rachael Feb 26 '13 at 18:01