Вызов метода внутри запроса 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'.
Ответы
Ответ 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();
}
}