使用 EF POCO 对象获取只读数据绑定

发布于 2024-10-25 20:10:59 字数 2310 浏览 2 评论 0原文

我将 EF4 与 WPF 一起使用。我以主从样式将数据绑定到 DataGrid。想想 Northwind 客户 ->订单->订单详情。

我发现当我使用 POCO 对象时,Orders 和 OrderDetails 网格是只读的。如果我恢复使用设计器生成的实体,它们将变得可编辑。

绑定 XAML 如下所示:(

<Window.Resources>
    <CollectionViewSource x:Key="CustomersViewSource" d:DesignSource="{d:DesignInstance my:Customer, CreateList=True}" />
    <CollectionViewSource x:Key="CustomersOrdersViewSource" Source="{Binding Path=Orders, Source={StaticResource CustomersViewSource}}" />
</Window.Resources>
<Grid DataContext="{StaticResource CustomersViewSource}">

    <DataGrid ItemsSource="{Binding}" >
    <DataGrid ItemsSource="{Binding Source={StaticResource CustomersOrdersViewSource}}" >

当然,我删除了与数据绑定无关的属性。)

然后是用于绑定上下文实例的标准表单加载事件:

Dim NorthwindEntities As BindTest.NorthwindEntities = New BindTest.NorthwindEntities()
Dim CustomersViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("CustomersViewSource"), System.Windows.Data.CollectionViewSource)
CustomersViewSource.Source = NorthwindEntities.Customers

网格填充,但如果我使用我的POCO 对象,如果它们是标准 EF 生成的对象,则可编辑。

关键似乎在于实体的导航属性。我的 POCO 对象使用:

Public Overridable Property Orders() As ICollection(Of Order)
    Get
        If _Orders Is Nothing Then  _Orders = New HashSet(Of Order)
   Return _Orders
    End Get
    Set(ByVal value As ICollection(Of Order))
        _Orders = value
    End Set
End Property

而 EF 对象要复杂得多:

<XmlIgnoreAttribute()>
<SoapIgnoreAttribute()>
<DataMemberAttribute()>
<EdmRelationshipNavigationPropertyAttribute("NorthwindModel", "FK_Order_Details_Orders", "Orders")>
Public Property Order() As Order
    Get
        Return CType(Me, IEntityWithRelationships).RelationshipManager.GetRelatedReference(Of Order)("NorthwindModel.FK_Order_Details_Orders", "Orders").Value
    End Get
    Set
        CType(Me, IEntityWithRelationships).RelationshipManager.GetRelatedReference(Of Order)("NorthwindModel.FK_Order_Details_Orders", "Orders").Value = value
    End Set
End Property

由于缺乏更好的措辞,EntityCollection 类型的属性似乎有一些魔力。 ICollection 不是只读接口,HashSet 也不是只读。

关于如何让 POCO 在这里工作有什么想法吗?或者我是否坚持使用 EF 派生对象? (使单元测试变得困难。)

谢谢。

I am using EF4 with WPF. I am databinding to the DataGrid in a Master-Detail style. Think of the Northwind Customers -> Orders -> OrderDetails.

What I am finding is that when I use POCO objects, the Orders and OrderDetails grids are read-only. If I revert to using the designer generated entities they become editable.

The binding XAML looks like this:

<Window.Resources>
    <CollectionViewSource x:Key="CustomersViewSource" d:DesignSource="{d:DesignInstance my:Customer, CreateList=True}" />
    <CollectionViewSource x:Key="CustomersOrdersViewSource" Source="{Binding Path=Orders, Source={StaticResource CustomersViewSource}}" />
</Window.Resources>
<Grid DataContext="{StaticResource CustomersViewSource}">

    <DataGrid ItemsSource="{Binding}" >
    <DataGrid ItemsSource="{Binding Source={StaticResource CustomersOrdersViewSource}}" >

(I've removed attributes not relevant to databinding, of course.)

Then there's the standard form load event to bind the context instance:

Dim NorthwindEntities As BindTest.NorthwindEntities = New BindTest.NorthwindEntities()
Dim CustomersViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("CustomersViewSource"), System.Windows.Data.CollectionViewSource)
CustomersViewSource.Source = NorthwindEntities.Customers

