我之前问过这个问题但没有答案,我相信这可能是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。
所以我可以像这样写代码:(它不会做任何事情,我只是展示了一些例子)
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也是每个父对象的独生子对象。是的,一个资源可以有三个或更多的“父母”,如果你愿意,甚至可以有两个父亲,但是祖先会共享一个孩子吗?在我眼皮底下是不行的。所以不管怎样。我们必须从现实世界的场景中考虑这一点,这样才能从这一点开始有意义。
下面是我们开始学习的一些代码:
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是多么有限后承担了胎儿的位置。
这部分变得有点复杂了。如果
child.Quantity = 4
parent1.Quantity = 1
parent2.Quantity = 1我们如何知道或设置它,以便我们可以将2个子对象分配给parent1,2个子对象分配给parent2?
这只能通过向ResourceHierarchy表添加另一个quantity(int)列来完成,我们称之为"Required“,因此它看起来如下所示:
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中的关系,则在存储模型中如下所示。
<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功能会有帮助吗?
发布于 2010-10-14 04:56:37
所以我最终得到了一个令人惊讶的优雅的解决方案。
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重命名为祖先。
然而,在我可以像这样写代码之前:
Resource resource = new Resource();
resource.Descendants.Add(new Resource());
foreach (Resource descendant in resource.descendants)
{
descendant.Type = "Brawr";
List<Resource> ancestors = descendant.Ancestors.ToList();
}当然,这种方法不允许我访问所需的属性。
现在我必须做以下几件事:
Resource ancestor = new Resource();
Resource descendant = new Resource();
ResourceHierarchy rh = new ResourceHierarchy { Ancestor = ancestor, Descendant = descendant, Required = 1 };
ancestor.DescendantRelationships.Add(rh);但是检查一下,我现在可以像这样获得所需的属性:
int req = ancestor.DescendantRelationships.First().Required;可以将必填字段重命名为RequiredDescendants,因为Descendants不需要所需数量的祖先,只有祖先需要指定需要多少Descendant。
所以这是一个跳跃,但是一个优雅的跳跃。
请让我知道你的想法,特别是如果我忽略了一个问题或什么。
发布于 2010-10-14 22:50:51
需要注意的是……
当我们想要向资源添加子资源时,我们需要记住,DescendantRelationships为我们提供了一个层次结构,其中被引用的资源充当其他资源的子孙。
因此,为了将后代添加到资源中,我们必须执行以下操作:
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.
所以我们可以这样做:
// print descendant types
foreach (ResourceHierarchy rh in resource.AncestorRoles)
{
Console.WriteLine(rh.Descendant.ResourceType.Type);
}很抱歉更改了这么多的命名法,但我认为这有助于理解发生了什么。
发布于 2010-10-15 04:55:01
MSDN Libary链接:http://msdn.microsoft.com/en-us/library/ms742451.aspx的标题是PropertyPath XAML语法,并有一个标题为源遍历(绑定到集合层次结构)的部分。
这是我想要使用的HierarchicalDataTemplate:
<HierarchicalDataTemplate DataType="{x:Type Inventory:Resource}" ItemsSource="{Binding Path=AncestorRoles/Descendant}">
<CustomControls:ResourceTreeItem Type="{Binding ResourceType.Type}"/>
</HierarchicalDataTemplate>仅显示第一个资源。运行以下代码后,TreeView将在TreeView中显示一个ResourceTreeItem。
ObservableCollection<Resource> Resources = new ObservableCollection<Resource>
Resources.Add(new Resource { ResourceType.Type = "WidgetA" });
MyTreeView.ItemsSource = Resources;所以这是可行的。但是,当我运行以下代码时,TreeView没有更新。
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。
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:
<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不适合你。
https://stackoverflow.com/questions/3926042
复制相似问题