C# 返回重复项的递归linq结果

C# 返回重复项的递归linq结果,c#,asp.net-mvc,entity-framework,linq,linq-to-entities,C#,Asp.net Mvc,Entity Framework,Linq,Linq To Entities,这个问题是我上周问的一个问题的基础:。那篇文章给出的答案产生了我所需要的;基于父级的位置及其子级的独特列表。我们需要使用我们自己的位置模型,所以我们创建了一个,从那以后,我得到了重复的结果。我们的模型非常基本: class LocationModel { public int LocationID { get; set; } public int ParentLocationID { get; set; } public string LocationName { get

这个问题是我上周问的一个问题的基础:。那篇文章给出的答案产生了我所需要的;基于父级的位置及其子级的独特列表。我们需要使用我们自己的位置模型,所以我们创建了一个,从那以后,我得到了重复的结果。我们的模型非常基本:

class LocationModel
{
    public int LocationID { get; set; }
    public int ParentLocationID { get; set; }
    public string LocationName { get; set;}
}
如果您将其与EF创建的实体进行比较,我只是删除了所有我们不需要/使用的字段(参见上面的链接)。因此,我修改了linq语句,改为使用此新模型:

DBEntities db = new DBEntities();

public IEnumerable<LocationModel> GetAllChildLocations(int parentId)
{
    var locations = (from l in db.Locations
                        where l.ParentLocationID == parentId ||
                        l.LocationID == parentId
                        select new LocationModel()
                        {
                            LocationID = l.LocationID,
                            ParentLocationID = l.ParentLocationID,
                            LocationName = l.LocationName
                        }).ToList();

    var children = locations.AsEnumerable()
                            .Union(db.Locations.AsEnumerable()
                            .Where(x => x.ParentLocationID == parentId)
                            .SelectMany(y => GetAllChildLocations(y.LocationID)))
                            .ToList();
    return children.OrderBy(l => l.LocationName);
}
DBEntities db=newdbentities();
公共IEnumerable GetAllChildLocations(int parentId)
{
变量位置=(从数据库位置中的l开始
其中l.ParentLocationID==parentId||
l、 LocationID==父ID
选择新位置模型()
{
LocationID=l.LocationID,
ParentLocationID=l.ParentLocationID,
LocationName=l.LocationName
}).ToList();
var children=locations.AsEnumerable()
.Union(db.Locations.AsEnumerable()
.Where(x=>x.ParentLocationID==parentId)
.SelectMany(y=>GetAllChildLocations(y.LocationID)))
.ToList();
返回children.OrderBy(l=>l.LocationName);
}
当我在VisualStudio或LinqPad中运行它时,我现在得到了副本。以下是不产生重复项的原始代码:

public IEnumerable<Location> GetAllChildLocations(int parentId)
{
    var locations = (from l in db.Locations
                        where l.ParentLocationID == parentId ||
                        l.LocationID == parentId
                        select l).ToList();

    var child = locations.AsEnumerable()
                        .Union(db.Locations.AsEnumerable()
                        .Where(x => x.ParentLocationID == parentId)
                        .SelectMany(y => GetAllChildLocations(y.LocationID)))
                        .ToList();
    return child;
}
public IEnumerable GetAllChildLocations(int-parentId)
{
变量位置=(从数据库位置中的l开始
其中l.ParentLocationID==parentId||
l、 LocationID==父ID
选择l).ToList();
var child=locations.AsEnumerable()
.Union(db.Locations.AsEnumerable()
.Where(x=>x.ParentLocationID==parentId)
.SelectMany(y=>GetAllChildLocations(y.LocationID)))
.ToList();
返回儿童;
}
为什么我使用自己的模型与EF生成的模型相比会产生重复?它是否与EF模型和我的模型没有的自动生成字段有关

为什么我使用自己的模型与EF生成的模型相比会产生重复

因为您使用的是
Enumerable.Union
方法,默认情况下该方法使用引用相等。EF
DbContext
change tracker在内部保留(跟踪)具有相同主键的已加载实体对象实例(即使您通过单独的数据库查询检索它们),因此引用相等有效。对于由查询
select
操作符创建的
newlocationmodel
实例,这是不可能的

解决此问题的一种方法是在
LocationModel
类中实现
GetHashCode
Equals
。但总的来说,我不喜欢递归子对象检索的实现和
Union
的使用-肯定有更好的方法,但这超出了这个问题的范围(但对于链接)

对我来说,邪恶的根源是以下情况

where l.ParentLocationID == parentId || l.LocationID == parentId
它同时选择项及其子项,导致结果集中出现重复项,然后应该通过
Union
方法消除重复项。良好的实现根本不会生成重复项

为什么我使用自己的模型与EF生成的模型相比会产生重复

因为您使用的是
Enumerable.Union
方法,默认情况下该方法使用引用相等。EF
DbContext
change tracker在内部保留(跟踪)具有相同主键的已加载实体对象实例(即使您通过单独的数据库查询检索它们),因此引用相等有效。对于由查询
select
操作符创建的
newlocationmodel
实例,这是不可能的

解决此问题的一种方法是在
LocationModel
类中实现
GetHashCode
Equals
。但总的来说,我不喜欢递归子对象检索的实现和
Union
的使用-肯定有更好的方法,但这超出了这个问题的范围(但对于链接)

对我来说,邪恶的根源是以下情况

where l.ParentLocationID == parentId || l.LocationID == parentId
它同时选择项及其子项,导致结果集中出现重复项,然后应该通过
Union
方法消除重复项。良好的实现根本不会生成重复项