简体   繁体   English

LINQ 表达式 'DbSet<> 无法转换方法 'System.DateTime.ToString' 失败,从 ASP.NET Core 2.1 迁移到 .NET6 后

[英]The LINQ Expression 'DbSet<> Could not be translated method 'System.DateTime.ToString' failed, After Migrated from ASP.NET Core 2.1 to .NET6

I have migrated my project ASP.NET Core 2.1 to .NET6, but some of my LINQ Query do not work properly and give the below error:我已将我的项目 ASP.NET Core 2.1 迁移到 .NET6,但我的一些 LINQ 查询无法正常工作并出现以下错误:

System.InvalidOperationException Message=The LINQ expression 'DbSet().Where(p => p.PaymentDate.ToString("MMM-yyyy").Equals(__ToString_0) && p.PaymentType == "Treatment Fee")' could not be translated. System.InvalidOperationException Message=The LINQ expression 'DbSet().Where(p => p.PaymentDate.ToString("MMM-yyyy").Equals(__ToString_0) && p.PaymentType == "Treatment Fee")' 不能翻译。 Additional information: Translation of method 'System.DateTime.ToString' failed.附加信息:方法“System.DateTime.ToString”的翻译失败。 If this method can be mapped to your custom function, Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.如果此方法可以映射到您的自定义 function,请以可以翻译的形式重写查询,或者通过插入对“AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”的调用来显式切换到客户端评估.

Here is my Home Controller Code in ASP.NET Core 2.1 which returns data from Database As a Monthly and Daily report and displays them in a Line chart and ViewBag.这是我的主页 Controller ASP.NET Core 2.1 中的代码,它以每月和每日报告的形式从数据库返回数据,并将它们显示在折线图和 ViewBag 中。 This Code is working fine with asp.net core 2.1, but after I migrated to .NET6 it gives an error for each line of LINQ Query Code that I used Date String Formatting for Example: a.RegisterDate.Value.Date.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy")此代码在 asp.net 核心 2.1 上运行良好,但在我迁移到 .NET6 后,它为我使用日期字符串格式的 LINQ 查询代码的每一行提供了一个错误,例如: a.RegisterDate.Value.Date.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy")

Here Is my Home Controller code in ASP.NET Core 2.1 That Need to be modified in .NET6 LINQ Query Format, please help me to modify all this code from asp.net core 2.1 to .NET6 LINQ Query, Thank You.这是我家 Controller 代码在 ASP.NET Core 2.1 需要修改.NET6 LINQ 查询格式,请帮我把这段代码从 asp.net core 2.1 修改成 .NET6 LINQ 查询,谢谢。