The grids populate, but the second is readonly if I'm using my POCO objects, editable if they are the standard EF generated objects.

The key seems to be in the navigation properties of the entities. My POCO objects use:

Public Overridable Property Orders() As ICollection(Of Order)
    Get
        If _Orders Is Nothing Then  _Orders = New HashSet(Of Order)
   Return _Orders
    End Get
    Set(ByVal value As ICollection(Of Order))
        _Orders = value
    End Set
End Property

Whereas the EF objects are much more complicated:

<XmlIgnoreAttribute()>
<SoapIgnoreAttribute()>
<DataMemberAttribute()>
<EdmRelationshipNavigationPropertyAttribute("NorthwindModel", "FK_Order_Details_Orders", "Orders")>
Public Property Order() As Order
    Get
        Return CType(Me, IEntityWithRelationships).RelationshipManager.GetRelatedReference(Of Order)("NorthwindModel.FK_Order_Details_Orders", "Orders").Value
    End Get
    Set
        CType(Me, IEntityWithRelationships).RelationshipManager.GetRelatedReference(Of Order)("NorthwindModel.FK_Order_Details_Orders", "Orders").Value = value
    End Set
End Property

For the lack of some better wording, there seems to be some magic in either the attributes for the EntityCollection type. ICollection isn't a readonly interface and a HashSet isn't readonly either.

Any ideas about how to get POCO to work here or am I stuck with EF derived objects? (Makes unit testing difficult.)

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

久夏青 2024-11-01 20:10:59

问题可能是 OrdersOrderDetails 集合的类型为 ICollection / HashSet在你的 POCO 示例中。 WPF 数据网格在内部不直接使用集合,而是使用关联的“集合视图”。当您将集合绑定到 DataGrid 时,WPF 绑定引擎会根据集合的类型创建此内部集合视图。

如果您的集合仅实现 IEnumerable 或仅实现 ICollection,则创建的集合视图的类型为 CollectionView,该类不强>实现IEditableCollectionView。这就是当您将 HashSet 绑定到 DataGrid 时无法编辑 DataGrid 的原因。

DataGrid 需要一个实现 IEditableCollectionView 的集合视图以允许编辑。例如,这是ListCollectionView(它也派生自CollectionView)。如果您的源集合实现 IList 接口,WPF 将创建这种类型的集合视图。

因此,要解决此问题,您应该将 POCO 的 Orders 属性的类型更改为 IList

Public Overridable Property Orders() As IList(Of Order)
    Get
        If _Orders Is Nothing Then  _Orders = New List(Of Order)
        Return _Orders
    End Get
    Set(ByVal value As IList(Of Order))
        _Orders = value
    End Set
End Property

编辑

根据下面 @Allon Guralnek 的评论需要实现非通用IList接口才能获得可编辑的DataGrid。 List(Of T) 就是这种情况,因此上面的代码仍然有效。仅实现通用IList(Of T)但未实现非通用IList的其他实现不会使DataGrid可编辑。

The problem is likely that the Orders and OrderDetails collections are of type ICollection<T> / HashSet<T> in your POCO example. The WPF datagrid internally does not work with the collection directly but rather with an associated "collection view". When you bind the collection to the DataGrid the WPF binding engine creates this internal collection view based on the type of the collection.

If your collection implements only IEnumerable or only ICollection the type of the created collection view is CollectionView, a class which does not implement IEditableCollectionView. That's the reason why you can't edit the DataGrid when you bind a HashSet to it.

The DataGrid needs a collection view which implements IEditableCollectionView to allow editing. This is for example the ListCollectionView (which also derives from CollectionView). WPF creates this type of collection view if your source collection implements the IList interface.

So, to fix the problem you should change the type of the Orders property of your POCO to IList:

Public Overridable Property Orders() As IList(Of Order)
    Get
        If _Orders Is Nothing Then  _Orders = New List(Of Order)
        Return _Orders
    End Get
    Set(ByVal value As IList(Of Order))
        _Orders = value
    End Set
End Property

Edit

According to @Allon Guralnek's comment below it is necessary to implement the non-generic IList interface to get an editable DataGrid. This is the case for List(Of T), therefore the code above will still work. Other implementations which only implement the generic IList(Of T) but not the non-generic IList won't make the DataGrid editable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文