无法在对象 'dbo 中插入重复键。地址'



我的WPF应用程序名为";ContactManager";当我想将记录添加到数据库时。我有两个实体:

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; }

添加方法:

    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();
        }
    }

当数据库为空时,它正在工作,但在关闭并重新启动应用程序后,我得到以下异常:

SqlException:违反PRIMARY KEY约束"PK_dbo.Addresses"。无法在对象"dbo.Addresses"中插入重复的键。重复的键值为(1(。语句已终止。

我不明白为什么Sql服务器(或实体框架??(想要插入密钥1而不是下一个id…

如果我删除Addresses表中的行,它将再次工作,它将插入下一个/右侧addressid没有问题的行。(也就是说,由于前几行联系人,地址不是1而是5(

在我看来,(键等(表的模式是正确的,问题在于其他问题,可能是绑定或上下文,我对此一无所知。。。

(我做了一个简单的例子,省略了MVC,它正在工作。(

以下是MVC应用程序的代码:

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>

这可能是您的问题:

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

同时,您将在保存后处理DbContext。当EF DbContext遇到一个新实体(Contact(,其中包含一个您期望存在但未被跟踪的相关实体时,它会将该实体视为新实体。这种行为很可能是让你绊倒周围传递实体的根源。不再跟踪地址,因此尝试重新插入现有地址记录,并且由于它是指向联系人的FK,因此不会生成新值,而是尝试使用联系人的当前ID。

如果您的联系人可能有多个地址,Address通常会有一个PK(AddressID(和一个FK(ContactId(。或者Contact实体将具有AddressID。(如果一个地址可以为多个联系人提供服务(如果您希望一个联系人只有一个地址,则将地址字段放入contact本身,或者创建一个ContactAddressDetails表,如果您希望HasRequired/WithRequired关系位于单独的表中,则使用ContactId作为PK+FK。所需的关系将定义实体/架构上的字段的样子。

谢谢你的回答,但我发现了问题:

public ContactRepository()
        {

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

在调用GetAll((之后的ContactRepository构造函数中,我必须处理当前上下文:

private List<Contact> _contactStore = new List<Contact>();
        public ContactRepository()
        {
            _contactStore = this.GetAll();
            Context.Dispose();
            Context = new CMEntities();
     
        }

一切都很好。我不知道有没有更好的解决方案来代替连续调用Context.Dispose((,或者这是正确的方法?

最新更新