索引在查询中超出范围异常

时间:2011-05-18 02:47:40

标签: c# .net entity-framework linq-to-entities

每条路线都按特定顺序包含地点 例如: 纽约 - > 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();时异常苛刻且异常抱怨Where(x => x.Locations.ElementAt(i).LocationId == locationId);

6 个答案:

答案 0 :(得分:1)

我相信这个查询是完全错误的。首先,它不是linq-to-entities查询,它永远不会是因为linq to entities无法使用索引。我认为比较有序序列必须在内存中执行= 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,查询内置循环和延迟执行时这是known gotcha - 我相信这总是将locationId与最后一个元素进行比较。

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

答案 1 :(得分:0)

看起来您的x.Locations.Count()可能小于您的locationsInRoute.Count。你确定不是吗?我说b / c你正在调用x.Locations.ElementAt(i),如果i&gt;计数()。

作为旁注,对你正在做的事情的一个更好的解决方案是重写相等或在你的类上实现你想要唯一的IComparer,然后你可以使用Any()和Contains()之类的东西进行测试。 / p>

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

我猜这与你使用ElementAt, which can't be translated to SQL(参见没有翻译的操作员部分)有关,可以对你的IQueryable进行操作。这将在第一次迭代时实现IQueryable的结果集,因此后续迭代路径项将无法访问其相关的位置集。这应该只发生在第二次迭代中,但LINQ的延迟执行性质的无数含义在我看来并不完全清楚;-) HTH

您可以在SingleOrDefault中放置一个断点并检查它在那里执行的SQL语句,以查看为什么没有返回SingleOrDefault要查找的记录。虽然SQL可能非常难看,具体取决于您拥有的路由数量。

答案 4 :(得分:0)

感谢@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;
    }

答案 5 :(得分:0)

如果您在上面指出的位置有订单,则此可以完全在(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;
}