繁体   English   中英

索引超出查询范围

[英]Index was out of range exception in query

每条路线均按特定顺序包含位置。
例如:NY-> LA与LA-> NY不同。
我想编写一个获取位置数组并返回true或false的方法,而该方法是否存在具有相同位置和顺序的路由。
我需要使用linq来实现实体和实体框架(Route和Location是实体)。 这是我写的:

    public bool IsRouteExists(IList<LocationInRoute> locationsInRoute)
    {
        Route route = null;
        if (locationsInRoute.Count > 0)
        {
            var query = GetRoutesQuery().
                Where(x => x.Locations.Count() == locationsInRoute.Count);

            for (int i = 0; i < locationsInRoute.Count; i++)
            {
                long locationId = locationsInRoute[i].LocationId;
                query = query.Where(x => 
    x.Locations.ElementAt(i).LocationId == locationId); //THROWS EXCEPTION
            }
            route = query.SingleOrDefault();
        }
        return route!=null;
    }

我在标记的行中收到以下异常:

Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index

发生此异常的原因是什么?

编辑
执行route = query.SingleOrDefault();时会发生异常route = query.SingleOrDefault(); 并且异常抱怨Where(x => x.Locations.ElementAt(i).LocationId == locationId);

我相信这个查询是完全错误的。 首先,它不是linq-to-entities查询,并且永远不会因为linq to实体无法使用索引。 我认为比较有序序列将必须在memory = linq-to-objects中执行。

另一个问题是:

for (int i = 0; i < locationsInRoute.Count; i++)
{
    long locationId = locationsInRoute[i].LocationId;
    query = query.Where(x => x.Locations.ElementAt(i).LocationId == locationId);
}
route = query.SingleOrDefault();

我认为使用Linq,在循环中建立查询和推迟执行时这是已知的陷阱 -我相信这总是将locationId与最后一个元素进行比较。

在我看来,最有效的方法是使用表值参数传递您期望的序列并使用SQL游标比较存储过程中的序列的存储过程。

看起来您的x.Locations.Count()可能小于您的locationsInRoute.Count。 您确定不是吗? 我是说b / c您正在调用x.Locations.ElementAt(i),如果i> Count()会抛出该错误。

附带说明,一种更好的解决方案是重写相等性或在要唯一的类上实现IComparer,然后可以使用Any()和Contains()之类的东西进行测试。

如果索引超出范围异常,则必须表示locationsRoute集合中的元素数超过了IQueryable中的元素数。 如果尝试测试提供的列表中的每个位置都包含在路由中,则应该可以执行以下操作:

var locationIds = locationsInRoute.Select(l => l.LocationId);
query = query.Where(r => r.Locations.All(l => locationIds.Contains(l.LocationId)))

我猜想这与您对ElementQuery的使用有关,后者不能转换为SQL (请参阅“无转换的运算符”部分),以对IQueryable进行操作。 这将在第一次迭代中实现IQueryable的结果集,因此后续迭代Route项将无法访问其相关的Locations集。 这可能只应该在第二次迭代中发生,但是在任何情况下,LINQ的延迟执行本质所带来的诸多影响在我看来都不是完全清楚;-) HTH

您可以在SingleOrDefault处放置一个断点,并检查在该处执行的SQL语句,以查看为什么没有返回任何记录供SingleOrDefault查找。 尽管SQL可能很难看,具体取决于您拥有多少条路由。

感谢@Ladislav Mrnka的建议,以下是解决方案:

public class LocationSequenceEqual : IEqualityComparer<Location>
    {
        public bool Equals(Location x, Location y)
        {
            return x.Id == y.Id;
        }

        public int GetHashCode(Location obj)
        {
            return obj.Id.GetHashCode();
        }
    }

    public bool IsRouteExists(IList<LocationInRoute> locationsInRoute)
    {
        Route route = null;
        if (locationsInRoute.Count > 0)
        {
            var query = GetRoutesQuery().
                Where(x => x.Locations.Count() == locationsInRoute.Count);

            query = query.Where(x => x.Locations.OrderBy(l => l.Order).
                    Select(l => l.Location).SequenceEqual(locations, new LocationSequenceEqual()));
            route = query.FirstOrDefault();
        }
        return route!=null;
    }

如果“位置”具有上面所指定的“顺序”, 可以完全在(Linq to)SQL中完成:

public bool IsRouteExists(IList<LocationInRoute> locationsInRoute)
{
    Route route = null;
    if (locationsInRoute.Count == 0)
        return;

    var possibleRoutes = GetRoutesQuery().
        Where(x => x.Locations.Count() == locationsInRoute.Count);

    var db = GetDataContext(); //get a ref to the DataContext or pass it in to this function
    for (var i = 0; i < locationsInRoute.Length; i++)
    {
        var lcoationInRoute = locationsInRoute[i];
        possibleRoutes = possibleRoutes.Where(x => x.Locations.Any(l => l.Id == locationInRoute.Id && l.Order == locationInRoute.Order));
    }
    route = possibleRoutes.FirstOrDefault();

    return route!=null;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM