如何使用EntityFramework 6.2将DbFunctions / SqlFunctions调用提取到可恢复的扩展/方法中?

时间:2018-12-26 20:31:46

标签: c# asp.net-mvc entity-framework linq entity-framework-6

我在ASP.NET MVC 5框架的顶部有一个使用C#编写的应用程序。另外,我正在使用EntityFramework 6.2作为ORM与我的数据进行交互。

我使用Fluent LINQ编写了以下join语句

List<string> names = new List<string>{"", ....};
query = TopLevelQuery.Where(x => x.EndedAt.HasValue && x.StartedAt >= startedAt && x.EndedAt.Value <= endedAt)
                     .Join(UserService.QueryUniqueRecords(),
                     entry => entry.UserId,
                     rec => rec.UserId,
                     (entry, rec) => new { entry, rec })
                     .Where(result => result.entry.IsEqualDateOf(result.rec.DayOf) 
                         && names.Contains(result.rec.Name))
                     .Select(x => x.entry);

但是,在运行时出现以下错误

  

LINQ to Entities无法识别方法'IsEqualDateOf',并且   此方法不能转换为商店表达式。

这是我的IsEqualDateOf扩展方法

public static class MyModelExtensions
{
    public static DateTime GetLocalDate(this MyModel entry)
    {
        var local = DbFunctions.TruncateTime(SqlFunctions.DateAdd("ss", entry.UtcOffset, entry.StartedAt));

        return local.Value;
    }

    public static bool IsEqualDateOf(this MyModel entry, DateTime dateOf)
    {
        bool isEqual = entry.GetLocalDate().Equals(dateOf.Date);

        return isEqual;
    }
}

但是,如果我将LINQ表达式转换为以下伪表达式,它将按预期工作

query = TopLevelQuery.Where(x => x.EndedAt.HasValue && x.StartedAt >= startedAt && x.EndedAt.Value <= endedAt)
                     .Join(UserService.QueryUniqueRecords(),
                     entry => entry.UserId,
                     rec => rec.UserId,
                     (entry, rec) => new { entry, rec })
                     .Where(result => DbFunctions.TruncateTime(SqlFunctions.DateAdd("ss", entry.UtcOffset, entry.StartedAt)) == result.rec.DayOf 
                         && names.Contains(result.rec.Name))
                     .Select(x => x.entry);

但是,我希望能够在项目中的多个地方重用相同的逻辑,这就是为什么我想将其提取到某种扩展或方法中的原因。

如何将DbFunctionsSqlFunctions调用提取到可重用的方法中,在调用AsEnumerable()之前可在LINQ中使用该方法?

已更新

我还尝试通过向MyModel类添加以下代码来将逻辑提取到lambda表达式中

public class MyModel
{
    public DateTime StartedAt { get; set; }
    public int UtcOffset { get; set; }
    // ...

    public Expression<Func<MyModel, bool>> IsDateOf(DateTime dayOf)
    {
        return p => p.StartedAt.AddSeconds(p.UtcOffset) == dayOf.Date;
    }
}

然后我尝试像这样食用它

query = TopLevelQuery.Where(x => x.EndedAt.HasValue && x.StartedAt >= startedAt && x.EndedAt.Value <= endedAt)
                     .Join(UserService.QueryUniqueRecords(),
                     entry => entry.UserId,
                     rec => rec.UserId,
                     (entry, rec) => new { entry, rec })
                     .Where(result => result.entry.IsDateOf(result.rec.DayOf) 
                         && names.Contains(result.rec.Name))
                     .Select(x => x.entry);

但是在尝试使用它时会引发以下语法错误

  

运算符'&&'不能应用于类型的操作数   'Expression<Func<MyModel,bool>>'和bool。

此外,我尝试将IsDateOf表达式设为静态,并这样称呼它

query = TopLevelQuery.Where(x => x.EndedAt.HasValue && x.StartedAt >= startedAt && x.EndedAt.Value <= endedAt)
                     .Join(UserService.QueryUniqueRecords(),
                     entry => entry.UserId,
                     rec => rec.UserId,
                     (entry, rec) => new { entry, rec })
                     .Where(result => result.entry.Where(MyModel.IsDateOf(result.rec.DayOf))
                         && names.Contains(result.rec.Name))
                     .Select(x => x.entry);

但这给了我以下语法错误

  

“ MyMode”不包含Where和最佳定义   扩展方法重载   Queryable.Where<MyModel>IQueryable<MyModel>, Expression<Func<MyModel, bool>>)需要一个IQueryable类型的接收者

1 个答案:

答案 0 :(得分:2)

从方法中删除参数,并使表达式接受两个参数:

public static Expression<Func<MyModel, DateTime, bool>> IsDateOf 
    = (MyModel p, DateTime d) => p.StartedAt.AddSeconds(p.UtcOffset) == d.Date;

请注意,我将其设置为静态字段,而不是方法。然后在LINQ表达式中,您需要调用它:

MyModel.IsDateOf(result.entry, result.rec.DayOf)

如果不将其设为字段(或属性),则必须首先调用该方法以获取表达式;那么您需要调用表达式:

MyModel.IsDateOf()(result.entry, result.rec.DayOf)

在我看来,这看起来很奇怪。