question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Async enumeration of filtered projection produces exception

See original GitHub issue

Source/destination types

public class Caseload
{
    public int Id { get; set; }
    public virtual ICollection<CaseFile> CaseFiles { get; set; }
}

public class CaseFile
{
    public int Id { get; set; }
    public int FileResourceId { get; set; }
}

public class CaseloadDto
{
    public int Id { get; set; }
    public IEnumerable<CaseFileDto> CaseFiles { get; set; }
}

public class CaseFileDto
{
    public int Id { get; set; }
    public int FileResourceId { get; set; }
}

Mapping configuration

var configuration = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<Caseload, CaseloadDto>();
    cfg.CreateMap<CaseFile, CaseFileDto>();
});

Version:

“Automapper” = “8.0.0” (also 7.0.0 seems to be affected) “Microsoft.EntityFrameworkCore” = “2.2.0” “Microsoft.EntityFrameworkCore.Sqlite” = “2.2.0” (data provider does not seem to matter, I have reproduced on SqlServer as well)

Expected behavior

I expect to to be able to filter a projection using an enumerable property as a predicate

Actual behavior

An exception is thrown: System.ArgumentException: Expression of type ‘System.Collections.Generic.IAsyncEnumerable1[CaseFileDto]' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable1[CaseFileDto]’ of method ‘System.Collections.Generic.List1[CaseFileDto] ToList[CaseFileDto](System.Collections.Generic.IEnumerable1[CaseFileDto])’ Parameter name: arg0

See below for specific cases. It is a narrow issue and has workarounds.

Example program

public class Program
{
    public static async Task Main(string[] args)
    {
        using (var connection = new SqliteConnection("DataSource=:memory:"))
        {
            connection.Open();
            var options = new DbContextOptionsBuilder<DataContext>()
                .UseSqlite(connection)
                .Options;
            using (var context = new DataContext(options))
            {
                context.Database.EnsureCreated();
            }

            var configuration = new MapperConfiguration(cfg =>
            {
                cfg.CreateMap<Caseload, CaseloadDto>();
                cfg.CreateMap<CaseFile, CaseFileDto>();
            });

            using (var context = new DataContext(options))
            {
                var caseloads = context.Set<Caseload>();
                var automapperMap = caseloads.ProjectTo<CaseloadDto>(configuration);
                var manualMap = caseloads.Select(x => new CaseloadDto
                {
                    Id = x.Id,
                    CaseFiles = x.CaseFiles.Select(y => new CaseFileDto
                    {
                        Id = y.Id,
                        FileResourceId = y.FileResourceId
                    }),
                });

                // this is fine:
                await automapperMap.ToListAsync();

                // This works:
                var filteredA = manualMap.Where(x => x.CaseFiles.Any(y => y.FileResourceId > 0)); // silly predicate needed to trigger issue
                await filteredA.ToListAsync();

                // this works as well:
                var filteredB = automapperMap.Where(x => x.CaseFiles.Any(y => y.FileResourceId > 0));
                filteredB.ToList();

                // this does not:
                var filteredC = automapperMap.Where(x => x.CaseFiles.Any(y => y.FileResourceId > 0));
                try
                {
                    await filteredC.ToListAsync(); // this line specifically causes the issue
                    throw new Exception("we never get here");
                }
                catch (ArgumentException e)
                {
                    Console.WriteLine(e);
                }
            }

            connection.Close();
        }
            

    }
}

public class Caseload
{
    public int Id { get; set; }
    public virtual ICollection<CaseFile> CaseFiles { get; set; }
}

public class CaseFile
{
    public int Id { get; set; }
    public int FileResourceId { get; set; }
}

public class CaseloadDto
{
    public int Id { get; set; }
    public IEnumerable<CaseFileDto> CaseFiles { get; set; }
}

public class CaseFileDto
{
    public int Id { get; set; }
    public int FileResourceId { get; set; }
}

public class DataContext : DbContext
{
    public DataContext(DbContextOptions options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Caseload>();
        modelBuilder.Entity<CaseFile>();
    }
}

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:7 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
mvidacovichcommented, Dec 7, 2018

I fiddled around with this and you are right - EF Core does not like filtering things that are not entities. Attempting to do so results in errors when attempting to eager load (like you showed with the ToList example / my original case). Or it results in a select N+1 scenario with the case I included that did not have the ToList (in the cases of “filteredA.ToList()” and “filteredB.ToList()”)

Having worked on far more EF6 than EF Core I think I took for granted parity between the way queryables worked. I even had to go back and ensure I wasn’t going crazy thinking this worked fine in EF6 (it does, +1 sanity)

Your suggestion to use expression mapping works exactly like I hoped e.g. caseloads.UseAsDataSource(configuration).For<CaseloadDto>(configuration).AsQueryable().Where(x => x.CaseFiles.Any(y => y.FileResourceId > 0))

The above produces reasonable SQL and no errors. The only downside is that it doesn’t support async but that’s really not critical (at least for me).

Thanks for the time, patience, and help 👍

1reaction
lbargaoanucommented, Dec 7, 2018

No, you missed the ToList there.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Entity Framework Queryable async
The problem seems to be that you have misunderstood how async/await work with Entity Framework. About Entity Framework.
Read more >
ASP.NET Core Best Practices
By Mike Rousos. This article provides guidelines for maximizing performance and reliability of ASP.NET Core apps.
Read more >
Client vs. Server Evaluation - EF Core
EF Core throws an exception whenever it comes across constants of a type that can't be mapped using current database provider. Common causes ......
Read more >
Processing Pipelines Series - Reactive Extensions (Rx.NET)
With sequences we can create projections, transformations and filters. We can combine multiple sequences into a single one.
Read more >
Bug - async and uncaught Exceptions
Hello everyone, In one of the projects I'm working on, we're using async and await in order to execute code asynchronously.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found