Error when WPF client saves entity to Entity Framework (2010)

Hi

I've created a WPF application on top of an EF 4 model. It all works good untill I try to save a many-to-many entity from the GUI.
A Person can have many Roles in a collection, and Role can be used by many People. See included image.

Somehow when adding a Role to a persons Roles collection the entity is saved as a new Role entity instead.

Could somebody please take a look at this while i still have some hair left? Any example, tutorial or book (2010) will do.

re
Dennis
Window1.xaml

<Window x:Class="WPF.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Client.WPF"
        Title="Data Templates" Height="650" Width="1024" Loaded="Window_Loaded">
    <Window.Resources>
        <CollectionViewSource x:Key="MainView" />
        <CollectionViewSource x:Key="RolesView" 
            Source="{Binding Source={StaticResource MainView},
            Path='Roles'}"/>
        <!-- Pick lists-->
        <CollectionViewSource x:Key="StatusesLookup" />
        <CollectionViewSource x:Key="CitiesLookup" />
        <CollectionViewSource x:Key="CountriesLookup" />
        <CollectionViewSource x:Key="BanksLookup" />
        <CollectionViewSource x:Key="RolesLookup" />
        <!--
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="33" />
            <RowDefinition Height="*" />
            <RowDefinition Height="33" />
        </Grid.RowDefinitions>
        <Grid DataContext="{Binding Source={StaticResource MainView}}" Grid.Row="1" Margin="5">
            <Grid.RowDefinitions>
                <RowDefinition Height="33" />
                <RowDefinition Height="*" />
                <RowDefinition Height="0" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Left" MinWidth="150" MinHeight="33">
                <Button Content="|&lt;" Name="First_btn" Click="First_btn_Click" Margin="5" />
                <Button Content="&lt;" Name="Previous_btn" Click="Previous_btn_Click" Margin="5" />
                <Button Content="&gt;" Name="Next_btn" Click="Next_btn_Click" Margin="5" />
                <Button Content="&gt;|" Name="Last_btn" Click="Last_btn_Click" Margin="5" />
            </StackPanel>
            <StackPanel Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right" MinWidth="200" MinHeight="33" Name="Header_Right_stack">
                <Button Content="Delete" Height="23" Name="Delete_btn" Width="50" Margin="5" Click="Delete_btn_Click" />
                <Button Content="Add" Height="23" Name="Add_btn" Width="50" Margin="5" Click="Add_btn_Click" />
                <Button Content="Save" Height="23" Name="Save_btn" Width="50" Margin="5" Click="Save_btn_Click" />
                <Button Content="Close" Height="23" Name="Close_btn" Width="50" Margin="5" Click="Close_btn_Click" />
            </StackPanel>

            <StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" Width="auto" Height="auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
                <ListBox ItemsSource="{Binding Source={StaticResource MainView}}" IsSynchronizedWithCurrentItem="True"
                         VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MinWidth="200" SelectionChanged="ListBox_SelectionChanged">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Path=FirstName}"/>
                                <TextBlock Text=" "/>
                                <TextBlock Text="{Binding Path=LastName}"/>
                                <TextBlock Text=" ("/>
                                <TextBlock Text="{Binding Path=Status.Name}"/>
                                <TextBlock Text=")"/>
                            </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ListBox>
            </StackPanel>
            <StackPanel Grid.Row="1" Grid.Column="1">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="100" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <!-- First row-->
                    <Label Grid.Row="0" Grid.Column="0" Content="First name" />
                    <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=FirstName}" />
                    <Label Grid.Row="0" Grid.Column="2" Content="Hired date" />
                    <DatePicker Grid.Row="0" Grid.Column="3" Width="150" HorizontalAlignment="Left"
                            SelectedDate="{Binding Path=HiredDate}"/>
                    <!-- Second row-->
                    <Label Grid.Row="1" Grid.Column="0" Content="Middle name" />
                    <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=MiddleName}" />
                    <Label Grid.Row="1" Grid.Column="2" Content="Personal number" />
                    <TextBox Grid.Row="1" Grid.Column="3" Text="{Binding Path=PersonalNumber}" />
                    <!-- Third row-->
                    <Label Grid.Row="2" Grid.Column="0" Content="Last name" />
                    <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=LastName}" />
                    <Label Grid.Row="2" Grid.Column="2" Content="Summary" />
                    <TextBox Grid.Row="2" Grid.Column="3" Grid.RowSpan="2" Text="{Binding Path=Summary}" 
                     TextWrapping="Wrap"
                     AcceptsReturn="True"
                     VerticalScrollBarVisibility="Auto"/>
                    <!-- Fourth row-->
                    <Label Grid.Row="3" Grid.Column="0" Content="Title" />
                    <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Path=Title}" />
                    <!-- Fifth row-->
                    <Label Grid.Row="4" Grid.Column="0" Content="Status" />
                    <ComboBox Grid.Row="4" Grid.Column="1"
                          ItemsSource="{Binding Source={StaticResource StatusesLookup}}"
                          SelectedItem="{Binding Path=Status}"
                          DisplayMemberPath="Name"/>
                    <Label Grid.Row="4" Grid.Column="2" Content="Account number" />
                    <TextBox Grid.Row="4" Grid.Column="3" Text="{Binding Path=AccountNumber}" />
                    <!-- Sixth row-->
                    <Label Grid.Row="5" Grid.Column="0" Content="Born date" />
                    <DatePicker Grid.Row="5" Grid.Column="1" Width="150" HorizontalAlignment="Left"
                            SelectedDate="{Binding Path=BornDate}"/>
                    <Label Grid.Row="5" Grid.Column="2" Content="Bank" />
                    <ComboBox Grid.Row="5" Grid.Column="3"
                          ItemsSource="{Binding Source={StaticResource BanksLookup}}"
                          SelectedItem="{Binding Path=Bank}"
                          DisplayMemberPath="Name"
                           />
                    <!-- Seventh row-->
                    <Label Grid.Row="6" Grid.Column="0" Content="Born city" />
                    <ComboBox Grid.Row="6" Grid.Column="1"
                          ItemsSource="{Binding Source={StaticResource CitiesLookup}}"
                          SelectedItem="{Binding Path=City}"
                          DisplayMemberPath="Name"/>
                    <Label Grid.Row="6" Grid.Column="2" Content="SAP short" />
                    <TextBox Grid.Row="6" Grid.Column="3" Text="{Binding Path=SAPShort}" />
                    <!-- Eight row-->
                    <Label Grid.Row="7" Grid.Column="0" Content="Born country" />
                    <ComboBox Grid.Row="7" Grid.Column="1"
                          ItemsSource="{Binding Source={StaticResource CountriesLookup}}"
                          SelectedItem="{Binding Path=Country}"
                          DisplayMemberPath="Name"/>
                    <!-- Ninth row-->
                    <Label Grid.Row="8" Grid.Column="0" Content="Female" />
                    <CheckBox Grid.Row="8" Grid.Column="1" 
                          IsChecked="{Binding Path=Female}"/>
                    <StackPanel Grid.Row="10" Grid.ColumnSpan="4">
                        <TabControl Height="Auto" Width="Auto">
                            <TabItem Header="Roles">
                                <StackPanel Orientation="Vertical">
                                    <ListView Height="150" Name="Roles_ListView"
                                        IsSynchronizedWithCurrentItem="True"
                                        ItemsSource="{Binding Source={StaticResource RolesView}}">
                                        <ListView.ItemContainerStyle>
                                            <Style TargetType="ListViewItem">
                                                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                                                <EventSetter Event="GotFocus" Handler="Roles_GotFocus" />
                                            </Style>
                                        </ListView.ItemContainerStyle>
                                        <ListView.View>
                                            <GridView>
                                                <GridViewColumn Header="Roles" Width="300">
                                                    <GridViewColumn.CellTemplate>
                                                        <DataTemplate>
                                                            <ComboBox IsEditable="False"
                                                                IsSynchronizedWithCurrentItem="False"       
                                                                ItemsSource="{Binding Source={StaticResource RolesLookup}}"
                                                                SelectedItem="{Binding Path=Roles}" 
                                                                DisplayMemberPath="Name" 
                                                                Margin="-6,0,-6,0"/>
                                                        </DataTemplate>
                                                    </GridViewColumn.CellTemplate>
                                                </GridViewColumn>
                                            </GridView>
                                        </ListView.View>
                                    </ListView>
                                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                                        <Button Content="Insert" Height="28" Width="50" Margin="5" Name="InsertRole_btn" Click="InsertRole_btn_Click" />
                                        <Button Content="Update" Height="28" Width="50" Margin="5" Name="UpdateRole_btn" Click="Save_btn_Click" />
                                        <Button Content="Remove" Height="28" Width="50" Margin="5" Name="RemoveRole_btn" Click="RemoveRole_btn_Click" />
                                    </StackPanel>
                                </StackPanel>
                            </TabItem>
                        </TabControl>
                    </StackPanel>
                </Grid>
            </StackPanel>

        </Grid>
    </Grid>
