简体   繁体   English

EF Core Linq 到 SQLite 无法翻译,适用于 SQL 服务器

[英]EF Core Linq to SQLite could not be translated, works on SQL Server

I have a linq expression that is working fine on the production database but throws error on the SQLite in memory db of the test context.我有一个 linq 表达式,它在生产数据库上运行良好,但在测试上下文的 memory db 中的 SQLite 上抛出错误。 The error I got says:我得到的错误是:

The LINQ expression (EntityShaperExpression:

EntityType: Item
ValueBufferExpression: 
    (ProjectionBindingExpression: Inner)
IsNullable: True ).Price * (Nullable<decimal>)(decimal)(EntityShaperExpression: 
EntityType: ISItem
ValueBufferExpression: 
    (ProjectionBindingExpression: Outer)
IsNullable: False ).Qty' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

The linq expression: linq 表达式:

var locationsQuery = context.DbContext.Locations
            .Include(x => x.Check)
            .Include(x => x.Scan)
            .Include(x => x.EScan)
                .ThenInclude(es => es!.Items)
                    .ThenInclude(isi => isi.Item)
            .Where(x => x.ProjectId == query.ProjectId)
            .Select(x => x);

Then I have a projection:然后我有一个预测:

LocationId = entity.Id,
        LHA = entity.LHA,
        Zone = entity.Zone,
        Area = entity.Area,
        LocationState = $"DB.{nameof(LocationState)}.{entity.State.ToString()}",
        CheckUserId = entity.Check != null ? entity.Check.ScanUserId : (int?)null,
        ScanUserId = entity.Scan != null ? entity.Scan.ScanUserId : (int?)null,
        CheckUserName = entity.Check != null ? entity.Check.ScanUser.Name : null,
        ScanUserName = entity.Scan != null ? entity.Scan.ScanUser.Name : null,
        SumPrice = entity.EffectiveScan != null // This cannot be evaluated
                        ? entity.EScan.Items
                            .Where(x => x.Item != null)
                            .Sum(x => x.Item!.Price * (decimal)x.Qty)
                        : null,
        SumQty = entity.EScan != null
                        ? entity.EScan.Items
                            .Sum(x => x.Qty)
                        : (double?)null

If I remove the SumPrice calculation it works as expected (as on the production system).如果我删除 SumPrice 计算,它会按预期工作(与在生产系统上一样)。 What can I do to this query to work the same on SqlServer and SQLite In memory db?我可以对此查询做些什么才能在 SqlServer 和 SQLite In memory db 上发挥相同的作用?

You probably are going to use the code in production environment with some database other than sqllite.您可能会在生产环境中使用除 sqllite 以外的其他数据库的代码。 You may not want to change your code based on a dependency on your development database.您可能不想根据对开发数据库的依赖来更改代码。 in your context class, in OnModelCreating method add below snippet and remove casting from your code.在您的上下文类中,在 OnModelCreating 方法中添加以下代码段并从您的代码中删除转换。

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());

        if (Database.ProviderName == "Microsoft.EntityFrameworkCore.Sqlite")
        {
            foreach (var entityType in modelBuilder.Model.GetEntityTypes())
            {
                var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == typeof(decimal));
                var dateTimeProperties = entityType.ClrType.GetProperties()
                    .Where(p => p.PropertyType == typeof(DateTimeOffset));

                foreach (var property in properties)
                {
                    modelBuilder.Entity(entityType.Name).Property(property.Name).HasConversion<double>();
                }

                foreach (var property in dateTimeProperties)
                {
                    modelBuilder.Entity(entityType.Name).Property(property.Name)
                        .HasConversion(new DateTimeOffsetToBinaryConverter());
                }
            }
        }
    }

Okay, the solution was to change my projection class property (SumPrice) to use double instead of decimal and convert the value to double:好的,解决方案是将我的投影类属性 (SumPrice) 更改为使用 double 而不是十进制并将值转换为 double:

SumPrice = entity.EffectiveScan != null
             ? entity.EffectiveScan.Items
                     .Where(x => x.Item != null)
                     .Sum(x => x.Qty * (double?)x.Item!.Price)
             : (double?)null,

I don't know what caused this.我不知道是什么原因造成的。 Does SQLite or its provider has problem with the decimal type? SQLite 或其提供者是否对十进制类型有问题?

Or you can query all the data first and process it locally instead of needing to translate to sql或者你可以先查询所有数据,然后在本地处理而不需要翻译成sql

var result = _context.Votes
    .Where(x => x.Id == id)
    .Select(x => new VotePublicViewModel
    {
        Title = x.Title,
        Deadline = x.Deadline,
        IsMultiple = x.IsMultiple,
        IsPublish = x.IsPublish,
        VoteItems = x.VoteItems
            .Select(item => new VotePublicViewModel.Item
            {
                Id = item.Id,
                ItemName = item.ItemName,
                Count = item.Count,
                // notice: not supported sqlite
                // Percentage = item.Count == 0 ? "0%" :
                //     (item.Count / (decimal)x.VoteItems.Where(v => v.Count != 0).Sum(v => v.Count)).ToString("p"),
            })
    })
    .SingleOrDefault();

if (result != null)
{
    foreach (var item in result.VoteItems)
    {
        item.Percentage = item.Count == 0
            ? "0%"
            : (item.Count / (decimal) result.VoteItems.Where(v => v.Count != 0).Sum(v => v.Count)).ToString("p");
    }
}

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM