Last active
August 10, 2021 23:38
-
-
Save joakimriedel/f716b518572117f64aeb31eaa39ea850 to your computer and use it in GitHub Desktop.
EF Core bug with null check in nested projection
This file contains 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 EfCoreBug | |
{ | |
class Program | |
{ | |
static async Task Main(string[] args) | |
{ | |
using (var context = new ClientContext()) | |
{ | |
//context.Database.EnsureDeleted(); | |
context.Database.EnsureCreated(); | |
var query1 = context.Ads.Select(dtoAd => new AdDto() | |
{ | |
Offers = dtoAd.Offers.Select(dtoOffer => new | |
{ | |
__LastAction = dtoOffer.Actions.OrderByDescending(a => a.TimeCreatedUtc).FirstOrDefault(), | |
Id = dtoOffer.Id | |
}) | |
.Select(dtoLet => new OfferDto() | |
{ | |
Id = dtoLet.Id, | |
LastAction = dtoLet.__LastAction == null ? null : new OfferActionDto() | |
{ | |
Action = dtoLet.__LastAction.Action, | |
TimeCreatedUtc = dtoLet.__LastAction.TimeCreatedUtc, | |
AnyEstablished = dtoLet.__LastAction.Offer.Actions.Any(a => a.Action == OfferActions.Established) | |
} | |
}) | |
.ToList() | |
}); | |
var query2 = context.Offers.Select(dtoOffer => new | |
{ | |
__LastAction = dtoOffer.Actions.OrderByDescending(a => a.TimeCreatedUtc).FirstOrDefault(), | |
Id = dtoOffer.Id | |
}) | |
.Select(dtoLet => new OfferDto() | |
{ | |
Id = dtoLet.Id, | |
LastAction = dtoLet.__LastAction == null ? null : new OfferActionDto() | |
{ | |
Action = dtoLet.__LastAction.Action, | |
TimeCreatedUtc = dtoLet.__LastAction.TimeCreatedUtc, | |
// IF THIS LINE IS COMMENTED OUT, QUERY WILL WORK IN BOTH SQL AND INMEMORY | |
AnyEstablished = dtoLet.__LastAction.Offer.Actions.Any(a => a.Action == OfferActions.Established) | |
} | |
}); | |
// INMEMORY: System.ArgumentNullException: 'Value cannot be null.' | |
// SQL (without QueryClientEvaluationWarning): System.InvalidOperationException: 'No mapping to a relational type can be found for the CLR type 'object'.' | |
// SQL (with QueryClientEvaluationWarning): System.InvalidOperationException: 'Error generated for warning 'Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning: The LINQ expression 'where (Convert([a].Action, Int32) == 1)' could not be translated and will be evaluated locally.'. | |
//await query1.ToListAsync(); | |
// INMEMORY: System.ArgumentNullException: 'Value cannot be null.' | |
// SQL (without QueryClientEvaluationWarning): System.ArgumentNullException: 'Value cannot be null.' | |
// SQL (with QueryClientEvaluationWarning): System.InvalidOperationException: 'Error generated for warning 'Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning: The LINQ expression 'where (Convert([a].Action, Int32) == 1)' could not be translated and will be evaluated locally.'. | |
await query2.ToListAsync(); | |
} | |
} | |
public class Ad | |
{ | |
public int Id { get; set; } | |
// navigation properties | |
public ICollection<Offer> Offers { get; set; } | |
} | |
public class AdDto | |
{ | |
public ICollection<OfferDto> Offers { get; set; } | |
} | |
public class Offer | |
{ | |
public int Id { get; set; } | |
[Required] | |
public Ad Ad { get; set; } | |
public int AdId { get; set; } | |
// navigation properties | |
public ICollection<OfferAction> Actions { get; set; } | |
} | |
public enum OfferActions : int | |
{ | |
Established = 1 | |
} | |
public class OfferAction | |
{ | |
public int Id { get; set; } | |
[Required] | |
public Offer Offer { get; set; } | |
public int OfferId { get; set; } | |
[Required] | |
public DateTime TimeCreatedUtc { get; set; } | |
[Required] | |
public OfferActions Action { get; set; } | |
} | |
public class OfferDto | |
{ | |
public int Id { get; set; } | |
public OfferActionDto LastAction { get; set; } | |
} | |
public class OfferActionDto | |
{ | |
public DateTime TimeCreatedUtc { get; set; } | |
public OfferActions Action { get; set; } | |
public bool AnyEstablished { get; set; } | |
} | |
class ClientContext : DbContext | |
{ | |
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) | |
{ | |
// TO USE SQL | |
optionsBuilder | |
.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=EfCoreBug;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<Ad>().HasData(new Ad | |
{ | |
Id = 1 | |
}); | |
builder.Entity<Offer>().HasData(new Offer | |
{ | |
Id = 1, | |
AdId = 1 | |
}); | |
builder.Entity<OfferAction>().HasData(new OfferAction() | |
{ | |
Id = 1, | |
OfferId = 1, | |
Action = Program.OfferActions.Established, | |
TimeCreatedUtc = DateTime.UtcNow | |
}); | |
} | |
public DbSet<Ad> Ads { get; set; } | |
public DbSet<Offer> Offers { get; set; } | |
public DbSet<OfferAction> OfferActions { get; set; } | |
} | |
} | |
} |
This file contains 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="AutoMapper" Version="7.0.1" /> | |
<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
Hello Joakim :D, I found you here, could you tell me what you did to fix this?
Do you remember if you upgrade .net core version, entity framework version or another thing?
Since that I have a lot of projects to make the changes, I want to know which is the best way I should go.
I'm working on it for a long long time and I found only you with the same problem as me.
Thanks so much :D