AunAun
AunAun

Reputation: 1453

AutoMapper IQueryable Extension throws "Cannot compare elements of type <Complex Type>" with

AutoMapper IQueryable Extension's Project().To<TViewModel>().SingleOrDefault() throws this exception:

Cannot compare elements of type 'App.Domain.MyComplexType. Only primitive types, enumeration types and entity types are supported.

I have this model:

public class MyEntityType  // this is an entity type on the dbContext
{
   public int Id {get;set;
   public MyComplexType MyComplexType {get;set;}
}

public class MyComplexType // this is a complex type
{
    public decimal Property1 { get; set;}
    public string Property2 { get;set;}
}

public class ViewModel
{
public int Id { get;set;}
public decimal MyComplexTypeProperty1 { get;set;}
}

I use AutoMapper to configure mapping from IQueryable<MyEntityType> to ViewModel:

Mapper.CreateMap<MyEntityType, MyComplexType>(); // I rely on AutoMapper's 
//convention for flattening `source.MyComplexType.Property1` to `dest.MyComplexTypeProperty1'

Then I try to retrieve a single item like this:

var myItem = myContext.Where(x => x.Id == id).Project().To<ViewModel>().SingleOrDefault();

I get the above exception when SingleOrDefault() is called, so apparently

I currently work around this by first calling SingleOrDefault() and then doing the mapping, this works:

var myItem = Mapper.Map<ViewModel>(myContext.Find(id));

Other posts basically say that the error above arises when trying to compare a EF Complex Type with null, as, e.g., in a Where clause, but that is apparently not the case here.

Upvotes: 8

Views: 1713

Answers (3)

Adnan
Adnan

Reputation: 966

I was having this issue with EF 8 since it allowed using ComplexTypes again.

The problem here is that ComplexTypes in .Net 8 are not supposed to be null, so the solution is to avoid null value.

Also there was change in AutoMapper Api, I needed to call Internal() to access ForAllPropertyMaps().

So the solutions to this problem in recent version, based on this answer, is:

using AutoMapper.Internal;

class MapProfile : Profile
{
  public MapProfile()
  {
    // Per Map
    CreateMap<MyEntityType, MyComplexType>()
      .ForMember(p => p.MyComplexTypeProperty1, p => p.DoNotAllowNull());

    // Or for all maps (don't forget the using above)
    this.Internal().ForAllPropertyMaps(p =>
        p.SourceType == typeof(MyComplexType) ||
        p.SourceType == typeof(AnotherComplexType)
        ,
        (p, q) => { 
          // Null Complex Types are not supported in .Net 8, so don't allow it.
          q.DoNotAllowNull();
        }
    );
  }
}

Upvotes: 1

Omid.Hanjani
Omid.Hanjani

Reputation: 1522

make your prop Nullable

public decimal? MyComplexTypeProperty1 { get;set; }

then use this mapping

Mapper.CreateMap<MyEntityType, MyComplexType>()
    .ForMember(p => p.MyComplexTypeProperty1, p => p.AllowNull())

and if you want solve problems for all complex types, then you can use this code in creating MapperConfiguration

var config = new MapperConfiguration(cfg =>
{
    cfg.ForAllPropertyMaps(p =>
        p.SourceType == typeof(MyComplexType) ||
        p.SourceType == typeof(AnotherComplexType) // || ...

        //NOTE: if you have another ComplexTypes so remove prev lines
        //      and use this line to handle all of them
        //p.SourceType.GetCustomAttributes(typeof(ComplexType))

        (p, q) => { q.AllowNull(); }
    );

    //other configs
});

Upvotes: 2

Tomas Dolezal
Tomas Dolezal

Reputation: 1990

LINQ to entities is unable to perform compare (null check) for complex types as you suggested. For example this does not work...

myContext.Select(i => new
{
    MyComplexType = i.MyComplexType != null ? 
        new MyComplexTypeViewModel() 
        {  
           Property1 = i.MyComplexType.Property1
        }
        : null
})

By default Automapper tries to map null source values as nulls and sometimes adds similar conditions in generated expression when using Project().To<>() or Mapper.Engine.CreateMapExpression<,>().

In my case i was mapping whole complex type to it's own viewmodel and did not use property flattening. This configuration value solved the issue for me...

Mapper.AllowNullDestinationValues = false;

You may try to manually create mapping expression using CreateMapExpression<TSource,TDest>() and look for null checks on complex type to see if it's the same situation.

Upvotes: 6

Related Questions