Повторное использование частей запроса linq - PullRequest
1 голос
/ 27 апреля 2011

У нас есть программа, которая использует Linq-To-Sql и выполняет много подобных запросов в наших таблицах.В частности, у нас есть столбец отметки времени, и мы извлекаем самую последнюю отметку времени, чтобы увидеть, изменилась ли таблица со времени нашего последнего запроса.

System.Data.Linq.Binary ts;
ts = context.Documents
    .Select(r => r.m_Timestamp)
    .OrderByDescending(r => r)
    .FirstOrDefault();

Мы часто повторяем этот запрос для разных таблиц, которые актуальны для текущей формы / запроса.Что я хотел бы сделать, это создать «функцию» или что-то, что может повторить последние 3 строки этого запроса (и в идеале будет работать на каждой таблице).Вот что я хотел бы написать:

System.Data.Linq.Binary ts;
ts = context.Documents
    .GetMostRecentTimestamp();

Но я понятия не имею, как создать этот «GetMostRecentTimestamp».Кроме того, эти запросы никогда не бывают такими простыми.Они обычно фильтруются по клиенту или по текущему заказу, поэтому более правильный запрос может быть

ts = context.Documents
    .Where(r => r.CustomerID == ThisCustomerID)
    .GetMostRecentTiemstamp(); 

Любая помощь?Спасибо!


Обновление [Решение]

Я выбрал ответ Bala R, вот код обновлен, поэтому он компилируется:

public static System.Data.Linq.Binary GetMostRecentTimestamp(this IQueryable<Data.Document> docs)
{
    return docs
        .Select(r => r.m_Timestamp)
        .OrderByDescending(r => r)
        .FirstOrDefault();
    }

Единственный недостаток этого решенияв том, что мне придется написать эту функцию для каждой таблицы.Мне бы понравился ответ Тейса, если бы он действительно работал, но я не перепроектирую свою базу данных для него.Плюс DateTime не очень хороший способ делать отметки времени.


Обновление № 2 (не так быстро)

Хотя я могу сделать запрос, такой как Documents.Where (...) .GetMostRecentTimestamp (), это решение не работает, если я пытаюсь выполнить запрос на основе ассоциации, такой как MyCustomer.Orders.GetMostRecentTimestamp () или MyCustomer.Orders.AsQueryable (). GetMostRecentTimestamp ();

Ответы [ 4 ]

6 голосов
/ 27 апреля 2011

Это на самом деле довольно легко сделать.Вам просто нужно определить интерфейс для сущностей, для которых вы хотите предоставить это:

public class MyEntity : ITimestamp

Затем ваш метод расширения:

public static DateTime GetMostRecentTimestamp<T>(this IQueryable<T> queryable)
    where T : ITimestamp
{
     return queryable.Select(x => x.m_Timestamp)
            .OrderByDescending(r => r)
            .FirstOrDefault()
}

Это будет полезно для любой сущности, которая соответствуетинтерфейс:

context.Documents.GetMostRecentTimestamp()
context.SomeOtherEntity.GetMostRecentTimestamp()
1 голос
/ 28 апреля 2011

Хмм ...

DateTime timeStamp1 = dataContext.Customers.Max(c => c.TimeStamp);
DateTime timeStamp2 = dataContext.Orders.Max(c => c.TimeStamp);
DateTime timeStamp3 = dataContext.Details.Max(c => c.TimeStamp);
1 голос
/ 27 апреля 2011

Как насчет такого расширения

public static DateTime GetMostRecentTimestamp (this IQueryable<Document> docs)
{
    return docs.Select(r => r.m_Timestamp)
               .OrderByDescending(r => r)
               .FirstOrDefault();
}
0 голосов
/ 27 апреля 2011

Я создал пару методов расширения, которые могут вам помочь: ObjectWithMin и ObjectWithMax:

public static T ObjectWithMax<T, TResult>(this IEnumerable<T> elements, Func<T, TResult> projection)
        where TResult : IComparable<TResult>
    {
        if (elements == null) throw new ArgumentNullException("elements", "Sequence is null.");
        if (!elements.Any()) throw new ArgumentException("Sequence contains no elements.");

        var seed = elements.Select(t => new {Object = t, Projection = projection(t)}).First();

        return elements.Aggregate(seed,
                                  (s, x) =>
                                  projection(x).CompareTo(s.Projection) >= 0
                                      ? new {Object = x, Projection = projection(x)}
                                      : s
            ).Object;
    }

public static T ObjectWithMin<T, TResult>(this IEnumerable<T> elements, Func<T, TResult> projection)
        where TResult : IComparable<TResult>
    {
        if (elements == null) throw new ArgumentNullException("elements", "Sequence is null.");
        if (!elements.Any()) throw new ArgumentException("Sequence contains no elements.");

        var seed = elements.Select(t => new {Object = t, Projection = projection(t)}).First();

        return elements.Aggregate(seed,
                                  (s, x) =>
                                  projection(x).CompareTo(s.Projection) < 0
                                      ? new {Object = x, Projection = projection(x)}
                                      : s
            ).Object;
    }

Эти два более эффективны, чем OrderBy (). FirstOrDefault () при использовании в коллекциях в памяти; однако они не разрешимы поставщиками IQueryable. Вы бы использовали их примерно так:

ts = context.Documents
    .Where(r => r.CustomerID == ThisCustomerID)
    .ObjectWithMax(r=>r.m_Timestamp);

ts теперь объект с самой последней отметкой времени, поэтому вам не требуется второй запрос для его получения.

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