重用linq查询的部分内容

时间:2011-04-27 15:31:51

标签: c# linq linq-to-sql

我们有一个使用Linq-To-Sql的程序,并在我们的表上进行了很多类似的查询。特别是,我们有一个timestamp列,我们提取最新的时间戳,以查看自上次查询以来表是否已更改。

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();
    }

此解决方案的唯一缺点是我必须为每个表编写此函数。我会喜欢Tejs的答案,如果它真的有效,但我不会为它重新设计我的数据库。加上DateTime不是一个做时间戳的好方法。


更新#2(不是那么快)

虽然我可以执行诸如Documents.Where(...)。GetMostRecentTimestamp()之类的查询,但如果我尝试执行基于关联的查询(例如MyCustomer.Orders.GetMostRecentTimestamp()或MyCustomer.Orders),此解决方案将失败。.AsQueryable()GetMostRecentTimestamp();

4 个答案:

答案 0 :(得分:6)

这实际上很容易做到。您只需要在要为其提供此实体的实体上定义接口:

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 :(得分:1)

这样的扩展如何

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

答案 2 :(得分:1)

嗯...

DateTime timeStamp1 = dataContext.Customers.Max(c => c.TimeStamp);
DateTime timeStamp2 = dataContext.Orders.Max(c => c.TimeStamp);
DateTime timeStamp3 = dataContext.Details.Max(c => c.TimeStamp);

答案 3 :(得分:0)

我创建了一对可以帮助你的扩展方法: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现在是具有最新时间戳的对象,因此您不需要第二个查询来获取它。