Мне часто нужно вызывать скалярные функции, определенные на SQL Server, из моих веб-приложений (ASP.NET Core / EF Core). Поскольку эти функции являются простыми вспомогательными функциями, и я также использую их много, я использую общий шаблон для вызова этих скалярных функций - с помощью новых типов запросов, доступных в EF Core 2.1. Поскольку я относительно новичок в EF Core, мой вопрос в том, может ли этот шаблон вызвать проблемы и / или есть ли лучшее решение или лучший способ вызова скалярных функций. Решение работает, и я пока не могу наблюдать никаких проблем, но, например, мне было интересно, может ли использование одного и того же типа запроса для разных функций привести к неожиданным значениям или странному поведению из-за поведения кэширования / отслеживания и т. д. В EF Core - это больше похоже на внутреннее чувство.
Итак, вот шаблон: Вместо того, чтобы определять разные типы сущностей для каждой отдельной скалярной функции, я просто определяю один общий тип:
public class PrimitiveDto<T>
{
public T Value { get; set; }
}
В моем классе контекста я регистрирую эти типы для каждого типа возвращаемого значения, которое я ожидаю от скалярных функций, которые я хочу использовать, поэтому для всех скалярных функций, возвращающих 'int', класс контекста будет иметь одну дополнительную запись, подобную этой:
public virtual DbQuery<PrimitiveDto<int>> BasicIntDto { get; set; }
Для EF Core> = 3 это:
public virtual DbSet<PrimitiveDto<int>> BasicIntDto { get; set; }
В каждой части приложения, где я хочу вызвать скалярную функцию, возвращающую int, я просто использую один и тот же следующий шаблон:
context.BasicIntDto.FromSql("SELECT <FUNCTION> AS Value")
Используя этот шаблон, я могу одинаково вызывать любое количество функций без определения дополнительных типов или расширения класса контекста.
Пожалуйста, дайте мне знать, могу ли я попасть в ловушку из-за этого шаблона. Большое спасибо.
Вы правы - спасибо за идею.
Почему бы вам не использовать Отображение скалярных функций базы данных. Да, это требует расширения класса контекста, но не требует дополнительных типов и обеспечивает возможность повторного использования, поддержку Intelisense и проверку времени компиляции, как и в случае с обычными методами LINQ и CLR.
Спасибо за подсказку, Иван Стоев - должен признать, что я еще не знал об этой функции. Но оказывается, что вы не можете вызвать скалярную функцию напрямую через эту функцию - вы должны начать с типа объекта, а затем использовать эти заглушки метода в запросе (например, context.ENTITY.where (x => x.METHODSTUB ()) ...). Однако, похоже, они над этим работают (см. github.com/aspnet/EntityFrameworkCore/issues/9810). Итак, еще раз спасибо, что указали на это.
Вы можете использовать метод Enumerable.Single для получения результата.
Марк Дж. Не могли бы вы предоставить дополнительную информацию об этом? Это другой подход или вы просто имеете в виду, что я должен вызывать .Single () после .FromSql ()?
@ Гримм, правильно. Другой вариант - использовать ADO.NET с помощью GetDbConnection и ExecuteScalar.
Марк Джи, мне очень жаль, что я должен спросить еще раз, но что именно сейчас правильно?
Похоже на DbQuery не рекомендуется для EF Core 3, значит, это должен быть DbSet<PrimitiveDto<int>>.
Это правда, просто замените в этом шаблоне DbQuery <...> на DbSet <...>.





К сожалению, похоже, что эта функция была оставлена в стороне: https://github.com/aspnet/EntityFrameworkCore/issues/9810
Можно заключить вызовы функций в статический класс, используя небольшую таблицу, которая никогда не бывает пустой:
public static class DbFunctions
{
public static decimal MyFunctionABC(int param1, int param2)
{
using (var db = new MyDbContext())
{
return db.table.Take(1).Select(t => MyDbContext.MyFunctionABC(x, y)).Single();
}
}
}
Тогда вы можете вызвать в DbFunctions.MyFunctionABC(x,y);
Я не вижу в этом серьезных проблем, но вы, вероятно, можете немного реорганизовать его, чтобы вам не приходилось предоставлять весь запрос при каждом вызове. Создание свойства в контексте, которое возвращает
BasicDto<int>для каждой функции / процедуры, например, принимает любые требуемые аргументы.