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

C# LinqToSql选择一个类,然后执行更多查询,c#,.net,linq,linq-to-sql,C#,.net,Linq,Linq To Sql,我有一个使用多个联接运行的LINQ查询,我希望将其作为IQueryable传递,并在其他方法中应用额外的过滤器 问题是,我无法解决如何传递var数据类型并使其保持强类型,如果我尝试将其放在自己的类中(例如:。Select((a,b)=>newmyclass(a,b))),我在稍后尝试添加Where子句时会出错,因为我的类没有转换成SQL。是否有任何方法可以执行以下操作之一: 让我的类映射到SQL 让var数据类型实现一个接口(这样我就可以像这样传递它) 我没想到会解决我的问题 例如: publi

我有一个使用多个联接运行的LINQ查询,我希望将其作为
IQueryable
传递,并在其他方法中应用额外的过滤器

问题是,我无法解决如何传递
var
数据类型并使其保持强类型,如果我尝试将其放在自己的类中(例如:
。Select((a,b)=>newmyclass(a,b))
),我在稍后尝试添加
Where
子句时会出错,因为我的类没有转换成SQL。是否有任何方法可以执行以下操作之一:

  • 让我的类映射到SQL
  • var
    数据类型实现一个接口(这样我就可以像这样传递它)
  • 我没想到会解决我的问题
  • 例如:

    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:我知道在我的示例中,我可以很容易地将函数展平,但在实际操作中,如果我尝试的话,它将是一团混乱。

    您正在从您的联接返回一个被称为“匿名类型”的东西-它不是一个特定的数据库类型,而是一个包含a和B的对象。因为该类型是匿名的,我认为您将无法编写返回该类型的函数或该类型的集合

    如果它是SQL/LINQtoSQL知道的类型,那么您可能能够做到这一点


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

    您所要做的就是将您作为查询结果构建的类型反匿名化-
    新建{a=a,B=B}
    只需使用这些属性创建一个类,并对其进行适当的命名。然后您的查询将有一个类型为
    IQueryable

    像这样:

    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);
    }
    
    公共类MyClass
    { 
    公共int A{get;set;}
    公共int B{get;set;}
    }
    公共图书馆
    {
    使用(DBDataContext=new DBDataContext())
    {
    var result=context.TableAs.Join(
    上下文表,
    a=>a.BID,
    b=>b.ID,
    (a,b)=>新的MyClass{a=a,b=b}
    );
    结果=addNeedValue(结果,4);
    }
    }
    私有IQueryable addNeedValue(IQueryable结果,int值)
    {
    返回结果,其中(r=>r.A.Value==Value);
    }
    
    我绝对不推荐,但如果您使用的是.NET 4.0,您可能可以在这里使用dynamic;第一种方法是在执行
    联接之前应用过滤器,因为联接的查询不必只是基本表。第二种方法在连接之后应用过滤器,使用中间投影(并将其更改为返回谓词,而不是在内部应用)

    这已在.NET3.5和.NET4上成功测试;请注意,在3.5(SP1)中,
    表达式.Invoke(对于第二个示例)在EF上不起作用,但对于LINQ to SQL很好

    如果您想运行该示例,我已经使用了Northwind(仅仅因为这是我在本地使用的):

    使用系统;
    使用System.Linq;
    使用System.Linq.Expressions;
    使用ConsoleApplication1;//我的数据上下文的命名空间
    静态类程序
    {
    公共静态void Main()
    {
    使用(var context=newtestdatacontext())
    {
    context.Log=Console.Out;//检查它是否有效
    IQueryable lhs=上下文.Orders;
    IQueryable rhs=context.Order\u详细信息;
    //这里有多少谓词等
    rhs=加入前添加(rhs,4);
    var结果=lhs.Join(rhs,
    a=>a.OrderID,
    b=>b.OrderID,
    (a,b)=>新的{a=a,b=b}
    );
    //或在后面加上
    result=result.Where(row=>row.B,addAfterJoin(100));
    Console.WriteLine(result.Count());
    }
    }
    私有静态IQueryable addBeforeJoin(IQueryable查询,int值)
    {
    返回查询,其中(r=>r.数量>=值);
    }
    私有静态表达式addAfterJoin(int值)
    {
    
    返回r=>r.Quantity,顺便说一下,为了解决一些困惑:“var”不是一个数据类型。它是一个关键字,告诉编译器根据您分配给它的内容确定应该存在的类型。类型总是具体的和定义的,只是您不必显式地在那里编写它。嗯,我知道var是如何工作的,从技术上讲,它不是一个数据类型,而是编译器工作的指示器k了解数据类型本身是什么,只是不太清楚如何使用它…我已经测试了Fyodor Soikin的解决方案,它可以工作。我怀疑您在成员Test.MyClass.A不支持转换为SQL时遇到的错误是因为您没有使用其解决方案中显示的成员初始化语法;instead您可能正在将值传递给构造函数。L2S无法推断传递给构造函数的值随后将作为类似命名的属性公开。但是,它可以从查询的表达式树中知道如何使用成员初始化语法初始化属性,并根据需要映射它们。您说得对,Michael,没错我也弄错了什么。谢谢!嘿,Fyodor,这是我第一次这样做,但是一旦我这样做,我就会得到
    成员Test.MyClass.A'不支持转换为SQL。
    只要我尝试执行查询(例如:调用
    result.ToList()
    ).我已经尝试过用反射创建的动态类型,我看不出它在我的类型不起作用的情况下会起作用的任何原因,但是是的…运行时不知道你的类型与SQL的关系-仅仅因为它具有相同的属性并不意味着它能够以相同的方式使用它们。可能有一些接口或属性你可以使用,但我使用
    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));
        }
    
    }