Я два полных дня возился с простой проблемой.
У меня есть база данных с объектами Product и ProductLocationHistory. ProductLocationHistory имеет ссылку либо на хранилище, либо на контакт, либо на отношение. Каждый раз, когда продукт перемещается, он получает новую запись, чтобы можно было отследить прошлое продукта. Таким образом, текущее местоположение является последней записью, определяемой полем DateCreation в ProductLocationHistory.
Пример:
var storehousesWithBorrowedItems = productService.GetAllProducts()
.Select(p => p.ProductLocationHistories
.SingleOrDefault(plh => plh.DateCreation == p.ProductLocationHistories.Max(grp => grp.DateCreation)))
.Select(plh => plh.Storehouse)
.Distinct();
Это все склады, на которых в данный момент есть товар.
Конечно, очень неудобно записывать это в код все время, когда мне нужно определить текущее местоположение продукта. На мой взгляд, ссылка на текущую историю ProductLocationHistory в Product абсолютно нежелательна из-за возможных проблем с согласованностью. Я бы предпочел что-то вроде:
Product.ProductLocationHistories.Current();
Итак, я попытался:
public static ProductLocationHistory Current(this EntitySet<ProductLocationHistory> plhs)
{
return plhs.SingleOrDefault(plh => plh.DateCreation == plhs.Max(grp => grp.DateCreation));
}
Это не работает с запрашиваемыми, так как я получаю «Текущий не поддерживает перевод в sql», и поскольку комбинация Product и ProductLocationHistory часто является «началом» запроса, я хочу оставаться IQueryable, а не сразу в IEnumerable и иметь запрос для каждого продукта, чтобы определить текущее местоположение! Не говоря уже о том, что последует дальше... Часто используется текущая запись в журнале для любого объекта, и не имеет большого значения, насколько сложна функция .Current(), пока она работает и остается доступной для запросов. Я надеялся, что моя функция .Current(...) будет работать, поскольку базовый код можно запрашивать, но я все равно получаю исключение. Я не получаю исключения, когда код встроен, как в первом примере.
Я рассмотрел такие возможности, как Func, ProductLocationHistory>>, а также с помощью Expression‹...>, но не смог найти пример того, что ищу. Решение вида Product.CurrentProductLocationHistory() может быть даже лучше. Абсолютно лучшее решение было бы еще более общим и имело бы форму:
Current<T> (IQueryable<T> collection, string field) { return entity with max field of collection }
Помощь будет очень признательна, я пробовал это в течение длительного времени, и я уверен, что это должно быть возможно, поскольку внутренние функции самого LINQ - Any, First, Count, Max - также остаются доступными для запросов, если это необходимо.
Обновить
В настоящее время выполняются следующие работы:
Expression<Func<Product, ProductLocationHistory>> expression = IQueryable.Current(null);
var ken = productService.GetAllProducts()
.Where(p => p.OnLoan)
.Select(expression)
.Where(plh => plh.Storehouse != null)
.Select(plh => plh.Storehouse)
.Distinct();
public static Expression<Func<Product, ProductLocationHistory>> Current(this EntitySet<ProductLocationHistory> productLocationHistories)
{
Expression<Func<Product, ProductLocationHistory>> expression = p => p.ProductLocationHistories
.SingleOrDefault(plh => plh.DateCreation == p.ProductLocationHistories.Max(plhs => plhs.DateCreation));
return expression;
}
Шаг в правильном направлении, но я еще не полностью удовлетворен. Я хочу иметь возможность использовать p.ProductLocationHistories().Current(), поэтому мои поиски продолжаются.
Спасибо уже Кирилл! Это первый раз, когда я вижу код C#, переведенный в SQL! Хороший шаг в правильном направлении!
OrderByDescending(pld => pld.DateCreation).FirstOrDefault()
, чем запрашивать max, а затем использоватьSingleOrDefault
. - person jessehouwing   schedule 19.11.2012