public class HomeController : BaseController
{
    private readonly IMvcControllerDiscovery _mvcControllerDiscovery;
    private readonly IWebHostEnvironment _hostingEnvironment;
    public HomeController(HoshmandDBContext context, IWebHostEnvironment hostingEnvironment, IMvcControllerDiscovery mvcControllerDiscovery) : base(context)
    {
        _hostingEnvironment = hostingEnvironment;
        _mvcControllerDiscovery = mvcControllerDiscovery;
    }
    public IActionResult Index(DateTime? date = null)
    {
        date = date ?? GetLocalDateTime();
        ViewBag.date = date;
        // MonthlyTransectionList(date);
        ViewBag.CompletedPatients = _context.PatientTbs.Where(a => a.PatientStatus == "Completed" && !a.IsDeleted && a.RegisterDate.Value.Date.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy"))).Count();

        string[] monthName = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
        // patient chart registration
        ViewBag.PatientRegistrationStatisticsInCurrentYear = GetNumberOfPatientsPerMonthInCurrentYear(monthName);

        // transaction chart
        List<IncomeAndOutcome> incomeAndOutcomes = new List<IncomeAndOutcome>();
        var IncomePerMonthInCurrentYear = GetIncomePerMonthInCurrentYear(monthName);
        var OutcomePerMonthInCurrentYear = GetOutcomePerMonthInCurrentYear(monthName);
        foreach (var item in IncomePerMonthInCurrentYear)
        {
            incomeAndOutcomes.Add(new IncomeAndOutcome
            {
                month = item.Key,
                Income = item.Value,
                Outcome = OutcomePerMonthInCurrentYear.FirstOrDefault(a => a.Key == item.Key).Value
            });
        }
        ViewBag.IncomeAndOutcomeStatisticsInCurrentYear = incomeAndOutcomes;
        var _todayAppointments = _context.AppointmentTbs
            .Where(a => !a.IsDeleted && a.AppointmentDate.Value.ToString("MMM-yyyy") == date.Value.ToString("MMM-yyyy"))
            .GroupBy(a => a.SesstionGroup);

        List<TodayAppointment> todayAppointments = new List<TodayAppointment>();
        if (_todayAppointments.Any(a => a.FirstOrDefault().AppointmentStatus == 4))
        {
            todayAppointments.Add(new TodayAppointment
            {
                status = "Pending",
                count = _todayAppointments.Where(a => a.FirstOrDefault().AppointmentStatus == 4).Count()
            });
        }
        else
        {
            todayAppointments.Add(new TodayAppointment
            {
                status = "Pending",
                count = 0
            });
        }
        if (_todayAppointments.Any(a => a.FirstOrDefault().AppointmentStatus == 1))
        {
            todayAppointments.Add(new TodayAppointment
            {
                status = "Completed",
                count = _todayAppointments.Where(a => a.FirstOrDefault().AppointmentStatus == 1).Count()
            });
        }
        else
        {
            todayAppointments.Add(new TodayAppointment
            {
                status = "Completed",
                count = 0
            });
        }
        if (_todayAppointments.Any(a => a.FirstOrDefault().AppointmentStatus == 3))
        {
            todayAppointments.Add(new TodayAppointment
            {
                status = "Canceled",
                count = _todayAppointments.Where(a => a.FirstOrDefault().AppointmentStatus == 3).Count()
            });
        }
        else
        {
            todayAppointments.Add(new TodayAppointment
            {
                status = "Canceled",
                count = 0
            });
        }
        ViewBag.TodayAppointments = todayAppointments;

        return View();
    }
    private IActionResult MonthlyTransectionList(DateTime? date = null)
    {
        ViewBag.MonthlyPatientIcomeTreatmentFee = _context.PatientPaymentHistories.Where(a => a.PaymentDate.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy")) && a.PaymentType == "Treatment Fee").Sum(a => a.PaidAmount);
        ViewBag.MonthlyPatientIcomeVisitFee = _context.PatientPaymentHistories.Where(a => a.PaymentDate.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy")) && a.PaymentType == "Checkup Fee").Sum(a => a.PaidAmount);
        ViewBag.MonthlyExpenseOut = _context.Expenses.Where(a => !a.IsDelete && a.ExpenseDate.Date.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy"))).Sum(a => a.ExpenseAmount);
        ViewBag.MonthlyStockOut = _context.StockTransectionTbs.Where(a => a.TransectionDate.Value.Date.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy"))).Sum(a => a.TransectionAmount);
        ViewBag.MonthlyPatientRefunded = _context.PatientPaymentHistories.Where(a => a.PaymentDate.Date.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy")) && a.PaymentType == "Refund").Sum(a => a.PaidAmount);
        ViewBag.MonthlyLabOut = _context.labsPayments.Where(a => a.PaymentDate.Date.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy"))).Sum(a => a.TotalPaid);
        ViewBag.MonthlySalaryOut = _context.EmployeeTransectionTbs.Where(a => !a.IsDeleted && a.TransectionDate.Value.Date.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy"))).Sum(a => a.TransectionAmount);
        ViewBag.MonthlyDebitedTransectionOut = _context.OtherTransectionTbs.Where(a => !a.IsDeleted && a.TransectionType == "Debited" && a.TransectionDate.Value.Date.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy"))).Sum(a => a.TransectionAmount);
        ViewBag.MonthlyCreditedTransectionOut = _context.OtherTransectionTbs.Where(a => !a.IsDeleted && a.TransectionType == "Credited" && a.TransectionDate.Value.Date.ToString("MMM-yyyy").Equals(date.Value.ToString("MMM-yyyy"))).Sum(a => a.TransectionAmount);
        return View();
    }
    private PatientRegistrationStatisticsInCurrentYear GetNumberOfPatientsPerMonthInCurrentYear(string[] monthName)
    {
        var PatientsInCurrentYear = _context.PatientTbs.Where(a => !a.IsDeleted && a.IsActive.Value && a.RegisterDate.Value >= new DateTime(GetLocalDateTime().Year, 1, 1).Date);
        PatientRegistrationStatisticsInCurrentYear patientRegistrationStatisticsInCurrentYear = new PatientRegistrationStatisticsInCurrentYear
        {
            totalPatient = PatientsInCurrentYear.Count(),
            year = new DateTime(GetLocalDateTime().Year, 1, 1).Year,
            PatientPerMonth = new Dictionary<string, int>()
        };
        foreach (var item in monthName)
        {
            patientRegistrationStatisticsInCurrentYear.PatientPerMonth[item] = PatientsInCurrentYear.Where(a => a.RegisterDate.Value.ToString("MMM") == item).Count();
        }
        return patientRegistrationStatisticsInCurrentYear;
    }
    private Dictionary<string, decimal> GetIncomePerMonthInCurrentYear(string[] monthName)
    {
        Dictionary<string, decimal> Income = new Dictionary<string, decimal>();
        foreach (var month in monthName)
        {
            var income = _context.PatientPaymentHistories
                .Where(a => a.PaymentDate.Date >= new DateTime(GetLocalDateTime().Year, 1, 1) && !a.IsDeleted &&
                 !string.Equals(a.PaymentType, "Refund", StringComparison.CurrentCultureIgnoreCase)
                 && a.PaymentDate.ToString("MMM") == month).Sum(a => a.PaidAmount)
                + _context.OtherTransectionTbs.Where(a => a.TransectionDate.Value.Date >= new DateTime(GetLocalDateTime().Year, 1, 1)
                 && !a.IsDeleted && string.Equals(a.TransectionType, "Credited", StringComparison.CurrentCultureIgnoreCase)
                 && a.TransectionDate.Value.ToString("MMM") == month).Sum(a => a.TransectionAmount);
            Income.Add(month, income.Value);
        }
        return Income;
    }
    private Dictionary<string, decimal> GetOutcomePerMonthInCurrentYear(string[] monthName)
    {
        Dictionary<string, decimal> outcome = new Dictionary<string, decimal>();
        foreach (var month in monthName)
        {
            var og = _context.Expenses.Where(a => !a.IsDelete && a.ExpenseDate.Date >= new DateTime(GetLocalDateTime().Year, 1, 1) && a.ExpenseDate.ToString("MMM") == month).Sum(a => a.ExpenseAmount)
             + _context.StockTransectionTbs.Where(a => a.TransectionDate.Value.Date >= new DateTime(GetLocalDateTime().Year, 1, 1) && a.TransectionDate.Value.ToString("MMM") == month).Sum(a => a.TransectionAmount)
             + _context.PatientPaymentHistories.Where(a => a.PaymentDate.Date >= new DateTime(GetLocalDateTime().Year, 1, 1) && a.PaymentDate.ToString("MMM") == month && a.PaymentType == "Refund").Sum(a => a.PaidAmount)
             + _context.labsPayments.Where(a => a.PaymentDate.Date >= new DateTime(GetLocalDateTime().Year, 1, 1) && a.PaymentDate.ToString("MMM") == month).Sum(a => a.TotalPaid)
             + _context.EmployeeTransectionTbs.Where(a => !a.IsDeleted && a.TransectionDate.Value.Date >= new DateTime(GetLocalDateTime().Year, 1, 1) && a.TransectionDate.Value.ToString("MMM") == month).Sum(a => a.TransectionAmount)
             + _context.OtherTransectionTbs.Where(a => !a.IsDeleted && a.TransectionType == "Debited" && a.TransectionDate.Value.Date >= new DateTime(GetLocalDateTime().Year, 1, 1) && a.TransectionDate.Value.ToString("MMM") == month).Sum(a => a.TransectionAmount);
            outcome.Add(month, og.Value);
        }
        return outcome;
    }
}
public class PatientRegistrationStatisticsInCurrentYear
{
    public int year { get; set; }
    public int totalPatient { get; set; }
    public Dictionary<string, int> PatientPerMonth { get; set; }
}

public class IncomeAndOutcome
{
    public string month { get; set; }
    public decimal Income { get; set; }
    public decimal Outcome { get; set; }
}
public class TodayAppointment
{
    public string status { get; set; }
    public int count { get; set; }
}

EF Core 2 had automatic silent client side evaluation enabled, which was disabled for later versions - see the corresponding breaking change : EF Core 2 启用了自动静默客户端评估,但在更高版本中已禁用 - 请参阅相应的重大更改

Old behavior旧行为

Before 3.0, when EF Core couldn't convert an expression that was part of a query to either SQL or a parameter, it automatically evaluated the expression on the client.在 3.0 之前,当 EF Core 无法将作为查询一部分的表达式转换为 SQL 或参数时,它会自动在客户端上计算表达式。 By default, client evaluation of potentially expensive expressions only triggered a warning.默认情况下,客户端对可能昂贵的表达式的评估只会触发警告。

New behavior新行为

Starting with 3.0, EF Core only allows expressions in the top-level projection (the last Select() call in the query) to be evaluated on the client.从 3.0 开始,EF Core 只允许在客户端计算顶级投影(查询中的最后一个 Select() 调用)中的表达式。 When expressions in any other part of the query can't be converted to either SQL or a parameter, an exception is thrown.当查询的任何其他部分中的表达式无法转换为 SQL 或参数时,将引发异常。

As a quick fix you explicitly evaluate on client side (via AsEnumerable or ToList and their async counterparts), but in general I would argue that you should consider rewriting queries so they are translated into SQL (based on your database you should look into supported function mappings, like here for SQL Server , based on exception message you should look into using correct datetime functions, which compare dateparts).作为快速修复,您可以在客户端显式评估(通过AsEnumerableToList及其异步副本),但总的来说,我认为您应该考虑重写查询,以便将它们转换为 SQL(根据您的数据库,您应该查看受支持的 function映射,例如此处的 SQL Server ,基于异常消息,您应该使用正确的日期时间函数来比较日期部分)。

Your approach to filter by month is not translatable by EF Core. EF Core 无法翻译您按月过滤的方法。 I would suggest to introduce extension method which will generate correct filter by month.我建议引入扩展方法,它将按月生成正确的过滤器。 And another benefit, if your tables has indexes on date - they will be used by Database server.另一个好处是,如果您的表有日期索引 - 它们将被数据库服务器使用。

Sample of usage:使用示例:

ViewBag.CompletedPatients = _context.PatientTbs.Where(a => a.PatientStatus == "Completed" && !a.IsDeleted)
   .FilterByMonth(date.Value, a => a.RegisterDate).Count();
ViewBag.CheckupPatients = _context.PatientTbs.Where(a => a.PatientStatus == "Checkup" && !a.IsDeleted)
   .FilterByMonth(date.Value, a => a.RegisterDate).Count();

Extension implemenation, it contains FilterByDay , FilterByMonth , FilterByYear and generic FilterByDateRange :扩展实现,它包含FilterByDayFilterByMonthFilterByYear和通用FilterByDateRange

public static class QueryableExtensions
{
    public static IQueryable<T> FilterByDay<T>(this IQueryable<T> query, DateTime date, Expression<Func<T, DateTime?>> dateField)
    {
        var start = date.Date;
        var end   = start.AddDays(1);

        return query.FilterByDateRange(start, end, dateField);
    }

    public static IQueryable<T> FilterByMonth<T>(this IQueryable<T> query, DateTime date, Expression<Func<T, DateTime?>> dateField)
    {
        var start = new DateTime(date.Year, date.Month, 1);
        var end   = start.AddMonths(1);

        return query.FilterByDateRange(start, end, dateField);
    }

    public static IQueryable<T> FilterByYear<T>(this IQueryable<T> query, DateTime date, Expression<Func<T, DateTime?>> dateField)
    {
        var start = new DateTime(date.Year, 1, 1);
        var end   = start.AddYears(1);

        return query.FilterByDateRange(start, end, dateField);
    }

    public static IQueryable<T> FilterByDateRange<T>(this IQueryable<T> query, DateTime startInclusive,
        DateTime endExclusive, Expression<Func<T, DateTime?>> dateField)
    {
        var entityParam = dateField.Parameters[0];
        var fieldExpr   = dateField.Body;

        // e.DateField >= startInclusive && e.DateField < endExclusive 
        var filterExpression = Expression.AndAlso(
            Expression.GreaterThanOrEqual(fieldExpr, Expression.Constant(startInclusive, fieldExpr.Type)),
            Expression.LessThan(fieldExpr, Expression.Constant(endExclusive, fieldExpr.Type)));

        // e => e.DateField >= startInclusive && e.DateField < endExclusive 
        var filterLambda = Expression.Lambda<Func<T, bool>>(filterExpression, entityParam);
        return query.Where(filterLambda);
    }
}

Also I have noticed that you have used EF Core extremely ineffective.我还注意到您使用的 EF Core 效率极低。 Access to the same table can be simplified:可以简化对同一张表的访问:

var statistic = _context.PatientTbs
    .Where(a => !a.IsDeleted)
    .FilterByMonth(date.Value, a => a.RegisterDate)
    .GroupBy(a => 1) // by constant
    .Select(g => new
    {
        Completed = g.Count(x => x.PatientStatus == "Completed"),
        Checkup = g.Count(x => x.PatientStatus == "Checkup"),
        Working = g.Count(x => x.PatientStatus == "Working"),
        Closed = g.Count(x => x.PatientStatus == "Closed")
    })
    .FirstOrDefault();

ViewBag.CompletedPatients = statistic?.Completed ?? 0;
ViewBag.CheckupPatients = statistic?.Checkup ?? 0;
ViewBag.WorkingPatients = statistic?.Working ?? 0;
ViewBag.ClosedPatients = statistic?.Closed ?? 0;

Also for month statistic query can be executed once:同样对于月统计查询可以执行一次:

string[] monthName = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; 

var patientsInCurrentYear = _context.PatientTbs.FilterByYear(date.Value, a => a.RegisterDate);

var statisticByMonth = patientsInCurrentYear
    .GroupBy(a => a.RegisterDate.Value.Month)
    .Select(g => new 
    {
        Month = g.Key,
        Count = g.Count();
    })
    .ToDictionary(x => x.Month);

for (var i = 0; i < monthName.Length; i++) 
{ 
    item = monthName[i];
    var count = 0;
    if (statisticByMonth.TryGetValue(i + 1, out var statistic))
        count = statistic.Count;

    patientRegistrationStatisticsInCurrentYear.PatientPerMonth[item] = count; 
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 System.InvalidOperationException:LINQ 表达式....'无法翻译。 ASP.NET 6...方法“DecodeFrom64”的翻译失败 - System.InvalidOperationException: The LINQ expression.... 'could not be translated. ASP.NET 6 ...Translation of method 'DecodeFrom64' failed Asp.net 内核 REST API Z979E08FD891EBB5526A70C82D741A40Z 表达式不能翻译 - Asp.net Core REST API The LINQ expression Could not be Translated ASP.NET 核心 Web API - System.InvalidOperationException: LINQ 表达式 'DbSet<mandate> ()</mandate> - ASP.NET Core Web API - System.InvalidOperationException: The LINQ expression 'DbSet<Mandate>() ASP.NET 内核 Web API controller; LINQ 表达式无法翻译 - ASP.NET Core Web API controller; the LINQ expression could not be translated .NET 核心运行时错误:LINQ 表达式无法翻译 - .NET Core runtime error: LINQ expression could not be translated .NET 核心 3.1 LINQ 表达式无法为列表中的列表翻译 - .NET Core 3.1 LINQ expression from could not be translated for lists within a list LINQ 表达式 DbSet<User> 无法翻译 - The LINQ expression DbSet<User> could not be translated LINQ 表达式 DbSet&lt;&gt;.Any 无法翻译 - The LINQ expression DbSet<>.Any could not be translated LINQ 表达式 'DbSet<students> ()' 无法翻译</students> - The LINQ expression 'DbSet<Students>()' could not be translated ASP.NET Core 2.1 - IdentityUser 问题 - 无法为“IdentityUser”创建 DbSet 此类型未包含在上下文模型中 - ASP.NET Core 2.1 - IdentityUser Issue - Cannot create a DbSet for 'IdentityUser' this type is not included in the model for the context
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM