我使用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?;
}
}
我尝试在查询中使用标量函数(为简化起见,我使用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();
但是得到System.InvalidOperationException:'在上一个操作完成之前,第二个操作在此上下文上启动。不能保证任何实例成员都是线程安全的。'
我该如何实现?
更新: 我在Ivan的答案的帮助下成功做到了:
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();
但是现在我需要为已过滤人员更新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();
但是出现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();
答案 0 :(得分:4)
您可以使用EF Core {{3},而不是调用功能客户端(在查询过滤器的client evaluation中,这种特殊情况会发生,而查询仍在进行中) }
可以在LINQ查询中使用并转换为SQL。
一种方法是在派生的上下文类中创建一个公共静态方法,并用DbFunction
属性对其进行标记:
public partial class AdventureWorks2012Context
{
[DbFunction("fn_AddDayPeriod")]
public static DateTime? AddDayPeriod(DateTime dateTime, int days, string periodName) => throw new NotSupportedException();
}
并使用
where p.ModifiedDate > AdventureWorks2012Context.AddDayPeriod(pa.ModifiedDate, 100, "DayPeriod_day")
另一种方法是在另一个类中创建一个公共静态方法
public static class AdventureWorks2012DbFunctions
{
[DbFunction("fn_AddDayPeriod")]
public static DateTime? AddDayPeriod(DateTime dateTime, int days, string periodName) => throw new NotSupportedException();
}
但是您需要使用流利的API注册它(对于上下文派生类中定义的方法,它会自动发生):
modelBuilder
.HasDbFunction(() => AdventureWorks2012DbFunctions.AddDayPeriod(default(DateTime), default(int), default(string)));
用法相同:
where p.ModifiedDate > AdventureWorksDbFunctions.AddDayPeriod(pa.ModifiedDate, 100, "DayPeriod_day")