LinqToSql选择一个类然后执行更多查询

时间:2010-04-23 04:03:24

标签: c# .net linq linq-to-sql

我有一个运行多个连接的LINQ查询,我希望将其作为IQueryable<T>传递,并在其他方法中应用其他过滤器。

问题是我无法弄清楚如何传递var数据类型并保持强类型,如果我尝试将它放在我自己的类中(EG:.Select((a,b) => new MyClass(a,b))当我尝试添加后来的Where子句时,我收到错误,因为我的类没有翻译成SQL。有什么方法可以做到以下其中一种:

  1. 让我的类映射到SQL?
  2. var数据类型实现一个接口(所以我可以传递它,就像它那样)?
  3. 我还没有解决我的问题?
  4. 示例:

    public void Main()
    {
        using (DBDataContext context = new DBDataContext())
        {
          var result = context.TableAs.Join(
             context.TableBs,
             a => a.BID,
             b => b.ID,
            (a,b) => new {A = a, B = b}
          );
          result = addNeedValue(result, 4);
       }
    }
    
    private ???? addNeedValue(???? result, int value)
    {
        return result.Where(r => r.A.Value == value);
    }
    
    PS:我知道在我的例子中我可以轻松地完成这个功能,但实际上如果我尝试的话,这将是一个绝对的混乱。

4 个答案:

答案 0 :(得分:2)

您所要做的就是对您在查询后构建的类型进行匿名处理 - new { A = a, B = b } 只需创建一个包含这些属性的类并对其进行适当的命名然后,您的查询的类型为IQueryable<your type>

像这样:

public class MyClass 
{ 
    public int A { get; set; }
    public int B { get; set; }
}

public void Main()
{
    using (DBDataContext context = new DBDataContext())
    {
      var result = context.TableAs.Join(
         context.TableBs,
         a => a.BID,
         b => b.ID,
        (a,b) => new MyClass {A = a, B = b}
      );
      result = addNeedValue(result, 4);
   }
}

private IQueryable<MyClass> addNeedValue(IQueryable<MyClass> result, int value)
{
    return result.Where(r => r.A.Value == value);
}

答案 1 :(得分:1)

这是两种不同的方法;第一个在执行Join之前应用过滤器,因为连接的查询不必只是基本表。第二种方法在连接之后应用过滤器,使用中间投影(并将其更改为返回谓词,而不是在内部应用它)。

已在.NET 3.5和.NET 4上成功测试过;请注意,在3.5(SP1)中,Expression.Invoke(对于第二个示例)将无法在EF上运行,但对于LINQ-to-SQL则可以。

如果你想运行这个例子,我使用了Northwind(因为那是我在本地的那个):

using System;
using System.Linq;
using System.Linq.Expressions;
using ConsoleApplication1; // my data-context's namespace
static class Program
{
    public static void Main()
    {
        using (var context = new TestDataContext())
        {
            context.Log = Console.Out; // to check it has worked
            IQueryable<Order> lhs = context.Orders;
            IQueryable<Order_Detail> rhs = context.Order_Details;
            // how ever many predicates etc here
            rhs = addBeforeJoin(rhs, 4);

            var result = lhs.Join(rhs,
                   a => a.OrderID,
                   b => b.OrderID,
                  (a, b) => new { A = a, B = b }
            );
            // or add after
            result = result.Where(row => row.B, addAfterJoin(100));
            Console.WriteLine(result.Count());
        }
    }

    private static IQueryable<Order_Detail> addBeforeJoin(IQueryable<Order_Detail> query, int value)
    {
        return query.Where(r => r.Quantity >= value);
    }
    private static Expression<Func<Order_Detail, bool>> addAfterJoin(int value)
    {
        return r => r.Quantity <= value;
    }
    private static IQueryable<TSource> Where<TSource, TProjection>(
        this IQueryable<TSource> source,
        Expression<Func<TSource, TProjection>> selector,
        Expression<Func<TProjection, bool>> predicate)
    {
        return source.Where(
            Expression.Lambda<Func<TSource, bool>>(
            Expression.Invoke(predicate, selector.Body),
            selector.Parameters));
    }

}

答案 2 :(得分:0)

您正在从连接中返回所谓的“匿名类型” - 它不是特定的数据库类型,而是仅包含A和B的对象。因为类型是匿名的,我不认为你是能够编写一个返回该类型的函数 - 或者该类型的集合。

如果它是SQL / LINQ-to-SQL所知道的类型,那么你可能会这样做。

让LINQ-to-SQL识别类型的一种方法可能是创建一个执行连接并选择相关列的存储过程。如果将该存储过程添加到DBML文件中,LINQ-to-SQL将了解结果中每个“行”的类型。然后,您应该能够让您的函数返回IQueryable<ResultSetRow>

答案 3 :(得分:0)

我绝对不推荐它,但你可以在这里使用动态,如果你使用的是.NET 4.0。