EF Core LINQ 使用标量函数

Dmi*_*nov 7 linq entity-framework-core

我使用实体框架核心 2.1。

我在数据库中有一个标量函数,它添加了指定的天数。我创建了一个扩展方法来执行它:

public static class AdventureWorks2012ContextExt
    {
        public static DateTime? ExecFn_AddDayPeriod(this AdventureWorks2012Context db, DateTime dateTime, int days, string periodName)
        {
            var sql = $"set @result = dbo.[fn_AddDayPeriod]('{dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}', {days}, '{periodName}')";
            var output = new SqlParameter { ParameterName = @"result", DbType = DbType.DateTime, Size = 16, Direction = ParameterDirection.Output };
            var result = db.Database.ExecuteSqlCommand(sql, output);
            return output.Value as DateTime?;
        }
    }
Run Code Online (Sandbox Code Playgroud)

我尝试在查询中使用标量函数(为了简化我使用 AdventureWorks2012 的事情),如下所示:

var persons =
    (from p in db.Person
     join pa in db.Address on p.BusinessEntityId equals pa.AddressId
     where p.ModifiedDate > db.ExecFn_AddDayPeriod(pa.ModifiedDate, 100, "DayPeriod_day")
     select p).ToList();
Run Code Online (Sandbox Code Playgroud)

但是得到一个 System.InvalidOperationException: '在前一个操作完成之前在这个上下文上启动了第二个操作。不保证任何实例成员都是线程安全的。

我怎样才能做到这一点?

更新: 我在伊万的回答的帮助下设法做到了:

var persons =
    (from p in db.Person
     join bea in db.BusinessEntityAddress on p.BusinessEntityId equals bea.BusinessEntityId
     join a in db.Address on bea.AddressId equals a.AddressId
     where p.ModifiedDate > AdventureWorks2012ContextFunctions.AddDayPeriod(a.ModifiedDate, 100, "DayPeriod_day")
     select p).ToList();
Run Code Online (Sandbox Code Playgroud)

但现在我需要为过滤的人更新 ModifiedDate。所以我这样做:

var persons =
     (from p in db.Person
      join bea in db.BusinessEntityAddress on p.BusinessEntityId equals bea.BusinessEntityId
      join a in db.Address on bea.AddressId equals a.AddressId
      let date = AdventureWorks2012ContextFunctions.AddDayPeriod(a.ModifiedDate, 100, "DayPeriod_day")
      where p.ModifiedDate > date
      select new { Person = p, NewDate = date }).ToList();

  foreach (var p in persons)
      p.Person.ModifiedDate = p.NewDate ?? DateTime.Now;

  db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

但是得到 System.NotSupportedException: '不支持指定的方法。'

如何在select语句中使用标量函数?

我试图将查询分成两部分:

var filteredPersons =                  // ok   
   (from p in db.Person
    join bea in db.BusinessEntityAddress on p.BusinessEntityId equals bea.BusinessEntityId
    join a in db.Address on bea.AddressId equals a.AddressId
    where p.ModifiedDate > AdventureWorks2012ContextFunctions.AddDayPeriod(a.ModifiedDate, 100, "DayPeriod_day")
    select new { Person = p, a.ModifiedDate }).ToList();

var persons =                          // here an exception occurs
    (from p in filteredPersons
     select new { Person = p, NewDate = AdventureWorks2012ContextFunctions.AddDayPeriod(p.ModifiedDate, 100, "DayPeriod_day") }).ToList();
Run Code Online (Sandbox Code Playgroud)

Iva*_*oev 6

而不是调用函数客户端(这是这种特殊情况下发生的的一部分的客户评价查询过滤器,而查询阅读仍在进行中),您可以使用EF核心数据库标量函数映射,因此

可用于 LINQ 查询并转换为 SQL。

一种方法是在派生的上下文类中创建一个公共静态方法并用DbFunction属性标记它:

public partial class AdventureWorks2012Context
{
    [DbFunction("fn_AddDayPeriod")]
    public static DateTime? AddDayPeriod(DateTime dateTime, int days, string periodName) => throw new NotSupportedException();
} 
Run Code Online (Sandbox Code Playgroud)

并使用

where p.ModifiedDate > AdventureWorks2012Context.AddDayPeriod(pa.ModifiedDate, 100, "DayPeriod_day")
Run Code Online (Sandbox Code Playgroud)

另一种方法是在另一个类中创建公共静态方法

public static class AdventureWorks2012DbFunctions
{
    [DbFunction("fn_AddDayPeriod")]
    public static DateTime? AddDayPeriod(DateTime dateTime, int days, string periodName) => throw new NotSupportedException();
} 
Run Code Online (Sandbox Code Playgroud)

但是随后您需要使用 fluent API 注册它(这对于在上下文派生类中定义的方法会自动发生):

modelBuilder
    .HasDbFunction(() => AdventureWorks2012DbFunctions.AddDayPeriod(default(DateTime), default(int), default(string)));
Run Code Online (Sandbox Code Playgroud)

用法是一样的:

where p.ModifiedDate > AdventureWorksDbFunctions.AddDayPeriod(pa.ModifiedDate, 100, "DayPeriod_day")
Run Code Online (Sandbox Code Playgroud)