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?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
sgude0Author Commented:
Got it to work
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft Development

From novice to tech pro — start learning today.