</Window>


Window1.cs:

using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using MCGModel;
using MCGCollections;

namespace WPF
{
    public partial class Window1 : Window
    {
        private MCGEntities db;
        private ListCollectionView masterView;
        private PeopleCollection people;

        #region Main BindingListCollectionViews
        private BindingListCollectionView rolesView; 
        #endregion

        #region Main CollectionViewSources
        private CollectionViewSource mainDataSource;
        private CollectionViewSource rolesDataSource;
        #endregion

        #region Pick list CollectionViewSources
        private CollectionViewSource statusesLookup;
        private CollectionViewSource citiesLookup;
        private CollectionViewSource countriesLookup;
        private CollectionViewSource banksLookup;
        private CollectionViewSource rolesLookup;
        #endregion

        public Window1()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            db = new MCGEntities();

            ConnectDataSources();
            ConnectData();
            ConnectListViews();
        }

        private void ConnectDataSources()
        {
            mainDataSource = (CollectionViewSource)this.Resources["MainView"];
            rolesDataSource = (CollectionViewSource)this.Resources["RolesView"];

            statusesLookup = (CollectionViewSource)this.Resources["StatusesLookup"];
            citiesLookup = (CollectionViewSource)this.Resources["CitiesLookup"];
            countriesLookup = (CollectionViewSource)this.Resources["CountriesLookup"];
            banksLookup = (CollectionViewSource)this.Resources["BanksLookup"];
            rolesLookup = (CollectionViewSource)this.Resources["RolesLookup"];
        }

