【问题标题】:Execute Function in IQueryable在 IQueryable 中执行函数
【发布时间】:2015-10-06 18:22:22
【问题描述】:

使用 Linq,很容易执行一个函数来投影一个 IEnumerable。

var orders = new List<Order>();
orders.Where(x => x.Id > 50).Select(x => new SomethingElse(x.Name));

使用 EntityFramework 和 IQueryable,这是不可能的。相反,您会在运行时获得不受支持的异常,因为您不能在 Select 中拥有函数。

不起作用:

var db = new Order();
db.MyEntities.Where(x => x.Id > 50).Select(x => new SomethingElse(x.Name)).Take(10);

是否存在,或者是否有可能为上述工作创建一种方式?

我知道它不起作用,因为它无法将函数转换为 SQL 可以理解的东西,但是,如果有一个名为“Execute”的扩展方法可以在执行之前和之后的所有 linq 之后在内存中执行,会那行吗?

数据库只会拉 > 50 并且只有 TOP 10,只有在它之后才会是

var db = new Order();
db.MyEntities.Where(x => x.Id > 50).Execute(x => new SomethingElse(x.Name)).Take(10);

澄清一下:要求是即使在 Sleect() 之后也将其保持为 IQueryable

【问题讨论】:

    标签: c# entity-framework iqueryable func


    【解决方案1】:

    您可以构建“WHERE”:

         Expression<Func<Products, bool>> expresionFinal = p => p.Active;
    
        if (mydate.HasValue)
        {
            Expression<Func<Products, bool>> expresionDate = p => (EntityFunctions.TruncateTime(c.CreatedDate) <= mydate);
            expresionFinal = PredicateBuilder.And(expresionFinal, expresionDate );
        }
    
    if (!String.IsNullOrEmpty(codeProduct))
                    {
                        Expression<Func<Products, bool>> expresionCode = c => (codeProduct== c.codProduct);
                        expresionFinal = PredicateBuilder.And(expresionFinal, expresionCode );
                    }
    
        IQueryable<T> query = dbSet;
    
    
            query = query.Where(expresionFinal);
    

    【讨论】:

      【解决方案2】:

      不,因为您需要将所有数据发送回数据库以完成最后一部分。

      通常您有一个额外的Select 来获取查询中所需的信息(如果您需要多个属性,则使用匿名类型),然后在本地完成所有其余操作:

      var query = db.MyEntities
                    .Where(x => x.Id > 50)
                    .Select(x => x.Name) // All we need - keep the query cheap
                    .Take(10)
                    .AsEnumerable()
                    .Select(x => new SomethingElse(x));
      

      请注意,您可能会遇到Queryable.AsQueryable,它确实IEnumerable&lt;T&gt; 生成IQueryable&lt;T&gt; - 但它并没有达到您想要的效果。如果源不是真正可查询的,它将“伪造”它但不会将其连接回数据库,这就是您所追求的。

      【讨论】:

      • 这个想法是即使在 Select 之后也将其保持为 IQueryable。所以让它按照定义的顺序工作。所以,在 .Select() 之后有更多的过滤器
      • @hatcyl:我的意思是,不,你不能那样做......但通常有办法避免它成为问题,如果你试图做的事情是可行的全部。如果您需要数据库使用仅在本地可用的信息,则没有干净有效的方法可以做到这一点。
      • 我很害怕 :(
      【解决方案3】:

      是的,这是可能的,但你必须稍微扭转一下局面:

      var db = new Order();
      db.MyEntities.Where(x => x.Id > 50)
                   .Take(10)
                   .AsEnumerable() // This will fetch data from the DB
                   .Select(x => new SomethingElse(x.Name)); // Here, data is already fetched
      

      这里的区别在于执行的延迟:Where()Select()Take(),以及其他几个操作被延迟 - 即在整个表达式被计算之前不调用,从而允许整个表达式有效映射到适当的 SQL 语句。

      AsEnumerable() 另一方面是一个非延迟操作符。它允许将输入序列转换为普通的IEnumerable&lt;T&gt; 序列,从而允许调用标准查询运算符方法。

      与其他一些运算符(如ToList()First() 等)一起,这实际上会导致查询的第一部分在应用之前执行。在这种情况下,这意味着查询的第一部分被转换为 SQL 并运行。结果将传递给下一个运算符 - 您的 Select() 语句 - 然后您可以按预期使用它。

      【讨论】:

        【解决方案4】:

        是的,当然可以像下面这样调用 AsEnumerable() 函数:

        var db = new Order();
        db.MyEntities.Where(x => x.Id > 50)
            .Take(10)
            .AsEnumerable()
            .Execute(x => new SomethingElse(x.Name));
        

        在 Linq 中调用 AsEnumerable()FirstOrDefault()ToList() 等函数的实体将执行查询。所以我们在函数之后所做的意味着我们处理的是对象而不是数据库。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-22
          • 2020-09-12
          • 1970-01-01
          • 1970-01-01
          • 2016-12-22
          • 2018-09-30
          • 1970-01-01
          相关资源
          最近更新 更多