Выполнить функцию в IQueryable

Используя 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, однако, если бы был метод расширения под названием «Выполнить», который мог бы выполняться в памяти после выполнения всего linq до и после него, будет ли это работать?

База данных вытянет только > 50 И только ТОП 10, только после этого будет

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

Для уточнения: требуется, чтобы он сохранялся как IQueryable даже после Sleect()


person hatcyl    schedule 17.07.2015    source источник


Ответы (4)


Нет, потому что вам нужно отправить все данные обратно в базу данных, чтобы сделать последнюю часть.

Обычно у вас есть дополнительный 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, что делает создать IQueryable<T> из IEnumerable<T>, но это не то, что вы хотите. Если источник на самом деле не является запрашиваемым, он «подделает» его, но не подключит обратно к базе данных, что вам и нужно.

person Jon Skeet    schedule 17.07.2015
comment
Идея состоит в том, чтобы сохранить его как IQueryable даже после выбора. Поэтому заставьте его работать в указанном порядке. Итак, добавьте больше фильтров ПОСЛЕ .Select() - person hatcyl; 17.07.2015
comment
@hatcyl: И я хочу сказать, что нет, вы не можете этого сделать ... но обычно есть способы избежать проблем, если то, что вы пытаетесь сделать, вообще возможно. Если вам нужно, чтобы база данных использовала информацию, доступную только локально, нет простого и эффективного способа сделать это. - person Jon Skeet; 17.07.2015
comment
Я этого боялся :( - person hatcyl; 17.07.2015

Да, конечно, можно просто вызвать функцию AsEnumerable(), как показано ниже:

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

В Linq to объекты, вызывающие такие функции, как AsEnumerable(), FirstOrDefault(), ToList(), будут выполнять запрос. То, что мы делаем после этих функций, означает, что мы имеем дело с объектами, а не с базой данных.

person Adil Mammadov    schedule 17.07.2015

Да, это возможно, но вам придется немного изменить ситуацию:

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<T>, что позволяет вызывать метод стандартного оператора запросов.

Наряду с несколькими другими операторами, такими как ToList(), First() и т. д., это фактически приводит к тому, что первая часть запроса выполняется до того, как она будет применена сама. В данном контексте это означает, что первая часть вашего запроса транслируется в SQL и выполняется. Результат передается следующему оператору — вашему оператору Select() — который вы затем можете использовать по назначению.

person Kjartan    schedule 17.07.2015

Вы можете построить «ГДЕ»:

     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);
person J4ime    schedule 24.09.2015