'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. 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:

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). What can I do to this query to work the same on SqlServer and SQLite In memory db?



Solution 1:[1]

You probably are going to use the code in production environment with some database other than 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.

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());
                }
            }
        }
    }

Solution 2:[2]

Okay, the solution was to change my projection class property (SumPrice) to use double instead of decimal and convert the value to 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?

Solution 3:[3]

Or you can query all the data first and process it locally instead of needing to translate to 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");
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Iman
Solution 2 Perrier
Solution 3