C# GroupJoin产生了非常令人惊讶的结果
我有一个如下所示的数据库表:C# GroupJoin产生了非常令人惊讶的结果,c#,entity-framework,C#,Entity Framework,我有一个如下所示的数据库表: MenuItemID | int [Primary Key] 1 -----+ MenuItemName | nvarchar(100) | ParentID | int * -----+ 正如您在上面的示意图中所看到的,ParentID列指的是MenuItemID,因此我们可以获得分层输出 上表中的样本数据如下所示: MenuItemI
MenuItemID | int [Primary Key] 1 -----+
MenuItemName | nvarchar(100) |
ParentID | int * -----+
正如您在上面的示意图中所看到的,ParentID列指的是MenuItemID,因此我们可以获得分层输出
上表中的样本数据如下所示:
MenuItemID | int [Primary Key] 1 -----+
MenuItemName | nvarchar(100) |
ParentID | int * -----+
根据上面的示例数据,我希望输出如下:
Electronics
Mobile
Desktop
Laptop
Lenovo
Dell
Sports
Cricket
Football
Hockey
Stationary
Books
Pens
Pencils
Erasers
我尝试过的:
我尝试了下面提到的代码,其中我尝试使用GroupJoin扩展方法来实现所需的输出:
class Program
{
static void Main(string[] args)
{
OnlineShoppingEntities db = new OnlineShoppingEntities();
var x = db.MenuItems.GroupJoin(db.MenuItems,
m => m.MenuItemID,
m => m.ParentID,
(parentMenuItems, childMenuItems) => new
{
ParentMenuItems = parentMenuItems,
ChildMenuItems = childMenuItems
});
foreach (var v in x)
{
Console.WriteLine(v.ParentMenuItems.MenuItemName);
foreach (var m in v.ChildMenuItems)
{
Console.WriteLine("\t" + m.MenuItemName);
}
}
}
}
我得到的输出:
我对输出感到非常惊讶,因为我期望得到如上所述的所需输出。您的解决方案可以在以下位置找到: 我不会抽象代码,因为有太多的答案可以解决您的问题,我特别喜欢
AsHierarchy()
linq扩展方法,如对该问题的回答所示
var hierachy = dc.Employees.ToList().AsHierarchy(e => e.EmployeeID, e => e.ReportsTo);
最重要的是:
使用系统;
使用System.Collections.Generic;
使用System.Linq;
命名空间ScipBe.Common.LinqExtensions
{
//斯特凡·克鲁斯伯格,http://www.scip.be,2008年3月
///
///包含层次结构节点嵌套集合的层次结构节点类
///
///实体
公共类层次结构节点,其中T:class
{
公共T实体{get;set;}
公共IEnumerable子节点{get;set;}
公共整数深度{get;set;}
}
公共静态类LinqExtensionMethods
{
私有静态System.Collections.Generic.IEnumerable CreateHierarchy
(i可数Allitem,TEntity parentItem,
Func idProperty,Func parentIdProperty,int-depth),其中tenty:类
{
数不清的孩子;
if(parentItem==null)
childs=allItems.Where(i=>parentIdProperty(i).Equals(default(TProperty));
其他的
childs=allItems.Where(i=>parentIdProperty(i).Equals(idProperty(parentItem));
如果(childs.Count()>0)
{
深度++;
foreach(childs中的变量项)
yield返回新的HierarchyNode(){Entity=item,ChildNodes=CreateHierarchy
(allItems,item,idProperty,parentIdProperty,depth),depth=depth};
}
}
///
///LINQ IEnumerable AsHierachy()扩展方法
///
///实体类
///实体类的性质
///实体的平面集合
///对实体Id/密钥的引用
///对父Id/密钥的引用
///实体的层次结构
公共静态System.Collections.Generic.IEnumerable AsHierarchy
(此IEnumerable Allitem、Func idProperty、Func parentIdProperty)
地点:班级
{
返回CreateHierarchy(allItems,默认值(tenty),idProperty,parentIdProperty,0);
}
}
}
当然,如果您的数据只有3个级别,为了简单起见,您可以使用其他GroupJoin()
var hierarchical = menuItems.Where(m => m.ParentID == null)
.GroupJoin(menuItems,
m => m.MenuItemID,
m => m.ParentID,
(parentMenuItems, childMenuItems) => new
{
ParentMenuItems = parentMenuItems,
ChildMenuItems = childMenuItems.GroupJoin(menuItems,
m => m.MenuItemID,
m => m.ParentID,
(subParentMenuItems, subChildMenuItems) => new
{
ParentMenuItems = subParentMenuItems,
ChildMenuItems = subChildMenuItems
})
});
foreach(var menu in hierarchical)
{
Console.WriteLine(menu.ParentMenuItems.MenuItemName);
foreach(var submenu in menu.ChildMenuItems)
{
Console.WriteLine("\t" + submenu.ParentMenuItems.MenuItemName);
foreach(var subitem in submenu.ChildMenuItems)
{
Console.WriteLine("\t\t" + subitem.MenuItemName);
}
}
}
你实际上是在要求一个可传递的关系。。。你是怎么期望的?从根本上说,SQL不擅长表达这类东西,LINQ也不擅长表达。@JonSkeet如果我使用两个不同的表,那么我可以获得视频中提到的输出:@JonSkeet我应该使用什么来获得这种输出?我问这个问题是因为你刚才提到SQL不擅长表达这种东西,LINQ也不擅长。我不想看一个21分钟的视频来看看你做了什么。。。但我怀疑,即使有两个表,也无法轻松获取任意嵌套的组。不过,您可能想看看。您可能应该从数据库中以列表的形式检索所有必需的项,并在应用程序端创建层次关系。谢谢。您的两个答案都非常有用,但我将使用asherarch()linq扩展方法,因为级别不是固定的。为了得到输出,我使用了一个递归函数。但唯一的问题是我不知道:如何增加子菜单的左边距,以便在子菜单项的名称前看到一些空格。这样我就可以知道什么才是真正的父项,谁才是真正的子项。您将拥有Depth
属性来了解项目的级别。此扩展返回的项的类型为HierarchyNode
,因此您将有一个实体
属性来获取实际项,一个ChildNodes
属性来获取其子项,一个深度
属性来获取其层次结构中的深度/级别。