-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Closed
Closed
Copy link
Description
Source/destination types
namespace MapRepro.Entities
{
public class Foo
{
public int Id { get; set; }
public int BarId { get; set; }
public virtual Bar Bar { get; set; }
}
public class Bar
{
public Bar()
{
Foos = new HashSet<Foo>();
}
public int Id { get; set; }
public int BazId { get; set; }
public virtual Baz Baz { get; set; }
public virtual ICollection<Foo> Foos { get; set; }
}
public class Baz
{
public Baz()
{
Bars = new HashSet<Bar>();
Widgets = new HashSet<Widget>();
}
public int Id { get; set; }
public virtual ICollection<Bar> Bars { get; set; }
public virtual ICollection<Widget> Widgets { get; set; }
}
public partial class Widget
{
public int Id { get; set; }
public int BazId { get; set; }
public virtual Baz Baz { get; set; }
}
}
namespace MapRepro.Models
{
public class Foo
{
public int Id { get; set; }
public int BarId { get; set; }
public Bar MyBar { get; set; }
}
public class Bar
{
public int Id { get; set; }
public int BazId { get; set; }
public Baz MyBaz { get; set; }
}
public class Baz
{
public int Id { get; set; }
public Widget FirstWidget { get; set; }
}
public class Widget
{
public int Id { get; set; }
public int BazId { get; set; }
}
}Mapping configuration
class TestProfile : Profile
{
public TestProfile()
{
CreateMap<Entities.Foo, Models.Foo>()
.ForMember(f => f.Id, opts => opts.MapFrom(src => src.Id))
.ForMember(f => f.BarId, opts => opts.MapFrom(src => src.BarId))
.ForMember(f => f.MyBar, opts => opts.MapFrom(src => src.Bar))
;
CreateMap<Entities.Bar, Models.Bar>()
.ForMember(f => f.Id, opts => opts.MapFrom(src => src.Id))
.ForMember(f => f.BazId, opts => opts.MapFrom(src => src.BazId))
.ForMember(f => f.MyBaz, opts => opts.MapFrom(src => src.Baz))
;
CreateMap<Entities.Baz, Models.Baz>()
.ForMember(f => f.Id, opts => opts.MapFrom(src => src.Id))
.ForMember(f => f.FirstWidget, opts => opts.MapFrom(src => src.Widgets.FirstOrDefault()))
;
CreateMap<Entities.Widget, Models.Widget>()
.ForMember(f => f.Id, opts => opts.MapFrom(src => src.Id))
.ForMember(f => f.BazId, opts => opts.MapFrom(src => src.BazId))
;
}
}
//...
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new TestProfile());
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();Version: 12.0.1
EF Core 7.0.3
EF Core InMemory 7.0.3
Expected behavior
A valid mapping and successful projection
Actual behavior
An ArgumentException when AutoMapper tries to access a property to the wrong object.
As you can see by the exception below SubQueryPath.GetSourceExpression is trying to access the property Bar on the object Bar, which does not exist. Strangely, it only happens when projecting Foos, not when projecting Bars
System.ArgumentException
HResult=0x80070057
Message=Property 'MapRepro.Entities.Bar Bar' is not defined for type 'MapRepro.Entities.Bar' (Parameter 'property')
Source=System.Linq.Expressions
StackTrace:
at System.Linq.Expressions.Expression.Property(Expression expression, PropertyInfo property)
at System.Linq.Expressions.Expression.MakeMemberAccess(Expression expression, MemberInfo member)
at System.Linq.Expressions.MemberExpression.Update(Expression expression)
at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
at AutoMapper.QueryableExtensions.Impl.ProjectionBuilder.FirstPassLetPropertyMaps.SubQueryPath.GetSourceExpression(Expression parameter)
at AutoMapper.QueryableExtensions.Impl.ProjectionBuilder.FirstPassLetPropertyMaps.<>c__DisplayClass11_0.<GetSubQueryExpression>b__0(SubQueryPath path)
at System.Linq.Enumerable.SelectListIterator`2.ToArray()
at AutoMapper.QueryableExtensions.Impl.ProjectionBuilder.FirstPassLetPropertyMaps.GetSubQueryExpression(ProjectionBuilder builder, Expression projection, TypeMap typeMap, ProjectionRequest& request, ParameterExpression instanceParameter)
at AutoMapper.QueryableExtensions.Impl.ProjectionBuilder.CreateProjection(ProjectionRequest& request, LetPropertyMaps letPropertyMaps)
at AutoMapper.Internal.LockingConcurrentDictionary`2.<>c__DisplayClass2_1.<.ctor>b__1()
at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
at System.Lazy`1.CreateValue()
at AutoMapper.QueryableExtensions.Impl.ProjectionBuilder.GetProjection(Type sourceType, Type destinationType, Object parameters, MemberPath[] membersToExpand)
at AutoMapper.QueryableExtensions.Extensions.ToCore(IQueryable source, Type destinationType, IConfigurationProvider configuration, Object parameters, IEnumerable`1 memberPathsToExpand)
at AutoMapper.QueryableExtensions.Extensions.ToCore[TResult](IQueryable source, IConfigurationProvider configuration, Object parameters, IEnumerable`1 memberPathsToExpand)
at MapRepro.Program.Main(String[] args) in C:\Users\alexander.DIVINELOGIC\source\repos\MapRepro\MapRepro\Program.cs:line 24
Steps to reproduce
See gist: https://gist.github.com/MrZander/0bcdd5a9b8b42e862b5b523b0d49002a
var context = new ClientContext(new DbContextOptionsBuilder<ClientContext>().UseInMemoryDatabase("Test").Options);
// Bar -> Baz -> Widgets.FirstOrDefault()
// OKAY
var resultOkay = mapper.ProjectTo<Models.Bar>(context.Bars);
// Foo -> Bar -> Baz -> Widgets.FirstOrDefault()
// System.ArgumentException: 'Property 'MapRepro.Entities.Bar Bar' is not defined for type 'MapRepro.Entities.Bar' (Parameter 'property')'
var resultNotOkay = mapper.ProjectTo<Models.Foo>(context.Foos);