        private void ConnectData()
        {
            var persons = from c in db.People
                          orderby c.FirstName, c.LastName
                          select c;

            var statuses = from s in db.Statuses
                           orderby s.Name
                           select s;

            var cities = from ci in db.Cities
                         orderby ci.Name
                         select ci;

            var countries = from co in db.Countries
                            orderby co.Name
                            select co;

            var banks = from b in db.Banks
                        orderby b.Name
                        select b;
            
            var roles = from r in db.Roles
                        orderby r.Name
                        select r;


            this.people = new PeopleCollection(persons, db);

            mainDataSource.Source = people;
            statusesLookup.Source = statuses;//.ToList();
            citiesLookup.Source = cities;//.ToList();
            countriesLookup.Source = countries;//.ToList();
            banksLookup.Source = banks;//.ToList();
            rolesLookup.Source = roles;
        }

        private void ConnectListViews()
        {
            // Set the ListCollectionView using the CollectionViewSource
            this.masterView = ((ListCollectionView)(mainDataSource.View));
            this.rolesView = ((BindingListCollectionView)(rolesDataSource.View));
        }

        private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ConnectListViews();
        }

        #region MainForm buttons

        private void Delete_btn_Click(object sender, RoutedEventArgs e)
        {
            if (this.masterView.CurrentPosition > -1)
            {
                this.masterView.RemoveAt(this.masterView.CurrentPosition);
            }
        }

        private void Add_btn_Click(object sender, RoutedEventArgs e)
        {
            this.masterView.AddNew();
            this.masterView.CommitNew();
        }

        private void Save_btn_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                db.SaveChanges();
                MessageBox.Show("All data was saved.", this.Title, MessageBoxButton.OK, MessageBoxImage.Information);

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void Close_btn_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void First_btn_Click(object sender, RoutedEventArgs e)
        {
            this.masterView.MoveCurrentToFirst();
        }

        private void Previous_btn_Click(object sender, RoutedEventArgs e)
        {
            if (this.masterView.CurrentPosition > 0)
            {
                this.masterView.MoveCurrentToPrevious();
            }
        }

        private void Next_btn_Click(object sender, RoutedEventArgs e)
        {
            if (this.masterView.CurrentPosition < (this.masterView.Count - 1))
            {
                this.masterView.MoveCurrentToNext();
            }
        }

        private void Last_btn_Click(object sender, RoutedEventArgs e)
        {
            this.masterView.MoveCurrentToLast();
        }

        #endregion

        #region Roles
        private void InsertRole_btn_Click(object sender, RoutedEventArgs e)
        {
            this.rolesView.AddNew();
            this.rolesView.CommitNew();
        }

        private void RemoveRole_btn_Click(object sender, RoutedEventArgs e)
        {
            if (this.rolesView.CurrentPosition > -1)
            {
                this.rolesView.RemoveAt(this.rolesView.CurrentPosition);
            }
        }
        private void Roles_GotFocus(object sender, RoutedEventArgs e)
        {
            ListViewItem item = ((ListViewItem)sender);
            this.Roles_ListView.SelectedItem = item.DataContext;
        }
        #endregion



    }
}


