可以将多个已编译的linq查询链接在一起吗?

时间:2009-10-09 14:07:42

标签: linq linq-to-sql

我正在尝试将多个已编译的linq查询链接在一起。我已经成功地将两个查询链接在一起,但我不能让三个链条正常工作。所以这是减少我的代码来重新创建问题。我的两个问题是:'为什么这不起作用?'并且'是否有更好的方法来保持编译查询的性能优势,并避免重复使用常用的基本查询逻辑?'

定义以下两个查询:

Func<DataContext, IQueryable<User>> selectUsers = 
    CompiledQuery.Compile(
        (DataContext dc)=>dc.Users.Select(x=>x)
    );
//        
Func<DataContext, string, IQueryable<User>> filterUserName = 
    CompiledQuery.Compile(
        (DataContext dc, string name) =>
            selectUsers(dc).Where(user=>user.Name == name)
    );

调用和枚举链可以正常工作:

filterUserName(new DataContext(), "Otter").ToList();

向链中添加第三个查询:

Func<DataContext, string, int, IQueryable<User>> filterUserAndGroup =     
    CompiledQuery.Compile(
        (DataContext dc, string name, int groupId) => 
            filterUserName(dc, name).Where(user=>user.GroupId == groupId)
    );

调用链不起作用:

filterUserAndGroup(new DataContext(), "Otter", 101);
  

System.InvalidOperationException:   会员访问“用户”的“字符串名称”   在类型上不合法   “System.Linq.IQueryable 1[User].. at System.Data.Linq.SqlClient.SqlMember.set_Expression(SqlExpression value) at System.Data.Linq.SqlClient.SqlFactory.Member(SqlExpression expr, MemberInfo member) at System.Data.Linq.SqlClient.SqlBinder.Visitor.AccessMember(SqlMember m, SqlExpression expo) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitMember(SqlMember m) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression expr) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitBinaryOperator(SqlBinary bo) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression expr) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitAlias(SqlAlias a) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlVisitor.VisitSource(SqlSource source) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitIncludeScope(SqlIncludeScope scope) at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node) at System.Data.Linq.SqlClient.SqlBinder.Bind(SqlNode node) at System.Data.Linq.SqlClient.SqlProvider.BuildQuery(ResultShape resultShape, Type resultType, SqlNode node, ReadOnlyCollection 1   parentParameters,SqlNodeAnnotations   注释)   System.Data.Linq.SqlClient.SqlProvider.BuildQuery(表达式   查询,SqlNodeAnnotations注释)   在   System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Compile(表达式   查询)   System.Data.Linq.CompiledQuery.ExecuteQuery(的DataContext   context,Object [] args)at   System.Data.Linq.CompiledQuery.Invoke(TArg0   arg0,TArg1 arg1)在TestMethod()中   ....

3 个答案:

答案 0 :(得分:3)

在执行第二个编译查询之前,您需要将第一个编译的查询转换为列表。理论上,这也应该导致你的两个查询链出错。

来自MSDN CompiledQuery

  

如果将新查询运算符应用于委托执行的结果,则会生成异常。

     

如果要对执行编译查询的结果执行查询运算符,则必须先将结果转换为列表,然后再对其进行操作。

也许这段代码会修复它,虽然如果你使用的是LINQ to SQL,这可能会对往返数据库产生影响。

filterUserName(dc, name).ToList().Where(user=>user.GroupId == groupId)

答案 1 :(得分:0)

不可否认,我不熟悉CompiledQuery。但是,由于LINQ的延迟执行性质,您可以执行以下操作:

var result = dbContext.Users.Where(user => user.id == id);
result = result.Where(user => user.GroupID == groupID);
result = result.Select(user => user.username);

for(String username in result){ 
   ; // do something
}

以上当然是一个简单的例子。但是,当根据用户输入(例如网站上的“高级搜索”表单)将不同的查询组合在一起时,它可能非常有用。

答案 2 :(得分:0)

您需要使用CompiledQuery类吗?试试这个......

static Func<DataContext, IQueryable<User>> selectUsers =
  (dc) => dc.Users.Select(x => x);
//        
static Func<DataContext, string, IQueryable<User>> filterUserName =
    (DataContext dc, string name) =>
        selectUsers(dc).Where(user => user.Name == name);
//
static Func<DataContext, string, int, IQueryable<User>> filterUserAndGroup =
    (DataContext dc, string name, int groupId) =>
        filterUserName(dc, name).Where(u => u.GroupID == groupId);

...测试代码(我知道我的DataContext不是LINQ2SQL,但这是LINQ的乐趣和美丽)......

此外,我对我自己的数据库使用此方法,因此我知道它们构建为单个查询以发送到数据库。我甚至使用了返回IQueryable&lt;&gt;的普通实例方法而不是Func&lt;&gt;代表。

public class DataContext
{
    public static Func<DataContext, IQueryable<User>> selectUsers =
      (dc) => dc.Users.Select(x => x);
    //        
    public static Func<DataContext, string, IQueryable<User>> filterUserName =
        (DataContext dc, string name) =>
            selectUsers(dc).Where(user => user.Name == name);
    //
    public static Func<DataContext, string, int, IQueryable<User>> UsrAndGrp =
        (DataContext dc, string name, int groupId) =>
            filterUserName(dc, name).Where(u => u.GroupID == groupId);

    public DataContext()
    {
        Users = new List<User>()
        {
            new User(){ Name = "Matt", GroupID = 1},
            new User(){ Name = "Matt", GroupID = 2},
            new User(){ Name = "Jim", GroupID = 2},
            new User(){ Name = "Greg", GroupID = 2}
        }.AsQueryable();
    }
    public IQueryable<User> Users { get; set; }
    public class User
    {
        public string Name { get; set; }
        public int GroupID { get; set; }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var q1 = DataContext.UsrAndGrp(new DataContext(), "Matt", 1);
        Console.WriteLine(q1.Count()); // 1
        var q2 = DataContext.filterUserName(new DataContext(), "Matt");
        Console.WriteLine(q2.Count()); // 2
    }
}