0

I have been struggling for weeks with my WPF appliaction named "ContactManager" when i want to add records to the database. I have two entites:

public partial class Contact : EntityBase
{        
    public int ContactId { get; set; }
    [StringLength(20)]
    public string FirstName { get; set; }
    [StringLength(20)]
    public string LastName { get; set; }
    [StringLength(20)]
    public string Organization { get; set; }
    [StringLength(20)]
    public string JobTitle { get; set; }
    public string ImagePath { get; set; }
    public string CellPhone { get; set; }
    public string HomePhone { get; set; }
    public string OfficePhone { get; set; }
    public string PrimaryEmail { get; set; }
    public string SecondaryEmail { get; set; }

    public virtual Address Address { get; set; } = new Address();


public class Address : EntityBase
{
    [ForeignKey("Contact")]
    public int AddressId { get; set; }
    public string City { get; set; }
    public string Country { get; set; }
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string Zip { get; set; }
    public string State { get; set; }

    public virtual Contact Contact { get; set; }

The add method:

    public int Add(Contact entity)
    {
        Context.Contacts.Add(entity);

        var o = SaveChanges();
        Context.Dispose();
        Context = new CMEntities();
        return o;
    }

    internal int SaveChanges()
    {
        try
        {
            return Context.SaveChanges();
        }
    }

When the database is empty it is working but after closing and restarting the app I get the following exception:

SqlException: Violation of PRIMARY KEY constraint 'PK_dbo.Addresses'. Cannot insert duplicate key in object 'dbo.Addresses'. The duplicate key value is (1). The statement has been terminated.

I do not understand why Sql server (or entity framework??) wants to insert key 1 instead of the next id...

if i delete rows in table Addresses it is working again, it inserts the row without problem with the next/right addressid.( that is the addressid is not 1 but 5 because of the previous rows of contacts )

In my opinion the schema of( keys etc ) tables are correct the problem is with other issue, maybe binding or context i have not got a clue...

(I made a simpler example omitting MVC, it is working. )

Here is the code of MVC app:

namespace CMnew.Model
{
    public partial class ContactRepository : IDisposable
    {

        public CMEntities Context { get; set; } = new CMEntities();

        private List<Contact> _contactStore;

        public ContactRepository()
        {


            if (this.GetAll() == null)
            {
                _contactStore = new List<Contact>();
            }
            else
            {
                _contactStore = this.GetAll();
            }
        }


        public List<Contact> FindByLookup(string lookupName)
        {
            IEnumerable<Contact> found = from c in _contactStore
                                         where c.LookupName.StartsWith(lookupName, StringComparison.OrdinalIgnoreCase)
                                         select c;

            return found.ToList();
        }


        public List<Contact> FindAll()
        {
            return new List<Contact>(_contactStore);
        }


        public void Save(Contact contact)
        {
          
            if (_contactStore.Contains(contact))
            {
                this.SaveToDatabase(contact);
            }
            else
            {
                _contactStore.Add(contact);
                this.Add(contact);
            }
        }

        public int SaveToDatabase(Contact entity)
        {
           
            return SaveChanges();
        }

        public void Delete(Contact contact)
        {
            _contactStore.Remove(contact);
            DeleteFromDatabase(contact);
        }


        public int DeleteFromDatabase(Contact entity)
        {
            Context.Entry(entity).State = EntityState.Deleted;
            return SaveChanges();
        }

        public Contact GetOne(int? id) => Context.Contacts.Find(id);
        public List<Contact> GetAll() => Context.Contacts.ToList();

        public int Add(Contact entity)
        {

            Context.Contacts.Add(entity);

            var o = SaveChanges();

            Context.Dispose();
            Context = new CMEntities();

            return o;

        }

        internal int SaveChanges()
        {
            try
            {
                return Context.SaveChanges();
            }
            catch (DbUpdateConcurrencyException ex)
            {

                throw;
            }

            catch (DbUpdateException ex)
            {
                throw;
            }

            catch (CommitFailedException ex)
            {
                throw;
            }

            catch (Exception ex)
            {
                throw ex;
            }
        }


        bool disposed = false;
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposed)
            {
                return;
            }

            if (disposing)
            {
                Context.Dispose();
            }

            disposed = true;
        }


    }
}


namespace CMnew.Presenters
{
    public class ApplicationPresenter : PresenterBase<Shell>, INotifyPropertyChanged
    {

        private readonly ContactRepository _contactRepository;
        private ObservableCollection<Contact> _currentContacts;

