Probably related to https://github.com/aspnet/EntityFrameworkCore/issues/16597 but I am opening because it is marked as fixed for 3.0.0
When performing complex operations, sometimes .Any(x)
throws an InvalidOperationException
with this message :
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Linq.Expressions.dll
When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this type.
Here is a repro. The actual query is meaningless (it does things in a very convoluted way) but it is a recreation of a problem I actually got in a real project, albeit with much more complex objects.
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Debug;
using System.Collections.Generic;
using System.Linq;
namespace _18179Repro
{
class Program
{
static void Main(string[] args)
{
using var context = new MyContext();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
context.Customer.Add(new Customer() { });
context.SaveChanges();
context.Invoice.Add(new Invoice { CustomerFk = 1 });
context.SaveChanges();
context.InvoiceLine.Add(new InvoiceLine { InvoiceFk = 1 });
context.SaveChanges();
var invoice = context.Invoice
.Include(i => i.InvoiceLine)
.First();
var customers = context.Customer
.Select(c => new
{
c.Id,
HasInvoiceLines = invoice.InvoiceLine.Any(il => il.InvoiceFk == 1)
})
.ToList();
}
}
public partial class MyContext : DbContext
{
public virtual DbSet<Customer> Customer { get; set; }
public virtual DbSet<Invoice> Invoice { get; set; }
public virtual DbSet<InvoiceLine> InvoiceLine { get; set; }
private static readonly LoggerFactory Logger = new LoggerFactory(new[] { new DebugLoggerProvider() });
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string connectionString = "Server=.;Database=Repro18179;Trusted_Connection=True;MultipleActiveResultSets=true";
optionsBuilder.UseSqlServer(connectionString)
.EnableSensitiveDataLogging()
.UseLoggerFactory(Logger);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>(entity =>
{
entity.Property(e => e.Id).HasColumnName("id");
});
modelBuilder.Entity<Invoice>(entity =>
{
entity.HasIndex(e => e.CustomerFk);
entity.Property(e => e.Id).HasColumnName("id");
entity.Property(e => e.CustomerFk).HasColumnName("customer_fk");
entity.HasOne(d => d.CustomerFkNavigation)
.WithMany(p => p.Invoice)
.HasForeignKey(d => d.CustomerFk)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_Invoice_Customer");
});
modelBuilder.Entity<InvoiceLine>(entity =>
{
entity.Property(e => e.Id).HasColumnName("id");
entity.Property(e => e.InvoiceFk).HasColumnName("invoice_fk");
entity.HasOne(d => d.InvoiceFkNavigation)
.WithMany(p => p.InvoiceLine)
.HasForeignKey(d => d.InvoiceFk)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_InvoiceLine_Invoice");
});
}
}
public partial class Customer
{
public Customer()
{
Invoice = new HashSet<Invoice>();
}
public int Id { get; set; }
public virtual ICollection<Invoice> Invoice { get; set; }
}
public partial class Invoice
{
public Invoice()
{
InvoiceLine = new HashSet<InvoiceLine>();
}
public int Id { get; set; }
public int CustomerFk { get; set; }
public virtual Customer CustomerFkNavigation { get; set; }
public virtual ICollection<InvoiceLine> InvoiceLine { get; set; }
}
public partial class InvoiceLine
{
public int Id { get; set; }
public int InvoiceFk { get; set; }
public virtual Invoice InvoiceFkNavigation { get; set; }
}
}
Working around this is fortunately trivial : move any query on preloaded objects outside of the main query, like this :
bool hasInvoiceLines = invoice.InvoiceLine.Any(il => il.InvoiceFk == 1);
var customers = context.Customer
.Select(c => new
{
c.Id,
HasInvoiceLines = hasInvoiceLines
})
.ToList();
Feel free to rename that issue with a more meaningful title if necessary.
EF Core version: 3.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET Core 3.0
Operating system: Windows 10 x64
IDE: Visual Studio 2019 16.3.1
++
@fschlaef maybe for now, you can change as below that.
class Program
{
static void Main(string[] args)
{
using var context = new MyContext();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
context.Customer.Add(new Customer() { });
context.SaveChanges();
context.Invoice.Add(new Invoice { CustomerFk = 1 });
context.SaveChanges();
context.InvoiceLine.Add(new InvoiceLine { InvoiceFk = 1 });
context.SaveChanges();
IQueryable<InvoiceLine> invoiceLines = context.InvoiceLine;
var customers = context.Customer
.Select(c => new
{
c.Id,
HasInvoiceLines = invoiceLines.Any(il => il.InvoiceFk == 1)
})
.ToList();
}
}
Note from triage: Since invoiceLines
is captured from outside the query, the entire invoiceLines.Any(il => il.InvoiceFk == 1)
should be evaluated by the funcletizer.
I get a similar exception when running in production SQL Server
``c#
System.InvalidOperationException
HResult=0x80131509
Message=When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this type.
Source=System.Linq.Expressions
StackTrace:
at System.Linq.Expressions.ExpressionVisitor.VisitAndConvert[T](T node, String callerName)
at System.Dynamic.Utils.ExpressionVisitorUtils.VisitParameters(ExpressionVisitor visitor, IParameterProvider nodes, String callerName)
at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression
1 node)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Translate(SelectExpression selectExpression, Expression expression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutorTResult
at Microsoft.EntityFrameworkCore.Storage.Database.CompileQueryTResult
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCoreTResult
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass12_01.<ExecuteAsync>b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func
1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryTResult
But when I wrote a unit test using In Memory db provider, I get a different exception.
```c#
System.InvalidOperationException
HResult=0x80131509
Message=The LINQ expression '(Unhandled parameter: o)' 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.
Source=Microsoft.EntityFrameworkCore.InMemory
StackTrace:
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryExpressionTranslatingExpressionVisitor.VisitParameter(ParameterExpression parameterExpression)
at System.Linq.Expressions.ParameterExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryExpressionTranslatingExpressionVisitor.VisitMember(MemberExpression memberExpression)
at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryExpressionTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryExpressionTranslatingExpressionVisitor.Translate(Expression expression)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Visit(Expression expression)
at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Visit(Expression expression)
at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Visit(Expression expression)
at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Visit(Expression expression)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryProjectionBindingExpressionVisitor.Translate(InMemoryQueryExpression queryExpression, Expression expression)
at Microsoft.EntityFrameworkCore.InMemory.Query.Internal.InMemoryQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass12_0`1.<ExecuteAsync>b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.<ToListAsync>d__64`1.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
I passed the test by calling ToListAsync
before the Select
, which also fixed the exception when using SQL Server.
Here's a minimal reproducible example. I use Single
on a collection of named tuples in my actual code which is affected by this issue. Also, I think the exception stack trace this example produces is different from the 2nd exception details I posted.
```c#
[Fact]
public async Task Test()
{
var options = new DbContextOptionsBuilder()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
using var ctx = new Context(options);
var tuples = new[] { (a: "a", b: "b") };
await ctx.Foo
.Select(f => new
{
tuples.Single(o => true).a
})
.ToListAsync();
}
public class Context : DbContext
{
public Context(DbContextOptions options) : base(options) { }
public DbSet<Foo> Foo { get; set; }
}
public class Foo
{
public int FooId { get; set; }
}
```
I also get the same exception when using Automapper.ProjectTo to map Entity to DTO.
```
public class ListProductUnitQuantityMapsDto
{
public ListProductUnitQuantityMapsDto(int id,
int companyId,
string name,
string description,
int productId,
int unitId,
decimal minUnitQuantity,
decimal maxUnitQuantity,
decimal defaultUnitPrice,
decimal defaultDiscountPercentage,
decimal defaultDiscountPrice,
bool isActive,
bool isDelete,
DateTime createdAt,
string createdBy,
DateTime? lastUpdatedAt,
string lastUpdatedBy//,
//string company,
//string product,
//string unit
)
{
Id = id;
CompanyId = companyId;
Name = name;
Description = description;
ProductId = productId;
UnitId = unitId;
MinUnitQuantity = minUnitQuantity;
MaxUnitQuantity = maxUnitQuantity;
DefaultUnitPrice = defaultUnitPrice;
DefaultDiscountPercentage = defaultDiscountPercentage;
DefaultDiscountPrice = defaultDiscountPrice;
IsActive = isActive;
IsDelete = isDelete;
CreatedAt = createdAt;
CreatedBy = createdBy;
LastUpdatedAt = lastUpdatedAt;
LastUpdatedBy = lastUpdatedBy;
//Company = company;
//Product = product;
//Unit = unit;
}
public int Id { get; private set; }
public int CompanyId { get; private set; }
public string Name { get; private set; }
public string Description { get; private set; }
public int ProductId { get; private set; }
public int UnitId { get; private set; }
public decimal MinUnitQuantity { get; private set; }
public decimal MaxUnitQuantity { get; private set; }
public decimal DefaultUnitPrice { get; private set; }
public decimal DefaultDiscountPercentage { get; private set; }
public decimal DefaultDiscountPrice { get; private set; }
public bool IsActive { get; private set; }
public bool IsDelete { get; private set; }
public DateTime CreatedAt { get; private set; }
public string CreatedBy { get; private set; }
public DateTime? LastUpdatedAt { get; private set; }
public string LastUpdatedBy { get; private set; }
public string Company { get; private set; }
public string Product { get; private set; }
public string Unit { get; private set; }
}
Here I'm getting exception.
public async Task
{
return await Mapper.ProjectTo
.ToListAsync();
}
`````
@vinu-arora Thanks for posting here. I have the same issue. After some trial and error I realized that the issue arises not during the query, but when telling automapper to project to the DTO entity.
@ajcvickers As we get this issue using automapper: Is it good to report it here or should we directly report to automapper?
@soomon This is the correct place. EF is not correctly interpreting the projection that Automapper is doing.
I have the same issue using SelectSingle
with projection in the following manner (no automapper involved). Database-first and scaffolded models.. tested efcore 3.0 and 3.1.1
namespace MyProject.Api.DTOs
{
public class ItemDTO : DTO
{
public int Id { get; set; }
public string ItemText { get; set; }
public ItemTypeDTO ItemType { get; set; }
public static readonly Expression<Func<Items, ItemDTO>> Projection = x => new ItemDTO
{
Id = x.Id,
ItemText = x.SomeText,
ItemType = x.ItemTypes.SelectSingle(ItemTypeDTO.Projection),
};
}
}
I'm having the same issue with AutoMapper's ProjectTo. Are there any workarounds for this?
I also have the same issue, trying to get a value from inherted class, and trying to query something like:
var query = _context.Users.Select(c => new DtoUser {
Id = c.Id,
DirectionName = c is UserA ? (c as UserA).Directions.Name : null
});
UPDATE: It's seems to be working just by changing (c as UserA) to ((UserA)c):
var query = _context.Users.Select(c => new DtoUser {
Id = c.Id,
DirectionName = c is UserA ? ((UserA)c).Directions.Name : null
});
Is there any update on when this will be fixed?
This is a major issue for us. It's affecting too many places in our app to be able to work around all of them.
We are stuck on .Net Core 2.2 and cannot upgrade because of this issue. With 2.2 not being supported at all now, MS has also removed the tags from docker hub for the .Net Core 2.2 container images that we use to create our builds. The 2.2 docker tag references are still working at the moment so we are still able to build, but it feels like a ticking time bomb. If those images are removed from the docker hub (or from wherever they are currently being cached), we are completely screwed!
@jcachat We will look into whether we can patch this.
/cc @smitpatel
The exception message comes while evaluating a lambda expression. Any lambda which are being passed to queryable methods will throw client eval exception. If you have any other kind of lambda then it is client eval and it may not work in all cases.
In very specific case in OP. the lambda expression is being applied on client code and can be fully extracted out in a variable outside of the query. In future release, EF core would do that automatically in parameter extraction.
Above scenario cannot be patched because it touches every query path (even cached ones). The work-around is really easy (which EF Core would do under the hood anyway).
From query in OP
```C#
var invoice = context.Invoice
.Include(i => i.InvoiceLine)
.First();
var hasInvoceLines = invoice.InvoiceLine.Any(il => il.InvoiceFk == 1); //since this is not correlated to customer, better to pre-evaluate this.
var customers = context.Customer
.Select(c => new
{
c.Id,
HasInvoiceLines = hasInvoceLines
})
.ToList();
```
If your issue is not same as OP then file a new issue with repro code. Then we can separately evaluating about patching.
Thanks for the explanation. Your comments about lambdas may explain my problems. I knew I had some of those to clean up (I've been tracking the warnings from EF 2.2) but I didn't realize that's what was causing this error. Sorry, I should have dug in to it more. If it's possible to catch that specific error and throw a more meaningful error, that might help people understand what's going on.
If I have any issues after getting those worked out, I'll create a new issue.
Following up on @smitpatel's comments, a similar exception message can be thrown from many scenarios. Therefore, if you think you are running into this bug, then please be sure it is really the same scenario. If in doubt, file a new issue with full code to reproduce what you are seeing and we will investigate.
We get the same exception on a very large project we are porting from .NET Framework 4.7 to .NET Core.
We are using EF Core, Automapper, and OData,
Here is a code sample:
//[GET] URL = /Data?$orderby=Name&$expand=CultureData&$count=true
public class DataController : BaseApiController
{
private Service Service { get; }
public ODataQuerySettings DefaultODataPagingSettings = new ODataQuerySettings() { PageSize = 50 };
public DataController(Service service)
{
Service = service;
}
// Doesn't work
[HttpGet, Route("")]
public ViewModel Get(ODataQueryOptions<MyEntityDTO> options)
{
var data = options.ApplyTo(Service.Data, DefaultODataPagingSettings);
var features = Request.ODataFeature();
return new ViewModel(data, features.NextLink, features.TotalCount);
}
// Doesn't work either
[HttpGet("QueryableGet"), EnableQuery]
public IQueryable<MyEntityDTO> QueryableGet(ODataQueryOptions<MyEntityDTO> options)
{
return Service.Data;
}
}
public class Service : IService
{
public Service(IUnitOfWork unitOfWork, IMapper mapper)
{
UnitOfWork = unitOfWork;
Mapper = mapper;
}
private IUnitOfWork UnitOfWork { get; }
private IMapper Mapper { get; }
// UnitOfWork.QueryablesRepository.MyEntities type is IQueryable<MyEntity>
public IQueryable<MyEntityDTO> Data => UnitOfWork.QueryablesRepository.MyEntities.ProjectTo<MyEntityDTO>(Mapper.ConfigurationProvider);
}
// Automapper Profile
public class GeneralProfile : Profile
{
protected void Configure()
{
CreateMap<Translation, TranslationDTO>()
.ForMember(dest => dest.LanguageName, opt => opt.MapFrom(o => o.Language.Name))
.ForMember(dest => dest.LanguageParentId, opt => opt.MapFrom(o => o.Language.ParentId))
.ReverseMap()
.ForMember(dest => dest.Language, opt => opt.Ignore());
CreateMap<MyEntity, MyEntityDTO>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(o => o.Translations.Where(t => t.LanguageId == LanguageId || t.LanguageId == SecondaryLanguageId).OrderByDescending(t => t.LanguageId == LanguageId).Select(t => t.Name).FirstOrDefault()))
.ForMember(dest => dest.CultureData, opt =>
{
if (IsFullAccess)
opt.MapFrom(o => o.Translations.OrderByDescending(t => t.LanguageId == LanguageId));
else
opt.Ignore();
})
.ReverseMap()
.ForMember(dest => dest.Translations, opt => opt.MapFrom(o => o.CultureData));
}
}
```csharp
// ViewModels
public class MyEntityDTO
{
public long Id { get; set; }
public string Name { get; set; }
public IEnumerable<TranslationDTO> CultureData { get; set; }
}
public class TranslationDTO
{
public string Name { get; set; }
public long LanguageId { get; set; }
public long? LanguageParentId { get; set; }
public string LanguageName { get; set; }
}
```csharp
// Entities
public partial class MyEntity
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime CreatedUtc { get; set; }
public DateTime? UpdatedUtc { get; set; }
public virtual ICollection<Child> Children { get; set; }
public virtual ICollection<Translation> Translations { get; set; }
}
public partial class Translation
{
public long Id { get; set; }
public long LanguageId { get; set; }
public string Name { get; set; }
public virtual Language Language { get; set; }
public virtual MyEntity MyEntities { get; set; }
}
We have been working on this projects for months but then we hit this issue and we are unable to test and release it.
The problem is that we have no control on the linq query being produced by OData, we also tried removing OData and using a direct Linq "UnitOfWork.QueryablesRepository.MyEntities.Select( new MyEntityDTO { ... })" we got the same error.
Would appreciate any fix or workaround.
I used to work with EF Core 2.2.6 and didn't have such an issue, but with EF Core 3.1.1 I do.
I didn't why it wasn't a problem before, and now it is,
The following code doesn't throw an error:
var data = Context.Projects.Where(entity => entity.CultureCode.Contains("fr")).Select(entity => Mapper.Map<Project, ProjectDTO>(entity)).FirstOrDefault();
But in the other hand, this does:
var data = Context.Projects.Where(entity => entity.CultureCode.Contains("fr")).Select(entity => Mapper.Map<Project, ProjectDTO>(entity, opt => opt.Items.Add("key", "value"))).FirstOrDefault();
The work around for me was:
var filteredData = Context.Projects.Where(entity => entity.CultureCode.Contains("fr")).ToList();
var data = filteredData.Select(entity => Mapper.Map<Project, ProjectDTO>(entity, opt => opt.Items.Add("key", "value"))).FirstOrDefault();
Why this is happening now and not before?
I faced the same issue.
for example this query is okay:
db.Products
.Where(...)
.Select(x => new ProductDto(x)) // ✔️ it's okay here
.FirstOrDefaultAsync();
but if I initialize in constructor like:
.Select(x => new ProductDto(x) { Price = 10 }) // ❌ not okay because of `{ Price = 10 }`
so my solution is adding another constructor:
public class ProductDto
{
public ProductDto() {} // empty
public ProductDto(Product p) { ... } // with 1 parameter
public ProductDto(Product p, double price) : this(p) { Price = price } // ⬅️ add this, issue gone
}
I figured out the problem in my case, the sample I provided was missing the part which caused the error, I have updated my previous comment with the part which caused the error.
it is this part in my AutoMapper profile:
.ForMember(dest => dest.CultureData, opt =>
{
if (IsFullAccess)
opt.MapFrom(o => o.Translations.OrderByDescending(t => t.LanguageId == LanguageId));
else
opt.Ignore();
})
I changed to using conditional mapping instead:
.ForMember(dest => dest.CultureData, opt => opt.Condition(o => IsFullAccess))
.ForMember(dest => dest.CultureData, opt => opt.MapFrom(o => o.Translations).OrderByDescending(t => t.LanguageId == LanguageId))
I also changed the DTOs to use (IEnumrable) instead of (ICollection or List) to fix another issue with OData.
Now the projection works just fine, I think the "if ... else" condition triggered client evaluation may be? or EF Core was not able to translate it.
Hope this helps others having the same issue.
I encountered the same issue when using AutoMapper. In my case, the problem was caused by the fact that the DTO class have multiple constructors and AutoMapper trying to use the incorrect one. I forced the use of the default constructor and the issue was fixed:
CreateMap<City, CityViewModel>()
.ConstructUsing(x => new CityViewModel()) // To force the use of the default constructor, otherwise get an error
I would hope a proposal to "punt" to .NET 5.0 will be refused, given that this issue occurs in .NET Core 3.1 (EFCore 3.1.6 as of writing), which has a Long Term Support policy.
I would hope a proposal to "punt" to .NET 5.0 will be refused, given that this issue occurs in .NET Core 3.1 (EFCore 3.1.6 as of writing), which has a Long Term Support policy.
Unfortunately I have learned that LTS in EFCore's case actually means "if you really want that one fix you need to upgrade to the next major version that has another 50 critical bugs that we will also never fix before the following major revision 2 years from now". That is true from every single version from 1.0 to now.
Resolving this issue will only update the exception message thrown. The query is going to throw exception either way as LambdaExpression cannot be translated to SQL.
Hello,
Can we hope for this issue before November 2021?
Most helpful comment
Unfortunately I have learned that LTS in EFCore's case actually means "if you really want that one fix you need to upgrade to the next major version that has another 50 critical bugs that we will also never fix before the following major revision 2 years from now". That is true from every single version from 1.0 to now.