【问题标题】:Can multiple compiled linq queries be chained together?可以将多个已编译的 linq 查询链接在一起吗?
【发布时间】:2009-10-09 14:07:41
【问题描述】:

我正在尝试将多个已编译的 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.IQueryable1[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, ReadOnlyCollection1 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 上下文,对象 [] 参数)在 System.Data.Linq.CompiledQuery.Invoke(TArg0 arg0, TArg1 arg1) 在 TestMethod() 中 ....

【问题讨论】:

  • 可以随意恢复它,但我可以忍受不得不滚动一行。

标签: linq linq-to-sql


【解决方案1】:

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

来自MSDN CompiledQuery

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

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

也许这段代码会修复它,但如果您使用 LINQ to SQL,这可能会影响到数据库的往返。

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

【讨论】:

  • AsEnumerable() 就足够了,无需调用 ToList() ...除了使用 AsEnumerable() 而不是使用 ToList() 可以节省大量不必要的内存复制
  • 哇,我什至没有想到可能存在 L2S 允许构建、编译和执行它甚至不支持的查询的错误。这让我感到震惊,因为链接两个编译查询确实返回了正确的结果。我想接受这个作为答案,但我想知道它最初是如何工作的?
  • 如果您使用 AsEnumerable() 或 .ToList() 它不会动态构建 SELECT 语句。它将在第一个 .ToList() 上提取数据,然后在内存中而不是在服务器上执行查询请求。
【解决方案2】:

您需要使用 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 而不是 Func 委托的普通实例方法。

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
    }
}

【讨论】:

  • 由于性能要求,使用编译查询对我的应用程序至关重要...您在此处提供的方法是使用像乐高积木这样的重复使用查询的非常好的方法,但不幸的是,您不能在一个函数,只在一个表达式上。
  • 您是否将我上面的 Func 的性能与您的 CompiledQuery 版本进行了比较?您可以使用秒表来检查执行时间。 msdn.microsoft.com/en-us/library/…
【解决方案3】:

诚然,我不熟悉 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
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-02
    • 1970-01-01
    • 1970-01-01
    • 2020-04-09
    • 2019-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多