EF.Property not mapped correctly
Xriuk opened this issue · 5 comments
public class TestProduct {
// Empty, has shadow key named BrandId
}
public class TestProductDTO {
public int Brand { get; set; }
}
void Test{
var config = new MapperConfiguration(c =>
{
c.CreateMap<TestProduct, TestProductDTO>()
.ForMember(p => p.Brand, c => c.MapFrom(p => EF.Property<int>(p, "BrandId")));
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
var products = new List<TestProduct>() {
new TestProduct { }
}.AsQueryable();
Expression<Func<TestProductDTO, bool>> expr = x => x.Brand == 2;
var mappedExpression = mapper.MapExpression<Expression<Func<TestProduct, bool>>>(expr);
// The expression above returns: x => EF.Property(p, "BrandId") == 2
}
I don't know if this happens for other "functions", apparently the parameter "p" does not get replaced by some visitor during the conversion
Side note: this time I'm pretty sure that this didn't happen when using UseAsDataSource
, is there any reason why there are two different behaviours for the same goal?
You are correct. Add the following to PrependParentNameVisitor
and your test should work.
protected override Expression VisitParameter(ParameterExpression node)
{
if (object.ReferenceEquals(CurrentParameter, node))
return NewParameter;
return base.VisitParameter(node);
}
On your second question you can contribute by reconciling the two i.e. get SourceInjectedQueryProvider
expression mapper to pass the MapExpression
tests or get XpressionMapperVisitor
to integrate with SourceInjectedQueryProvider
.
I tried and it works... partially.
If I have a complex model with inclusion, the property is still mapped to the top level:
public class TestCategory {
// Has FK BrandId
}
public class TestProduct {
public TestCategory? Category { get; set; }
}
public class TestProductDTO {
public int Brand { get; set; }
}
void Test(){
var config = new MapperConfiguration(c =>
{
c.CreateMap<TestCategory, TestProductDTO>()
.ForMember(p => p.Brand, c => c.MapFrom(p => EF.Property<int>(p, "BrandId"))); ;
c.CreateMap<TestProduct, TestProductDTO>()
.IncludeMembers(p => p.Category);
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
var products = new List<TestProduct>() {
new TestProduct {
Category = new TestCategory{ }
}
}.AsQueryable();
Expression<Func<TestProductDTO, bool>> expr = x => x.Brand == 2;
var mappedExpression = mapper.MapExpression<Expression<Func<TestProduct, bool>>>(expr);
// Expected: x => EF.Property<int>(x.Category, "BrandId") == 2
// Actual: x => EF.Property<int>(x, "BrandId") == 2
}
I took a look at the code, and I'm a bit lost... I was looking here
it calls
FindDestinationFullName()
, which seems to populate propertyMapInfoList
which is a list of mapping expressions for the passed node(?), so to convert the node to the mapped one you should apply all these expressions in order, right?But it seems that when it calls
GetMemberExpressionFromCustomExpression()
it only uses the last one.Am I on the right track or not?
Right track - but that's what PrependParentNameVisitor
does. Sorry my bad advice. The code should be as follows similar to VisitTypeBinary
, VisitMember
and VisitMethodCall
in the same class.
protected override Expression VisitParameter(ParameterExpression node)
{
if (object.ReferenceEquals(CurrentParameter, node))
{
return string.IsNullOrEmpty(ParentFullName)
? NewParameter
: ExpressionHelpers.MemberAccesses(ParentFullName, NewParameter);
}
return base.VisitParameter(node);
}
I'll update the code unless you're interested in doing the PR.
Yep, this works! You can update the code, I've never created a PR for open source projects (yet).
When I get some time I'll take a look at the SourceInjectedQueryProvider
matter, wanted to make it async too.