首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实体框架自参考多对多有效负载(物料清单BOM )

实体框架自参考多对多有效负载(物料清单BOM )
EN

Stack Overflow用户
提问于 2010-10-14 00:53:07
回答 3查看 2K关注 0票数 2

我之前问过这个问题但没有答案,我相信这可能是EF最离奇的实现,尽管它很实用。这是我之前的帖子:

Entity Framework Self Referencing Hierarchical Many To Many

我决定用额外的关键字Payload和更清晰的理解再问一遍。

在Apress出版物: Entity Framework4.0 Recipes: A Problem-Solution Approach中,pg上的配方2-6。26的标题是使用有效负载建模多对多关系。菜谱2-7的标题是建模自引用关系。

阅读这篇文章将为你了解我的问题奠定基础,不同的是,我有一个自我引用的多对多有效负载,这在书中或据我所知的宇宙中的任何地方都没有讨论过。

简单地说,我有一个带有ID和Type字段的资源表。我还有一个ResourceHierarchy表,它用作连接表或桥接表,因为它有一个由Parent_ID和Child_ID组成的复合主键和一个复合外键。因此,资源实体既可以作为子资源,也可以作为父资源,或者两者兼而有之。

到目前为止,实体框架应该已经生成了资源实体,但是ResourceHierarchy实体实际上对EDMX设计器是隐藏的,因为在EDMX文件中,它只被视为一个关系,而不是一个实体。

生成的资源实体将具有导航属性,如Resources和Resources1,我将其重命名为Parents和Children。

所以我可以像这样写代码:(它不会做任何事情,我只是展示了一些例子)

代码语言:javascript
复制
List<Resource> listResources = Context.Resouces.ToList()
foreach (Resource resc in listResources)
{
List<Resource> listParents = resc.Parents.ToList()
List<Resource> listChildren = resc.Children.ToList()
foreach (Resource parent in listParents)
{
Console.WriteLine(parent.Type);
}
foreach (Resource child in listChildren)
{
Console.WriteLine(child.Type);
}
resc.Children.Add(new Resource());
Console.WriteLine(resc.Parents.First().Children.First().Type);
}

假设我有一个资源正在被另外两个资源共享。另外两个资源将是所述资源的父级。Said Resource也是每个父对象的独生子对象。是的,一个资源可以有三个或更多的“父母”,如果你愿意,甚至可以有两个父亲,但是祖先会共享一个孩子吗?在我眼皮底下是不行的。所以不管怎样。我们必须从现实世界的场景中考虑这一点,这样才能从这一点开始有意义。

下面是我们开始学习的一些代码:

代码语言:javascript
复制
Resource parent1 = new Resource();
Resource parent2 = new Resource();
Resource child = new Resource();

parent1.Type = "WidgetA";
parent2.Type = "WidgetB";
child.Type = "1/4 Screw"

parent1.Children.Add(child);
parent2.Children.Add(child);

Product product1 = new Product();
Product product2 = new Product();

product1.Resources.Add(parent1);
product2.Resources.Add(parent2);

所以我们有两个有螺丝的小部件。WidgetA和WidgetB在网站上作为产品列出。如果WidgetA出售了,WidgetB的螺丝钉会发生什么?所以现在您可以看到,我们需要Resource实体上的Quantity属性。

快进几个月后,我目前在我的项目中,并在意识到EF是多么有限后承担了胎儿的位置。

这部分变得有点复杂了。如果

代码语言:javascript
复制
child.Quantity = 4
parent1.Quantity = 1
parent2.Quantity = 1

我们如何知道或设置它,以便我们可以将2个子对象分配给parent1,2个子对象分配给parent2?

这只能通过向ResourceHierarchy表添加另一个quantity(int)列来完成,我们称之为"Required“,因此它看起来如下所示:

代码语言:javascript
复制
Parent_ID int not null,
Child_ID int not null,
Required int not null default 1

因此,我们已经将有效负载附加到数据库中的ResourceHierarchy实体。如果我们从EDMX designer重新生成模型,ResourceHierarchy不再是一个关系,而是一个实体。如果我选择只刷新EDMX designer中的ResourceHierarchy表,我可以在存储模型中看到Required属性,但在概念模型或映射模型中看不到它,因为ResourceHierarchy将是一个关系。但是,如果我删除资源表和ResourceHierarchy表并重新生成它们,ResourceHierarchy表现在将显示为Required列,并且它现在是一个实体。

可以使用这种设置,但它比简单地访问ResourceHierarchy关系和检索所需属性要困难得多。即使ResourceHierarchy EntityType在存储模型中包含了Required属性,在访问AssociationSet之后,我也无法从代码中访问Required属性。如果ResourceHierarchy表是EF中的关系,则在存储模型中如下所示。

代码语言:javascript
复制
<EntityType Name="ResourceHierarchy">
          <Key>
            <PropertyRef Name="Parent_ID" />
            <PropertyRef Name="Child_ID" />
          </Key>
          <Property Name="Parent_ID" Type="int" Nullable="false" />
          <Property Name="Child_ID" Type="int" Nullable="false" />
          <Property Name="Required" Type="int" Nullable="false" />
</EntityType>

如果我尝试合并生成的EDMX文件,就会得到错误消息,告诉我ResourceHierarchy可以是实体,也可以是关系,但不能同时是两者。

这被称为具有有效负载的多对多。在EF中,试图使用自引用层次结构来实现这一点是一场噩梦。我正在使用VS2010、SQL2008和.NET 4.0框架。

我的概念是,我想拥有由资源组成的产品,这些资源本身由其他资源组成,或者用于组成其他资源,并且每个资源都需要一定数量的资源。它基本上是一个物料清单BOM表。EF是否不支持BOM模型?

SQL Server 2008中的新HIERARCHYID功能会有帮助吗?

EN

回答 3

Stack Overflow用户

发布于 2010-10-14 04:56:37

所以我最终得到了一个令人惊讶的优雅的解决方案。

代码语言:javascript
复制
CREATE TABLE Resource
(
ID INT NOT NULL,
Type VARCHAR(25) NOT NULL
)

ALTER TABLE Resource
ADD CONSTRAINT PK_Resource PRIMARY KEY (ID)

CREATE TABLE ResourceHierarchy
(
Ancestor_ID INT NOT NULL,
Descendant_ID INT NOT NULL,
Required INT NOT NULL DEFAULT 1
)

ALTER TABLE ResourceHierarchy
ADD CONSTRAINT PK_ResourceHierarchy PRIMARY KEY (Ancestor_ID, Descendant_ID)
ALTER TABLE ResourceHierarchy
ADD CONSTRAINT FK_Ancestors FOREIGN KEY (Ancestor_ID) REFERENCES Resource (ID)
ALTER TABLE ResourceHierarchy
ADD CONSTRAINT FK_Descendants FOREIGN KEY (Descendant_ID) REFERENCES Resource (ID)

在生成EDMX时,我将资源实体导航属性从ResourceHierarchy重命名为DescendantRelationships,将ResourceHierarchy1重命名为AncestorRelationships。然后,我将ResourceHierarchy实体导航属性从资源重命名为Descendant,将Resource1重命名为祖先。

然而,在我可以像这样写代码之前:

代码语言:javascript
复制
Resource resource = new Resource();
resource.Descendants.Add(new Resource());
foreach (Resource descendant in resource.descendants)
{
descendant.Type = "Brawr";
List<Resource> ancestors = descendant.Ancestors.ToList();
}

当然,这种方法不允许我访问所需的属性。

现在我必须做以下几件事:

代码语言:javascript
复制
Resource ancestor = new Resource();
Resource descendant = new Resource();

ResourceHierarchy rh = new ResourceHierarchy { Ancestor = ancestor, Descendant = descendant, Required = 1 };

ancestor.DescendantRelationships.Add(rh);

但是检查一下,我现在可以像这样获得所需的属性:

代码语言:javascript
复制
int req = ancestor.DescendantRelationships.First().Required;

可以将必填字段重命名为RequiredDescendants,因为Descendants不需要所需数量的祖先,只有祖先需要指定需要多少Descendant。

所以这是一个跳跃,但是一个优雅的跳跃。

请让我知道你的想法,特别是如果我忽略了一个问题或什么。

票数 2
EN

Stack Overflow用户

发布于 2010-10-14 22:50:51

需要注意的是……

当我们想要向资源添加子资源时,我们需要记住,DescendantRelationships为我们提供了一个层次结构,其中被引用的资源充当其他资源的子孙。

因此,为了将后代添加到资源中,我们必须执行以下操作:

