【问题标题】:In LINQ to SQL, how do you pass parts of a LINQ query into a function在 LINQ to SQL 中,如何将 LINQ 查询的一部分传递给函数
【发布时间】:2009-01-12 04:53:50
【问题描述】:

是否可以将 linq 查询的一部分传递给函数? 我想为我的 DAL 创建一个始终使用相同查询接口的通用接口。例如,

List<T> Get(Join j, Where w, Select s){    
    return currentDataContext<T>.Join(j).Where(w).Select(s).ToList();    
}

这种事情可能吗?我认为这将使用表达式树来完成,但我无法找到它的示例。

【问题讨论】:

    标签: c# linq linq-to-sql expression-trees


    【解决方案1】:

    嗯,“加入”很棘手,因为很难表达一个加入 - 但是像 where / select / orderby 这样的事情很容易......

    真的,这只是在IQueryable&lt;T&gt; 上组合各种LINQ 方法的情况,它们通常接受Expression&lt;Func&lt;...&gt;&gt; 进行某种组合。因此,带有可选谓词的基本选择将是:

        public IQueryable<T> Get<T>(
            Expression<Func<T,bool>> predicate
            ) where T : class
        {
            IQueryable<T> query = (IQueryable<T>)GetTable(typeof(T));
            if (predicate != null) query = query.Where(predicate);
            return query;
        }
    

    我也倾向于返回IQueryable&lt;T&gt;,因为它是完全可组合的。如果调用者想要一个列表,他们总是可以在上面使用ToList()...或者(例如):

        using(var ctx = new MyDataContext(CONN))
        {
            ctx.Log = Console.Out;
            int frCount = ctx.Get<Customer>(c => c.Country == "France").Count();
        }
    

    哪个(使用 Northwind)进行查询:

    SELECT COUNT(*) AS [value]
    FROM [dbo].[Customers] AS [t0]
    WHERE [t0].[Country] = @p0
    

    在查询中包含“选择”(投影)的问题是您最终会得到多个泛型类型。由于您经常希望投影是匿名类型,因此几乎不可能指定投影类型(匿名)表类型,并且它是不可调用的。

    实际上,我想知道编写这样的方法是否有很多好处。我可能会坚持使用基本方法:

        public IQueryable<T> Get<T>() where T : class
        {
            return (IQueryable<T>)GetTable(typeof(T));
        }
    

    并让调用者以他们喜欢的方式编写它——也许使用查询语法:

           var list = (from cust in ctx.Get<Customer>()
                       where cust.Country == "France"
                       select cust.CompanyName).Take(10).ToList();
    

    用途:

    SELECT TOP (10) [t0].[CompanyName]
    FROM [dbo].[Customers] AS [t0]
    WHERE [t0].[Country] = @p0
    

    或者,如果您确实想包含 order by 和 projection,那么扩展方法是最实用的方法;那么你不需要指定原始(源) T (这就是它在与匿名类型混合时无法调用的原因):

    public static class QueryExtension
    {
        public static IQueryable<TProjection>
            Get<TSource, TProjection, TOrderKey>(
                this IQueryable<TSource> source,
                Expression<Func<TSource, bool>> where, // optional
                Expression<Func<TSource, TProjection>> select,
                Expression<Func<TProjection, TOrderKey>> orderBy)
        {
            if (where != null) source = source.Where(where);
            return source.Select(select).OrderBy(orderBy);
        }
    }
    

    然后考虑一个 DAL 方法,例如:

        public List<string> Countries()
        {
            return Customers.Get(
                x=>x.CompanyName != "",
                x=>x.Country,
                x=>x).Distinct().ToList();
        }
    

    使用(同样,与 Northwind):

    SELECT DISTINCT [t0].[Country]
    FROM [dbo].[Customers] AS [t0]
    WHERE [t0].[CompanyName] <> @p0
    

    【讨论】:

      【解决方案2】:

      检查这个泛型类:TableView.cs

      它基本上使用 Func 委托来应用 Where 谓词:

      //...
      public TableView(DataContext dataContext, Expression<Func<TEntity, bool>> predicate)
      {
          this.table = dataContext.GetTable<TEntity>();
          this.baseQuery = table.Where(predicate);
          this.predicate = predicate.Compile();
      }
      //...
      

      【讨论】:

      • 其实我有点担心。它编译谓词的事实意味着它将始终加载整个表,然后对其执行 LINQ-to-Objects。那是非常效率低下。
      【解决方案3】:

      您可以使用 Linq 示例提供的动态表达式库,使用此扩展库,您可以为 where 等传递 linq 子句...

      可从此处下载 linq samples

      【讨论】:

        【解决方案4】:

        您可以使用Dynamic LINQ 并将参数作为字符串传递。

        【讨论】:

          【解决方案5】:

          Marc Gravell♦ 像往常一样提供了一个非常有见地的答案,但我真的认为拥有采用 IQueryables 并添加限制的方法在大多数情况下会起作用,并且它们使代码更清晰且易于维护。例如:

          //Join
          public static IQueryable<IContract> AllContracts(this IQueryable<IAccount> accounts, ISession s ) {
                    return from a in accounts
                           from contract in s.Query<IContract()
                           where (a.Id == contract.AccountId)
                           select contract;
              }
          //Where
          public static IQueryable<IContract> Active(this IQueryable<IContract> contracts) {
                    return from contract in contracts
                           where (contract.Active == true)
                           select contract;
              }
          

          然后你可以像这样混合和匹配这些:

          IQueryable<IContract> activeContracts = s.Query<IAccount>()
                      .Where(o => o.Name == "XXX")
                      .GetContracts(s)
                      .Active();
          

          我在这里使用扩展方法和 NHiberante 的 LINQ 提供 Query 方法,但是这可以很容易地在没有静态方法和任何 LINQ 提供程序的情况下重写。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-08-23
            • 2012-11-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-17
            • 1970-01-01
            相关资源
            最近更新 更多