Подтвердить что ты не робот

Вызов метода внутри запроса Linq

Я хочу вставить в свою таблицу столбец с именем "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'.

4b9b3361

Ответ 1

Вы должны выполнить вызов метода в 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() })

Ответ 2

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

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

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

Ответ 3

Это старый вопрос, но я вижу, что никто не упоминает один "взлом", который позволяет вызывать методы во время выбора без повторения. Идея заключается в использовании конструктора, а в конструкторе вы можете вызывать все, что хотите (по крайней мере, он отлично работает в 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();
    }
}