C# 无法将EF Core Linq转换为SQLite,可在SQL Server上运行

C# 无法将EF Core Linq转换为SQLite,可在SQL Server上运行,c#,entity-framework,sqlite,linq,entity-framework-core,C#,Entity Framework,Sqlite,Linq,Entity Framework Core,我有一个linq表达式,它在生产数据库上运行良好,但在测试上下文的内存数据库中的SQLite上抛出错误。我得到的错误是: The LINQ expression (EntityShaperExpression: EntityType: Item ValueBufferExpression: (ProjectionBindingExpression: Inner) IsNullable: True ).Price * (Nullable<decimal>)(decimal)

我有一个linq表达式,它在生产数据库上运行良好,但在测试上下文的内存数据库中的SQLite上抛出错误。我得到的错误是:

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.
然后我有一个投影:

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

如果我删除SumPrice计算,它将按预期工作(与生产系统一样)。如何处理此查询才能在SqlServer和SQLite内存数据库中同样工作?

好的,解决方案是将我的投影类属性(SumPrice)更改为使用double而不是decimal,并将值转换为double:

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

我不知道是什么原因造成的。SQLite或其提供程序是否有十进制类型的问题?

好的,解决方案是将我的投影类属性(SumPrice)更改为使用double而不是decimal,并将值转换为double:

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

我不知道是什么原因造成的。SQLite或其提供程序是否对十进制类型有问题?

您可能将在生产环境中的代码用于SQLite以外的数据库。您可能不希望基于对开发数据库的依赖关系更改代码。在上下文类中,在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());
                }
            }
        }
    }
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
基于模型创建(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.getExecutionGassembly());
if(Database.ProviderName==“Microsoft.EntityFrameworkCore.Sqlite”)
{
foreach(modelBuilder.Model.GetEntityTypes()中的var entityType)
{
var properties=entityType.ClrType.GetProperties(),其中(p=>p.PropertyType==typeof(decimal));
var dateTimeProperties=entityType.ClrType.GetProperties()
其中(p=>p.PropertyType==typeof(DateTimeOffset));
foreach(属性中的var属性)
{
modelBuilder.Entity(entityType.Name).Property(Property.Name).HasConversion();
}
foreach(dateTimeProperties中的var属性)
{
modelBuilder.Entity(entityType.Name).Property(Property.Name)
.HasConversion(新的DateTimeOffsetToBinaryConverter());
}
}
}
}

您可能会在生产环境中将代码用于sqllite以外的数据库。您可能不希望基于对开发数据库的依赖关系更改代码。在上下文类中,在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());
                }
            }
        }
    }
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
基于模型创建(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.getExecutionGassembly());
if(Database.ProviderName==“Microsoft.EntityFrameworkCore.Sqlite”)
{
foreach(modelBuilder.Model.GetEntityTypes()中的var entityType)
{
var properties=entityType.ClrType.GetProperties(),其中(p=>p.PropertyType==typeof(decimal));
var dateTimeProperties=entityType.ClrType.GetProperties()
其中(p=>p.PropertyType==typeof(DateTimeOffset));
foreach(属性中的var属性)
{
modelBuilder.Entity(entityType.Name).Property(Property.Name).HasConversion();
}
foreach(dateTimeProperties中的var属性)
{
modelBuilder.Entity(entityType.Name).Property(Property.Name)
.HasConversion(新的DateTimeOffsetToBinaryConverter());
}
}
}
}

Try
.Sum(x=>x.Item.Price*(decimal)x.Qty)
Try
.Sum(x=>x.Item.Price*(decimal)x.Qty)
SQLite没有
decimal
类型,只有
FLOAT
。这是一个非常有限的数据库。你不应该一开始就创造这样的环境。ORMs不是SQL的替代品,它们是用来将对象映射到表的。它们不适用于像这样复杂的报告查询。您可以为不同的数据库创建不同的视图,并将您的实体(或者更确切地说,无键实体)映射到该视图。视图的查询可能比您使用的LINQ查询生成的SQL查询EF简单得多,速度也快得多。。。。。近似左连接和ISNULL@PanagiotisKanavos谢谢你的提醒。我只在测试中使用SQLite,项目的主要数据库提供程序是SQL Server。如果我可以避免一些已知的限制,EF创建的SQL查询与我在数据库上下文(生产和测试)中创建的SQL查询几乎相同。出于性能原因,我不得不编写一些视图,但这意味着我必须为这两个数据库提供程序编写视图。因此,EF在这种情况下也是一个巨大的帮助。SQLite没有
decimal
类型,只有
FLOAT
。这是一个非常有限的数据库。你不应该一开始就创造这样的环境。ORMs不是SQL的替代品,它们是用来将对象映射到表的。它们不适用于像这样复杂的报告查询。您可以为不同的数据库创建不同的视图,并将您的实体(或者更确切地说,无键实体)映射到该视图。视图的查询可能比您使用的LINQ查询生成的SQL查询EF简单得多,速度也快得多。。。。。近似左连接和ISNULL@PanagiotisKanavos谢谢你的提醒。我只在测试中使用SQLite,项目的主要数据库提供程序是SQL Server。如果我可以避免一些已知的限制,EF创建的SQL查询与我在数据库上下文(生产和测试)中创建的SQL查询几乎相同。出于性能原因,我不得不编写一些视图,但这意味着我必须为这两个数据库提供程序编写视图。所以EF在这种情况下也是一个巨大的帮助。