        public event PropertyChangedEventHandler PropertyChanged;

        public ApplicationPresenter(Shell view, ContactRepository contactRepository) : base(view)
        {
            _contactRepository = contactRepository;
            _currentContacts = new ObservableCollection<Contact>(_contactRepository.FindAll());
        }


        // public ObservableCollection<Contact> CurrentContacts { get; set; }

        public ObservableCollection<Contact> CurrentContacts
        {
            get { return _currentContacts; }
            set
            {
                _currentContacts = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentContacts)));

            }
        }


        public string StatusText { get; set; }

        public void Search(string criteria)
        {
            if (!string.IsNullOrEmpty(criteria) && criteria.Length > 2)
            {
                CurrentContacts = new ObservableCollection<Contact>(_contactRepository.FindByLookup(criteria));

                StatusText = string.Format("{0} contacts found.", CurrentContacts.Count);
            }
            else
            {
                CurrentContacts = new ObservableCollection<Contact>(_contactRepository.FindAll());
            }

        }


        public void NewContact()
        {
            OpenContact(new Contact());
        }


        public void SaveContact(Contact contact)
        {
            if (!CurrentContacts.Contains(contact))
            {
                CurrentContacts.Add(contact);
            }

            _contactRepository.Save(contact);

            StatusText = string.Format("Contact '{0}' was saved.", contact.LookupName);
        }

        public void DeleteContact(Contact contact)
        {
            if (CurrentContacts.Contains(contact))
            {
                CurrentContacts.Remove(contact);
            }

            _contactRepository.Delete(contact);

            StatusText = string.Format("Contact '{0}' was deleted.", contact.LookupName);
        }

        public void CloseTab<T>(PresenterBase<T> presenter)
        {
            View.RemoveTab(presenter);
        }



        private void OpenContact(Contact contact)
        {
            if (contact == null) return;

            View.AddTab(new EditContactPresenter(this, new EditContactView(), contact));
        }


        public void DisplayAllContacts()
        {
            throw new NotImplementedException();
        }

    }
}

namespace CMnew.Presenters
{
    public class EditContactPresenter : PresenterBase<EditContactView>
    {

        private readonly ApplicationPresenter _applicationPresenter;
        private Contact _contact;

        public EditContactPresenter(ApplicationPresenter applicationPresenter, EditContactView view, Contact contact) : base(view, "Contact.LookupName")
        {
            _applicationPresenter = applicationPresenter;
            _contact = contact;
        }

        public Contact Contact
        {
            get { return _contact; }
            set { _contact = value; }
        }

        public void SelectImage()
        {
            string imagePath = View.AskUserForImagePath();
            if (!string.IsNullOrEmpty(imagePath))
            {
                Contact.ImagePath = imagePath;
            }
        }

        public void Save()
        {
            _applicationPresenter.SaveContact(Contact);
        }

        public void Delete()
        {
            _applicationPresenter.CloseTab(this);
            _applicationPresenter.DeleteContact(Contact);
        }

        public void Close()
        {
            _applicationPresenter.CloseTab(this);
        }

        public override bool Equals(object obj)
        {
            EditContactPresenter presenter = obj as EditContactPresenter;
            return presenter != null && presenter.Contact.Equals(Contact);
        }

    }
}


namespace CMnew.Views
{
    /// <summary>
    /// Interaction logic for EditContactView.xaml
    /// </summary>
 
        public partial class EditContactView : UserControl
        {
            public EditContactView()
            {
                InitializeComponent();
            }

            public EditContactPresenter Presenter
            {
                get { return DataContext as EditContactPresenter; }
            }

            private void Save_Click(object sender, RoutedEventArgs e)
            {
                Presenter.Save();
            }

            private void Delete_Click(object sender, RoutedEventArgs e)
            {
                Presenter.Delete();
            }

            private void Close_Click(object sender, RoutedEventArgs e)
            {
                Presenter.Close();
            }

            private void SelectImage_Click(object sender, RoutedEventArgs e)
            {
                Presenter.SelectImage();
            }

            public string AskUserForImagePath()
            {
                OpenFileDialog dlg = new OpenFileDialog();
                dlg.ShowDialog();
                return dlg.FileName;
            }
        }
}

<UserControl x:Class="CMnew.Views.EditContactView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:CMnew.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <DockPanel Margin="5">
        <Border DockPanel.Dock="Top">
            <DockPanel LastChildFill="False">
                <TextBlock DockPanel.Dock="Left" Text="{Binding Contact.LastName}"/>
                <TextBlock DockPanel.Dock="Left" Text=", "/>
                <TextBlock DockPanel.Dock="Left" Text="{Binding Contact.FirstName}"/>
                <TextBlock DockPanel.Dock="Right" Text="{Binding Contact.Organization}"/>
            </DockPanel>
        </Border>

        <StackPanel DockPanel.Dock="Bottom" Style="{StaticResource buttonPanel}">
            <Button Content="Save" Click="Save_Click"/>
            <Button Content="Delete" Click="Delete_Click"/>
            <Button Content="Close" Click="Close_Click"/>
        </StackPanel>

        <WrapPanel>
            <GroupBox BorderBrush="{StaticResource lightBlueBrush}">
                <GroupBox.Header>
                    <Border Background="{StaticResource lightBlueBrush}" Style="{StaticResource groupBoxHeader}">
                        <TextBlock Text="General"/>
                    </Border>
                </GroupBox.Header>

                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="100"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="175"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>


                    <Grid Grid.RowSpan="4">
                        <Border Background="Gray"
                                    CornerRadius="6"
                                    Margin="2 2 0 0"
                                    Opacity=".5"/>
                        <Border Margin="2 2 4 4"
                                    Background="White"/>
                        <Viewbox Margin="2 2 4 4">
                            <Image Source="{Binding Contact.ImagePath}" />
                        </Viewbox>
                        <Border BorderBrush="{StaticResource lightBlueBrush}"
                                    BorderThickness="2"
                                    Background="Transparent"
                                    CornerRadius="6"
                                    Margin="0 0 2 2"/>
                        <Button Style="{StaticResource openButton}"
                                    Background="White"
                                    Foreground="{StaticResource lightBlueBrush}"
                                    BorderBrush="{StaticResource lightBlueBrush}"
                                    ToolTip="Change Picture"
                                    Click="SelectImage_Click" />

                    </Grid>

                    <Label Grid.Column="1"
                               Content="_First Name:"
                               Target="{Binding ElementName=firstName}"/>
                    <TextBox x:Name="firstName"
                                 Grid.Column="2"
                                 Text="{Binding Contact.FirstName}"/>

                    <Label Grid.Row="1"
                               Grid.Column="1"
                               Content="_Last Name:"
                               Target="{Binding ElementName=lastName}"/>
                    <TextBox x:Name="lastName"
                                 Grid.Row="1"
                                 Grid.Column="2"
                                 Text="{Binding Contact.LastName}"/>

                    <Label Grid.Row="2"
                               Grid.Column="1"
                               Content="Or_ganization:"
                               Target="{Binding ElementName=organization}"/>
                    <TextBox x:Name="organization"
                                 Grid.Row="2"
                                 Grid.Column="2"
                                 Text="{Binding Contact.Organization}"/>


                    <Label Grid.Row="3"
                               Grid.Column="1"
                               Content="_Job Title:"
                               Target="{Binding ElementName=jobTitle}"/>
                    <TextBox x:Name="jobTitle"
                                 Grid.Row="3"
                                 Grid.Column="2"
                                 Text="{Binding Contact.JobTitle}"/>





                </Grid>

            </GroupBox>

            <GroupBox BorderBrush="{StaticResource greenBrush}">
                <GroupBox.Header>
                    <Border Background="{StaticResource greenBrush}"
                            Style="{StaticResource groupBoxHeader}">
                        <TextBlock Text="Address"/>
                    </Border>
                </GroupBox.Header>


                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="150"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="150"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>

                    <Label Content="Line _1:"
                           Target="{Binding ElementName=line1}" />
                    <TextBox x:Name="line1"
                             Grid.Column="1"
                             Grid.ColumnSpan="3"
                             Text="{Binding Contact.Address.Line1}" />


                    <Label Grid.Row="1"
                           Content="Line _2:"
                           Target="{Binding ElementName=line2}" />
                    <TextBox x:Name="line2"
                             Grid.Row="1"
                             Grid.Column="1"
                             Grid.ColumnSpan="3"
                             Text="{Binding Contact.Address.Line2}" />



                    <Label Grid.Row="2"
                           Content="Ci_ty:"
                           Target="{Binding ElementName=city}" />
                    <TextBox x:Name="city"
                             Grid.Row="2"
                             Grid.Column="1"
                             Text="{Binding Contact.Address.City}" />


                    <Label Grid.Row="2"
                           Grid.Column="2"
                           Content="_State:"
                           Target="{Binding ElementName=state}" />
                    <TextBox x:Name="state"
                             Grid.Row="2"
                             Grid.Column="3"
                             Text="{Binding Contact.Address.State}" />


                    <Label Grid.Row="3"
                           Grid.Column="0"
                           Content="_Zip:"
                           Target="{Binding ElementName=zip}" />
                    <TextBox x:Name="zip"
                             Grid.Row="3"
                             Grid.Column="1"
                             Text="{Binding Contact.Address.Zip}" />



                    <Label Grid.Row="3"
                           Grid.Column="2"
                           Content="Countr_y:"
                           Target="{Binding ElementName=country}" />
                    <TextBox x:Name="country"
                             Grid.Row="3"
                             Grid.Column="3"
                             Text="{Binding Contact.Address.Country}" />


                </Grid>





            </GroupBox>

            <GroupBox BorderBrush="{StaticResource redBrush}">
                <GroupBox.Header>
                    <Border Background="{StaticResource redBrush}"
                            Style="{StaticResource groupBoxHeader}">
                        <TextBlock Text="Phone"/>
                    </Border>
                </GroupBox.Header>

                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="150"/>
                    </Grid.ColumnDefinitions>

                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>


                    <Label Content="_Office:"
                           Target="{Binding ElementName=office}"/>
                    <TextBox x:Name="office"
                             Grid.Column="1"
                             Text="{Binding Contact.OfficePhone}" />


                    <Label Grid.Row="1"
                           Content="_Cell:"
                           Target="{Binding ElementName=cell}" />
                    <TextBox x:Name="cell"
                             Grid.Row="1"
                             Grid.Column="1"
                             Text="{Binding Contact.CellPhone}" />

                    <Label Grid.Row="2"
                           Content="_Home:"
                           Target="{Binding ElementName=home}" />
                    <TextBox x:Name="home"
                             Grid.Row="2"
                             Grid.Column="1"
                             Text="{Binding Contact.HomePhone}" />

                </Grid>

            </GroupBox>


            <GroupBox BorderBrush="{StaticResource brownBrush}">
                <GroupBox.Header>
                    <Border Background="{StaticResource brownBrush}"
                            Style="{StaticResource groupBoxHeader}">
                        <TextBlock Text="Email"/>
                    </Border>
                </GroupBox.Header>


                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="200"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>


                    <Label Content="_Primary:"
                   Target="{Binding ElementName=primaryEmail}"/>
                    <TextBox x:Name="primaryEmail"
                     Grid.Column="1"
                     Text="{Binding Contact.PrimaryEmail}"/>


                    <Label Grid.Row="1"
                        Content="S_econdary:"
                        Target="{Binding ElementName=secondaryEmail}"/>
                    <TextBox x:Name="secondaryEmail"
                     Grid.Row="1"
                     Grid.Column="1"
                     Text="{Binding Contact.SecondaryEmail}"/>

                </Grid>
            </GroupBox>
        </WrapPanel>

    </DockPanel>
</UserControl>

Termesz
  • 1
  • 3
  • The error is likely in code you don't show. You maybe have multiple contacts pointing to the same addres objects, which you then add twice. – JHBonarius Oct 24 '20 at 18:50
  • i have added more code, please focus only add/save method, update and delete is not coded correctly yet. Thanks. – Termesz Oct 24 '20 at 19:21
  • Which EF version? – Gert Arnold Oct 24 '20 at 19:40
  • This is code first? I would always recommend at least starting with database first. How does your linq end up generating an insert for address with an addressid in it at all? There should be no id at all in an insert to a table has an identity column. I would also explicitly apply [Key] attributes rather than rely on convention. Easier to read if nothing else. Copy entity data to viewmodels and put you validation and dataannotations on them rather than the ef classes. – Andy Oct 24 '20 at 19:48
  • In the "Add" method, put a breakpoint on the line var o = SaveChanges(); and see how the incoming object "entity" looks like. Its of type Contact. Try checking the "AddressId" field in the debugger window. what values are coming in ? –  Oct 25 '20 at 01:45
  • @GertArnold ''' ''' and VS2019 SQL server version: 14.00.1000 – Termesz Oct 25 '20 at 10:38
  • @GargiD.Chakravarty entity contains all data put in the forms, both Addressid and ContactID are 0 but i think it is ok because SQL server should determine the right value of ids – Termesz Oct 25 '20 at 10:46
  • @Andy Yes this is code first – Termesz Oct 25 '20 at 10:46
  • It's not clear what you mean by "after closing and restarting the app I get the following exception". Also, it would be more interesting to see the code that *doesn't* work. But one thing you should do is remove `= new Address();`, [Here's explained why](https://stackoverflow.com/a/20773057/861716). – Gert Arnold Oct 25 '20 at 11:32
  • @GertArnold 1. Empty database 2. put data to the form, it is saved to database witout problem 3. put data, second row is also created both table, 4. put data, third row is also ok 4. Close the application 5. Start the application 6. put data, save, get exception – Termesz Oct 25 '20 at 12:12
  • @GertArnold i have also tried...if i remove it, Address class will not be instantiated and will not be saved to the database which is not the expected operating. – Termesz Oct 25 '20 at 12:16
  • You should instantiate it when it's necessary. Not always. See the link I provided. Also, why doesn't your controller have a new context instance? You should not have to create a new context inside the `Add` method. That's a big code smell and probably the cause of the exception. – Gert Arnold Oct 25 '20 at 12:18
  • When i secondly run the app the controller should have new context because of ~~~ public CMEntities Context { get; set; } = new CMEntities(); ~~~ – Termesz Oct 25 '20 at 12:41
  • @GertArnold i think Address class should be instantiated when Contact class is instantiated that is why exists code: = new Address() but i will read the link – Termesz Oct 25 '20 at 13:01
  • i have read the post, i got to know that virtual property instantiation is wrong but i did not get to know when or how to instantiate dependent end of association so unfortunatelly it could not help me. – Termesz Oct 25 '20 at 20:33

2 Answers2

0

This is likely your problem:

public class Address : EntityBase
{
    [ForeignKey("Contact")] // <---
    public int AddressId { get; set; }  

along with that you are disposing your DbContext after saving. When the EF DbContext encounters a new entity (Contact) with a related entity that you expect exists but isn't tracked, it will treat that entity as a new entity. This behaviour is likely the root of what is tripping you up passing entities around. The Address is no longer tracked so an existing address record is attempted to be re-inserted and since it is the FK pointing at Contact, it will not generate a new value, but try to use the current ID of the contact.

Typically Address would expect to have a PK (AddressID) and a FK (ContactId) if your contact could have multiple addresses. Either that or the Contact entity would have an AddressID. (Where a single address could serve multiple contacts) If you want one contact to have only one address then either put the address fields into Contact itself, or create a ContactAddressDetails table with a ContactId as the PK+FK for a HasRequired/WithRequired relationship if you want it in a separate table. The desired relationship will define what the fields on the entity / schema will look like.

Steve Py
  • 26,149
  • 3
  • 25
  • 43
  • Thanks for the answer but i do not think this is the problem because why our app is working at the first run ?? At the second run of app I am disposing nothing because i get the error before disposing statement... – Termesz Oct 26 '20 at 08:36
  • Is this EF6 or EF Core? for EF6, one-to-one relationships need to be set up joining the primary keys which looks like how you're wiring it but it is going to get confusing with the different key names (Contact ID = Address ID) Hence either bring Address fields into Contact or call the table something like ContactAddressDetails with a PK of ContactId. Either way, try explicitly mapping the HasRequired.WithRequired mapping between the tables either on DbContext initialization or with an EntityTypeConfiguration... – Steve Py Oct 26 '20 at 20:37
  • It also isn't clear from your example where/how an Address entity is being allocated for a new Contact. In the case of a 1 to one an address should be instantiated wherever a Contact is also created. (I.e. a factory method `CreateContract` should do something like `var contract = new Contract { Address = new Address { /* ... */ }, /* ... */ }; return contract; ` – Steve Py Oct 26 '20 at 20:42
0

Thanks for your answers but i found the problem:

public ContactRepository()
        {


            if (this.GetAll() == null)
            {
                _contactStore = new List<Contact>();
            }
            else
            {
                _contactStore = this.GetAll();
            }
        }
public List<Contact> GetAll() => Context.Contacts.ToList();

In the ContactRepository constructor after calling GetAll() i have to dispose the current context:

private List<Contact> _contactStore = new List<Contact>();

        public ContactRepository()
        {

            _contactStore = this.GetAll();

            Context.Dispose();
            Context = new CMEntities();
     

        }

and everything is working fine. i do not know is there a better solution instead of continuous calling Context.Dispose() or it is the right way?

Termesz
  • 1
  • 3