Skip to content

Instantly share code, notes, and snippets.

@joakimriedel
Last active March 24, 2022 09:41
Show Gist options
  • Save joakimriedel/da8267bc9b461ef1701332d4c2a08912 to your computer and use it in GitHub Desktop.
Save joakimriedel/da8267bc9b461ef1701332d4c2a08912 to your computer and use it in GitHub Desktop.
TPH Concat bug with EF Core 6
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace EfCoreBug
{
class Program
{
public static readonly ILoggerFactory MyLoggerFactory
= LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
static async Task Main(string[] args)
{
using (var context = new ClientContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
/*
Expression of type 'System.Linq.IQueryable`1[EfCoreBug.Program+Cat]' cannot be used for parameter of type 'System.Linq.IQueryable`1[EfCoreBug.Program+Dog]'
of method 'System.Linq.IQueryable`1[EfCoreBug.Program+Dog] Concat[Dog](System.Linq.IQueryable`1[EfCoreBug.Program+Dog], System.Collections.Generic.IEnumerable`1[EfCoreBug.Program+Dog])'
*/
// var userCatsAndAllDogs = await context.Users
// .Where(u => u.Id == 1)
// .SelectMany(m => m.Animals.OfType<Cat>())
// .Cast<Animal>()
// .Concat(
// context.Animals.OfType<Dog>()
// )
// .ToListAsync();
// var userCatsAndAllDogs = await context.Users
// .Where(u => u.Id == 1)
// .SelectMany(m => m.Animals.OfType<Cat>())
// .Cast<Animal>()
// .Concat(
// context.Animals.OfType<Dog>().Cast<Animal>()
// )
// .ToListAsync();
// var userCatsAndAllDogs = await context.Users
// .Where(u => u.Id == 1)
// .SelectMany(m => m.Animals.OfType<Cat>().Cast<Animal>())
// .Concat(
// context.Animals.OfType<Dog>().Cast<Animal>()
// )
// .ToListAsync();
// var userCatsAndAllDogs = await context.Users
// .Where(u => u.Id == 1)
// .SelectMany(m => m.Animals.OfType<Cat>().OfType<Animal>())
// .Concat(
// context.Animals.OfType<Dog>().OfType<Animal>()
// )
// .ToListAsync();
/*
Unable to translate set operation after client projection has been applied. Consider moving the set operation before the last 'Select' call.
*/
// var userCatsAndAllDogs = await context.Users
// .Where(u => u.Id == 1)
// .SelectMany(m => m.Animals.OfType<Cat>().Select(c => c as Animal))
// .Concat(
// context.Animals.OfType<Dog>().Select(d => d as Animal)
// )
// .ToListAsync();
// workaround
var userCatsAndAllDogs = await context.Users
.Where(u => u.Id == 1)
.SelectMany(m => m.Animals.Where(a => EF.Property<string>(a, "Discriminator") == "Cat"))
.Concat(
context.Animals.Where(a => EF.Property<string>(a, "Discriminator") == "Dog")
)
.ToListAsync();
// 3
Console.WriteLine(userCatsAndAllDogs.Count);
}
}
public class User
{
public int Id { get; set; }
public ICollection<Animal> Animals { get; set; }
}
public abstract class Animal
{
public int Id { get; set; }
public User User { get; set; }
public int UserId { get; set; }
}
public class Cat : Animal
{
public int NumberOfMiceKilled {get;set;}
}
public class Dog : Animal
{
public bool IsShoeChewer {get;set;}
}
class ClientContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// TO USE SQL
optionsBuilder
.UseLoggerFactory(MyLoggerFactory)
.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=TPHConcat;Trusted_Connection=True;MultipleActiveResultSets=true;Connect Timeout=30")
.EnableSensitiveDataLogging(true);
// TO USE INMEMORY
//optionsBuilder
// .UseLoggerFactory(MyLoggerFactory)
// .UseInMemoryDatabase(Guid.NewGuid().ToString());
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<User>().HasData(new User()
{
Id = 1,
});
builder.Entity<Cat>().HasData(new Cat()
{
Id = 1,
UserId = 1,
NumberOfMiceKilled = 3
});
builder.Entity<Dog>().HasData(new Dog()
{
Id = 2,
UserId = 1,
IsShoeChewer = false
});
builder.Entity<User>().HasData(new User()
{
Id = 2,
});
builder.Entity<Dog>().HasData(new Dog()
{
Id = 3,
UserId = 2,
IsShoeChewer = true
});
}
public DbSet<User> Users { get; set; }
public DbSet<Animal> Animals { get; set; }
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.3" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
</ItemGroup>
</Project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment