AgileMapper icon indicating copy to clipboard operation
AgileMapper copied to clipboard

Query Projection int to enum Mapping Issue

Open marcocavanna opened this issue 3 years ago • 3 comments

Hi all!

First of all, congratulations on the excellent work of this tool that I'm starting to love and use every day!

The issue I'm facing out is the following (I'm using EFCore with SQLServer): When using the _myContext.MySet.Where(e => e.Anything == "something").Project().To<MyModel>().ToList() I'm receiving this error:

         - The client projection contains a reference to a constant expression of 'int[]' which is being passed as an argument to the method 'Contains'. This could potentially cause a memory leak; consider assigning this constant to a local variable and using the variable in the query instead. See https://go.microsoft.com/fwlink/?linkid=2103067 for more information.
              at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.ConstantVerifyingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
              at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
              at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
              at System.Linq.Expressions.ExpressionVisitor.VisitConditional(ConditionalExpression node)
              at System.Linq.Expressions.ConditionalExpression.Accept(ExpressionVisitor visitor)
              at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
              at System.Linq.Expressions.ExpressionVisitor.VisitMemberAssignment(MemberAssignment node)
              at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
              at System.Linq.Expressions.ExpressionVisitor.Visit[T](ReadOnlyCollection`1 nodes, Func`2 elementVisitor)
              at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
              [ ... other useless lines of Stack Trace ... ]

The error above won't show if I use the standard non projection api _myContext.MySet.Where(e => e.Anything == "something").ToList().Select(e => Mapper.Map(e).ToNew<MyModel>()).

After some tests on the code I realized that this problem comes from the projection of a value of type int to an enum, but this happen only when using Project().To<Something>() and not Map(anything).ToNew<Something>().

I'll use a "standard" Issue Template to expose my problem:

Prerequisites

  • [x] I am running the latest version
  • [x] I checked the documentation and found no answer
  • [x] I checked to make sure that this issue has not already been filed

Expected Behavior

I would like to be able to use the EFCore query projection feature even when the model defined on the database has a data type of type int that must be mapped to an enum

Current Behavior

An error will occur while using _myContext.MySet.Where(e => e.Anything == "something").Project().To<MyModel>().ToList() when the Model of _myContext.MySet has a property with int type but the MyModel has declared that property with an enum type.

Failure Information (for bugs)

This problem occur only while mapping int to enum. Removing the enum property declaration (or transform type to int instead of enum) everything is working well.

Clarification

The database entities model I'm using are automatically generated using dotate ef db scaffold tool, then I could not change the type of the entity model described in next section at point 1, named MyEntity.

Steps to Reproduce

  1. Create a database entity model like
// Automatically generated from db scaffold
// could not be changed because will be overwritten 
class MyEntity {
  public int Id { get; set; }
  public string Name { get; set; }
  public int Status { get; set; }
}
  1. Create the DTO model (and the enum) like
enum EntityStatus {
  OnHold = 0,
  WorkInProgress = 1,
  Done = 2
}

class MyEntityDto {
  public int Id { get; set; }
  public string Name { get; set; }
  public EntityStatus Status { get; set; }
}
  1. In some repository try to get entities projected to DTO
public IEnumerable<MyEntityDto> GetEntities(string search)
  => _myContext.MyEntitySet.Where(e => e.Name.Contains(search)).Project().To<MyEntityDto>().ToListAsync();

When arriving at code described at point 3, boom, the Exception will be thrown. The issue seems to be an error while building the projection query.


Thanks for your patience reading my long issue description, I hope you arrived here and help me resolving this trouble.

marcocavanna avatar Sep 21 '22 17:09 marcocavanna

Hi!

Thanks for the thorough report! Just to check - what version of EF Core are you using?

All the best,

Steve

SteveWilkes avatar Sep 24 '22 10:09 SteveWilkes

Hi! Currently I'm using one of the latest, but not the last, the 6.0.7.

marcocavanna avatar Sep 24 '22 10:09 marcocavanna

Thanks! The error comes from the projection Expression generated by the mapper including an embedded constant array containing the valid underlying numeric enum values - a Contains() call is performed on the array with the value in the DB column to check if it's a valid enum value.

I'll have to rework the generated Expression to do things differently. Thanks for letting me know!

SteveWilkes avatar Sep 24 '22 11:09 SteveWilkes