Индекс был вне диапазона исключения в запросе - PullRequest
0 голосов
/ 18 мая 2011

каждый маршрут содержит местоположения в определенном порядке.
Например: NY -> LA отличается от LA -> NY.
Я хотел бы написать метод, который получает массив местоположений и возвращает истину или ложь, независимо от того, существует ли маршрут с теми же местоположениями и порядком.
Мне нужно сделать это с помощью 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

В чем причина этого исключения?

EDIT
Исключение возникает при выполнении route = query.SingleOrDefault();, а исключение жалуется на Where(x => x.Locations.ElementAt(i).LocationId == locationId);.

Ответы [ 6 ]

1 голос
/ 18 мая 2011

Я считаю, что этот запрос совершенно неверный. Во-первых, это не запрос linq-to-entity, и он никогда не будет выполнен, потому что linq to entity не может работать с индексами. Я думаю, что сравнение упорядоченных последовательностей должно быть выполнено в 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 для сравнения последовательностей в хранимой процедуре.

0 голосов
/ 19 мая 2011

Если Местоположение имеет Порядок, как вы указали выше, это может быть полностью выполнено в (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;
}
0 голосов
/ 18 мая 2011

Благодаря совету @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;
    }
0 голосов
/ 18 мая 2011

Я предполагаю, что это связано с вашим использованием ElementAt, который не может быть переведен в SQL (см. Раздел «Операторы без перевода»), для работы с вашим IQueryable.Это материализует набор результатов IQueryable на первой итерации, поэтому последующие итерации элементы Route не смогут получить доступ к своим связанным наборам Locations.Вероятно, это должно произойти только на второй итерации, но бесчисленные последствия природы отложенного выполнения LINQ мне не всегда понятны ;-) HTH

Вы можете установить точку останова в SingleOrDefault и проверитьSQL-оператор, который он там выполняет, чтобы понять, почему не возвращена запись для поиска SingleOrDefault.Хотя SQL может быть довольно уродливым, в зависимости от того, сколько у вас маршрутов.

0 голосов
/ 18 мая 2011

Если вы получаете индекс вне диапазона исключений, это должно означать, что количество элементов в коллекции locationRoute превышает количество элементов в IQueryable.Если вы пытаетесь проверить, что каждое местоположение в указанном списке содержится в маршруте, вы должны сделать что-то вроде:

var locationIds = locationsInRoute.Select(l => l.LocationId);
query = query.Where(r => r.Locations.All(l => locationIds.Contains(l.LocationId)))
0 голосов
/ 18 мая 2011

Похоже, что ваш x.Locations.Count () может быть меньше вашего местоположения InRoute.Count. ты уверен, что это не так? Я говорю, что b / c вы вызываете x.Locations.ElementAt (i), который будет выдавать, если i> Count ().

Как заметка, лучшим решением для того, что вы делаете, является переопределение равенства или внедрение IComparer в вашем классе, который вы хотите уникальным, и затем вы можете использовать такие вещи, как Any () и Contains () для вашего теста. 1003 *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...