Вызов метода внутри запроса Linq - PullRequest
32 голосов
/ 02 апреля 2012

Я хочу вставить в мою таблицу столбец с именем 'S', который получит некоторое строковое значение на основе значения, полученного из столбца таблицы.

Например: for each ID (a.z) Я хочу получить строковое значение, хранящееся в другой таблице. Строковое значение возвращается из другого метода, который получает его через запрос Linq.

  • Можно ли вызвать метод из Linq?
  • Должен ли я делать все по одному и тому же запросу?

Это структура информации, которую мне нужно получить:

az - это идентификатор в первом квадрате таблицы # 1, из этого идентификатора я получаю другой идентификатор в таблице # 2, и из этого я могу получить свое строковое значение, которое мне нужно отобразить в столбце 'S'. * enter image description here

var q = (from a in v.A join b in v.B
    on a.i equals b.j
    where a.k == "aaa" && a.h == 0
    select new {T = a.i, S = someMethod(a.z).ToString()})
    return q;

Строка S = someMethod(a.z).ToString(), вызывающая следующую ошибку:

Невозможно привести объект типа 'System.Data.Linq.SqlClient.SqlColumn' набрать 'System.Data.Linq.SqlClient.SqlMethodCall'.

Ответы [ 3 ]

55 голосов
/ 02 апреля 2012

Вы должны выполнить свой вызов метода в контексте Linq-to-Objects, потому что на стороне базы данных этот вызов метода не будет иметь смысла - вы можете сделать это, используя AsEnumerable() - в основном остальная часть запроса будет затем оценена как в коллекции памяти, используя Linq-to-Objects, и вы можете использовать вызовы методов, как и ожидалось:

var q = (from a in v.A join b in v.B
        on a.i equals b.j
        where a.k == "aaa" && a.h == 0
        select new {T = a.i, Z = a.z })
        .AsEnumerable()
        .Select(x => new { T = x.T, S = someMethod(x.Z).ToString() })
10 голосов
/ 02 апреля 2012

Вы хотите разделить его на два утверждения. Возвратите результаты запроса (который попадет в базу данных), а затем перечислите результаты второй раз на отдельном шаге, чтобы преобразовать перевод в новый список объектов. Этот второй «запрос» не попадет в базу данных, поэтому вы сможете использовать someMethod() внутри него.

Linq-to-Entities - немного странная вещь, потому что она делает переход к запросам базы данных из C # чрезвычайно плавным: но вы всегда должны напоминать себе: «Этот C # будет переведен в некоторый SQL. " И в результате вы должны спросить себя: «Может ли весь этот C # действительно выполняться как SQL?» Если он не может - если вы звоните someMethod() внутри него - ваш запрос будет иметь проблемы. И обычное решение состоит в том, чтобы разделить его.

(Другой ответ от @BrokenGlass, использующий .AsEnumerable(), по сути, является еще одним способом сделать это.)

0 голосов
/ 04 июля 2017

Это старый вопрос, но я не вижу ни одного упоминания о "хаке", который позволяет вызывать методы во время выбора без повторения. Идея состоит в том, чтобы использовать конструктор, и в конструкторе вы можете вызывать все, что пожелаете (по крайней мере, он прекрасно работает в LINQ с NHibernate, не уверен насчет LINQ2SQL или EF, но я думаю, что это должно быть так же). Ниже у меня есть исходный код для тестовой программы, похоже, что повторяющийся подход в моем случае примерно в два раза медленнее подхода конструктора, и я думаю, что это неудивительно - моя бизнес-логика была минимальной, поэтому такие вещи, как итерация и распределение памяти, имеют значение.

Также хотелось бы, чтобы был лучший способ сказать, что то или иное не следует пытаться выполнить в базе данных,

// Here are the results of selecting sum of 1 million ints on my machine:
// Name    Iterations      Percent    
// reiterate       294     53.3575317604356%      
// constructor     551     100%

public class A
{
    public A()
    {            
    }

    public A(int b, int c)
    {
        Result = Sum(b, c);
    }

    public int Result { get; set; }

    public static int Sum(int source1, int source2)
    {
        return source1 + source2;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var range = Enumerable.Range(1, 1000000).ToList();

        BenchmarkIt.Benchmark.This("reiterate", () =>
            {
                var tst = range
                    .Select(x => new { b = x, c = x })
                    .AsEnumerable()
                    .Select(x => new A
                    {
                        Result = A.Sum(x.b, x.c)
                    })
                    .ToList();
            })
            .Against.This("constructor", () =>
            {
                var tst = range
                    .Select(x => new A(x, x))
                    .ToList();
            })
            .For(60)
            .Seconds()
            .PrintComparison();

        Console.ReadKey();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...