代码语言:javascript
复制
Resource resource = new Resource { Type = "WidgetA" };
Resource descendant = new Resource { Type = "Screw" };
resource.AncestorRelationships.Add(new ResourceHierarchy { Descendant = descendant, Required = 1 };

当然,这一切都取决于你如何命名你的导航属性,我只是说要小心。为了添加子元素,AncestorRelationships将成为go to navigation属性,反之亦然。更好的做法可能是将AncestorRelationships重命名为AncestorRoles,将DescendantRelationships重命名为DescendantRoles。

AncestorRoles将转换为ResourceHierarchiesWhereCurrentResourceIsAnAncestor.DescendantRoles将转换为ResourceHierarchiesWhereCurrentResourceIsADescendant.

所以我们可以这样做:

代码语言:javascript
复制
// print descendant types
foreach (ResourceHierarchy rh in resource.AncestorRoles)
{
Console.WriteLine(rh.Descendant.ResourceType.Type);
}

很抱歉更改了这么多的命名法,但我认为这有助于理解发生了什么。

票数 0
EN

Stack Overflow用户

发布于 2010-10-15 04:55:01

MSDN Libary链接:http://msdn.microsoft.com/en-us/library/ms742451.aspx的标题是PropertyPath XAML语法,并有一个标题为源遍历(绑定到集合层次结构)的部分。

这是我想要使用的HierarchicalDataTemplate:

代码语言:javascript
复制
<HierarchicalDataTemplate DataType="{x:Type Inventory:Resource}" ItemsSource="{Binding Path=AncestorRoles/Descendant}">
<CustomControls:ResourceTreeItem Type="{Binding ResourceType.Type}"/>
</HierarchicalDataTemplate>

仅显示第一个资源。运行以下代码后,TreeView将在TreeView中显示一个ResourceTreeItem。

代码语言:javascript
复制
ObservableCollection<Resource> Resources = new ObservableCollection<Resource>
Resources.Add(new Resource { ResourceType.Type = "WidgetA" });
MyTreeView.ItemsSource = Resources;

所以这是可行的。但是,当我运行以下代码时,TreeView没有更新。

代码语言:javascript
复制
Resource resource = MyTreeView.Items[0] as Resource;
resource.AncestorRoles.Add( new ResourceHierarchy { Descendant = new Resource { ResourceType = "Screw" }, Required = 1 } )

即使我获得TreeView.ItemsSource的CollectionViewSource并调用Refresh(),它也不会显示出来。我三次检查了它们的关系,它们都在那里。

我认为这是PropertyPath遍历语法的一个错误。

解决方案是向资源部分类声明添加一个TreeParent属性,并利用3 ValueConverters,这是一个很长的故事,但这是因为数据上下文在资源和ResourceHierarchy之间自然交替。RequiredConverter检查TreeParent并找到所需的属性Payload。

代码语言:javascript
复制
class ValidatorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Resource resource = value as Resource;
            ResourceHierarchy rh = value as ResourceHierarchy;
            if (resource != null)
            {
                value = resource;
            }
            else if (rh != null)
            {
                value = rh.Descendant;
            }

            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    class ResourceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Resource resource = value as Resource;
            ResourceHierarchy hierarchy = value as ResourceHierarchy;

            if (resource != null)
            {
                if (resource.AncestorRoles.Count > 0)
                {
                    value = resource.AncestorRoles;
                }
                else
                {
                    value = resource;
                }
            }
            else if (hierarchy != null)
            {
                value = hierarchy.Descendant.AncestorRoles;
            }

            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    class RequiredConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Resource resource = (Resource)value;
            Resource parent = ((Resource)value).TreeParent as Resource;
            if (parent != null)
            {
                value = parent.AncestorRoles.Where(p => p.Descendant == resource).First().Required;
            }
            else
            {
                value = 0;
            }

            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

下面是最终的HierarchicalDataTemplate:

代码语言:javascript
复制
<HierarchicalDataTemplate DataType="{x:Type Inventory:Resource}" ItemsSource="{Binding Path=., Converter={StaticResource resourceconv}}">
<StackPanel DataContext="{Binding Path=., Converter={StaticResource validatorconv}}">
<CustomControls:ResourceTreeItem Required="{Binding Path=., Converter={StaticResource requiredconv}}" Free="{Binding Free}" OnHand="{Binding OnHand, Mode=TwoWay}" Type="{Binding ResourceType.Type}"/>
</StackPanel>
</HierarchicalDataTemplate>

StackPanel仅用于添加另一个DataContext层。我留在了Free、OnHand和Type属性中,这样您就可以看到3个属性正在从StackPanels DataContext接收它们的绑定,而Required属性正在像个狂人一样做它的事情。

因此,教训是,如果你需要一个有效载荷,也许EF不适合你。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3926042

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档