Created
September 7, 2018 09:09
-
-
Save joakimriedel/caffa6a544ab3e7a5d22d63273df9d77 to your computer and use it in GitHub Desktop.
EF Core bug with AsQueryable and expression filter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.EntityFrameworkCore; | |
using Microsoft.EntityFrameworkCore.Diagnostics; | |
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel.DataAnnotations; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Threading.Tasks; | |
namespace EfCoreBug2 | |
{ | |
class Program | |
{ | |
static async Task Main(string[] args) | |
{ | |
using (var context = new ClientContext()) | |
{ | |
//context.Database.EnsureDeleted(); | |
context.Database.EnsureCreated(); | |
var query1 = context.Offers | |
.Select(dtoOffer => new OfferDto | |
{ | |
AcceptedActions = dtoOffer.Actions | |
.Where(a => a.Action == OfferActions.Accepted) | |
.OrderBy(a => a.TimeCreatedUtc) | |
.Take(1) | |
.Select(dtoOfferAction => new OfferActionDto() | |
{ | |
CompanyName = dtoOfferAction.Company.Name | |
}) | |
.ToList() | |
} | |
); | |
var query2 = context.Offers | |
.Select(dtoOffer => new OfferDto | |
{ | |
AcceptedActions = dtoOffer.Actions | |
.AsQueryable() | |
.Where(OfferAction.Accepted) | |
.OrderBy(a => a.TimeCreatedUtc) | |
.Take(1) | |
.Select(dtoOfferAction => new OfferActionDto() | |
{ | |
CompanyName = dtoOfferAction.Company.Name | |
}) | |
.ToList() | |
} | |
); | |
// THIS QUERY WORKS (WITHOUT ASQUERYABLE) | |
await query1.ToListAsync(); | |
// THIS QUERY DOES NOT WORK IN SQL PROVIDER | |
// SQL: Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning: The LINQ expression 'join Company a.Company in value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[EfCoreBug2.Program+Company]) on Property([a], "CompanyId") equals Property([a.Company], "Id")' could not be translated and will be evaluated locally.'. This exception can be suppressed or logged by passing event ID 'RelationalEventId.QueryClientEvaluationWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'. | |
// BUT WORKS WITH INMEMORY | |
await query2.ToListAsync(); | |
} | |
} | |
public class Company | |
{ | |
public int Id { get; set; } | |
[Required] | |
public string Name { get; set; } | |
// Navigation properties | |
public ICollection<Offer> Offers { get; set; } | |
public ICollection<OfferAction> OfferActions { get; set; } | |
} | |
public class Offer | |
{ | |
public int Id { get; set; } | |
[Required] | |
public Company Company { get; set; } | |
public int CompanyId { get; set; } | |
// navigation properties | |
public ICollection<OfferAction> Actions { get; set; } | |
} | |
public enum OfferActions : int | |
{ | |
Accepted = 1 | |
} | |
public class OfferAction | |
{ | |
public static Expression<Func<OfferAction, bool>> Accepted => a => a.Action == OfferActions.Accepted; | |
public int Id { get; set; } | |
[Required] | |
public Offer Offer { get; set; } | |
public int OfferId { get; set; } | |
[Required] | |
public Company Company { get; set; } | |
public int CompanyId { get; set; } | |
[Required] | |
public DateTime TimeCreatedUtc { get; set; } | |
[Required] | |
public OfferActions Action { get; set; } | |
} | |
public class OfferDto | |
{ | |
public int Id { get; set; } | |
public ICollection<OfferActionDto> AcceptedActions { get; set; } | |
} | |
public class OfferActionDto | |
{ | |
public string CompanyName { get; set; } | |
} | |
class ClientContext : DbContext | |
{ | |
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) | |
{ | |
// TO USE SQL | |
optionsBuilder | |
.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=EfCoreBug2;Trusted_Connection=True;MultipleActiveResultSets=true;Connect Timeout=30") | |
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); | |
// TO USE INMEMORY | |
//optionsBuilder | |
// .UseInMemoryDatabase(Guid.NewGuid().ToString()) | |
// .ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); | |
} | |
protected override void OnModelCreating(ModelBuilder builder) | |
{ | |
base.OnModelCreating(builder); | |
builder.Entity<Company>().HasData(new Company | |
{ | |
Id = 1, | |
Name = "Seller" | |
}); | |
builder.Entity<Company>().HasData(new Company | |
{ | |
Id = 2, | |
Name = "Buyer" | |
}); | |
builder.Entity<Offer>().HasData(new Offer | |
{ | |
Id = 1, | |
CompanyId = 2 | |
}); | |
builder.Entity<OfferAction>() | |
.HasOne(a => a.Company) | |
.WithMany(bg => bg.OfferActions) | |
.HasForeignKey(a => a.CompanyId) | |
.OnDelete(DeleteBehavior.Restrict); | |
builder.Entity<OfferAction>().HasData(new OfferAction() | |
{ | |
Id = 1, | |
OfferId = 1, | |
CompanyId = 2, | |
Action = Program.OfferActions.Accepted, | |
TimeCreatedUtc = DateTime.UtcNow | |
}); | |
} | |
public DbSet<Offer> Offers { get; set; } | |
public DbSet<OfferAction> OfferActions { get; set; } | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<OutputType>Exe</OutputType> | |
<TargetFramework>netcoreapp2.1</TargetFramework> | |
<LangVersion>latest</LangVersion> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.2" /> | |
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="2.1.2" /> | |
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.2" /> | |
</ItemGroup> | |
</Project> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment