我正在使用一个带有WPf / MVVM的小VB.NET应用程序和SQL Express-DB上的ADO.NET EF,我在尝试更新相关对象时遇到了问题:
我的数据库有三个表“tb_Actors”,“tb_Movies”和一个联结表“tb_movies_actors”。 EF设计器创建两个实体“Actors”和“Movies”,并根据外键正确设置其导航属性。因此能够提出一个绑定到viewModels属性的视图,该属性包含所有“Movies.Actors”。
我视图中的DataGrid正确显示所有actor,并且 - 如果它是一个新的actor,它不在我的数据库中 - 我能够将新的actor添加到电影中并将更改持久保存到数据库中。
但是,如果我想将一个actor添加到已经在我的数据库中的电影中,我将在我的tb_actors表中获得一个双重条目。首先,我将主键字段(名称和ID)设置为UNIQUE,但随后我的代码中断了。然后,我添加了一个更新例程,它检查电影的每个相关演员是否是一个已知的演员,并将“新演员”ID更改为“老演员”ID - 这也会中断。
有没有办法告诉EF它必须确定添加的相关对象(=已经添加到电影中的已知actor)是否已经存在于数据库中,因此它必须仅向连接表插入新条目,但不是相关的对象表?
我的下一步是分离相关对象并在我自己的数据访问代码中执行所有更新/插入...但由于我认为我的问题是围绕典型的EF用例,因此必须有更优雅的方式来处理更新关于相关的对象。
任何想法,答案和提示都非常感谢!
*编辑以下是相关的代码段*
1)我在MovieRepository数据访问类中有以下LoadMovies函数:
Private Function LoadMovies() As List(Of Movies)
movs = From m In dc.Movies.Include("Actors") Select m
Return movs.ToList
End Function
2)我的viewModel的以下属性公开了与特定电影相关的actor:
Public ReadOnly Property actors() As ICollectionView
Get
If evs Is Nothing Then
evs = New CollectionViewSource
evs.Source = _movie.Actors
End If
Return evs.View
End Get
End Property
3)在我的MovieDetail视图中,我有一个绑定到属性的数据网格:
<DataGrid Name="ActTestGrid" HorizontalAlignment="Left" VerticalAlignment="Stretch" ItemsSource="{Binding actors}" AutoGenerateColumns="False" Width="150" Height="120" Style="{StaticResource dgTemplate}" RowStyle="{StaticResource dgRowTemplate}" CellStyle="{StaticResource dgCellTemplate}" CanUserSortColumns="True" CanUserAddRows="True" CanUserDeleteRows="True" HeadersVisibility="None">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Path=name, UpdateSourceTrigger=PropertyChanged}" CanUserSort="true"/>
</DataGrid.Columns>
</DataGrid>
4)这是我的MovieRepository的updateMovie函数(截至目前):
Public Sub UpdateMovie(ByVal movie As Movies)
If movie Is Nothing Then
Throw New ArgumentNullException("Movie")
Else
dc.SaveChanges()
End If
End Sub
答案 0 :(得分:0)
EF做它做的事情,你不能告诉它做什么,也就是说它不会验证你的数据。 EF将插入您要插入的内容(或尝试)。在致电保存更改之前,您有责任进行数据验证。
为了解决这个问题,请考虑在视图中使用actor名称列表提供一个组合框。组合框的IsEditable
设置为true,文本属性绑定到ViewModel中的ActorName AS String
。如果用户选择现有的演员,EF将不会尝试插入新的演员。如果用户输入新名称,EF将创建一个新的演员。
以下是一些使用品牌名称的代码:
Public Property BrandName() As String
Get
Return _brandName
End Get
Set
_brandName = value.Trim()
If _brandName <> String.Empty Then
Dim b As Brand = _brands.ToList().Find(Function(br) br.BrandName.ToUpper() = _brandName.ToUpper())
If b Is Nothing Then
Brand = New Brand()
Brand.BrandName = _brandName
Else
Brand = b
End If
Else
Brand = Nothing
End If
CheckIsDirty()
RaisePropertyChanged("BrandName")
End Set
End Property
并且在观点上:
<ComboBox Grid.Row="1" Grid.Column="0" Height="28" HorizontalAlignment="Stretch" Margin="110,0,28,0" VerticalAlignment="Top" TabIndex="1" ItemsSource="{Binding Brands}" DisplayMemberPath="BrandName" Text="{Binding BrandName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsEditable="True"/>