我的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((,或者这是正确的方法?