PeopleCollection.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using MCGModel;
using System.ComponentModel;

namespace MCGCollections
{
    public class PeopleCollection : ObservableCollection<Person>
    {
        // Read only property to hold the context
        private MCGEntities _context;
        public MCGEntities Context
        {
            get { return _context; }
        }

        //Override the constructor
        public PeopleCollection(IEnumerable<Person> persons, MCGEntities context)
            : base(persons)
        {
            _context = context;

            foreach (Person p in persons)
            {
                p.Roles.AssociationChanged += new CollectionChangeEventHandler(Roles_AssociationChanged);
            }

        }

        //Delete the objects in the collection, not just the association
        void Roles_AssociationChanged(object sender, CollectionChangeEventArgs e)
        {
            if (e.Action == CollectionChangeAction.Remove)
            {
                this.Context.DeleteObject((Role)e.Element);
            }
        }

        // Override the insert function so the context can track the changes on navigation properties
        protected override void InsertItem(int index, Person item)
        {
            item.Roles.AssociationChanged += new CollectionChangeEventHandler(Roles_AssociationChanged);

            this.Context.AddToPeople(item);
            base.InsertItem(index, item);
        }

        // Override the remove function so the context deletes the object and not just the association
        protected override void RemoveItem(int index)
        {
            Person p = this[index];
            p.Roles.AssociationChanged -= Roles_AssociationChanged;
            for (int i = p.Roles.Count - 1; i >= 0; i--)
            {
                this.Context.DeleteObject(p.Roles.ElementAt(i));
            }

            this.Context.DeleteObject(this[index]);
            base.RemoveItem(index);
        }

    }
}

Open in new window

Roles.png
Error.png
sgude0Asked:
Who is Participating?
 
sgude0Author Commented:
Got it to work
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.