Skip to content

Commit

Permalink
Merge pull request #3870 from AutoMapper/mapfrom_identity
Browse files Browse the repository at this point in the history
Handle identity lambda resolvers with ProjectTo subquery
  • Loading branch information
jbogard committed Jan 31, 2022
2 parents 148c525 + f32b7ed commit 9ccbd7c
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/AutoMapper/Execution/ExpressionBuilder.cs
Expand Up @@ -232,7 +232,7 @@ public static bool IsMemberPath(this LambdaExpression lambda, out Stack<Member>
foreach (var member in members)
{
currentExpression = member.Expression;
if (!(currentExpression is MemberExpression))
if (currentExpression is not MemberExpression)
{
return false;
}
Expand Down
23 changes: 21 additions & 2 deletions src/AutoMapper/QueryableExtensions/ProjectionBuilder.cs
Expand Up @@ -265,8 +265,27 @@ public SubQueryPath(MemberProjection[] members, LambdaExpression letExpression)
Marker = Default(letExpression.Body.Type);
LetExpression = letExpression;
}
public Expression GetSourceExpression(Expression parameter) => _members.Take(_members.Length - 1).Select(p => p.Expression).Aggregate(parameter,
(left, right) => right is LambdaExpression lambda ? lambda.ReplaceParameters(left) : right.Replace(right.GetChain().Peek().Target, left));
public Expression GetSourceExpression(Expression parameter)
{
Expression sourceExpression = parameter;
for (int index = 0; index < _members.Length - 1; index++)
{
var sourceMember = _members[index].Expression;
if (sourceMember is LambdaExpression lambda)
{
sourceExpression = lambda.ReplaceParameters(sourceExpression);
}
else
{
var chain = sourceMember.GetChain();
if (chain.TryPeek(out var first))
{
sourceExpression = sourceMember.Replace(first.Target, sourceExpression);
}
}
}
return sourceExpression;
}
public PropertyDescription GetPropertyDescription() => new("__" + string.Join("#", _members.Select(p => p.MemberMap.DestinationName)), LetExpression.Body.Type);
internal bool IsEquivalentTo(SubQueryPath other) => LetExpression == other.LetExpression && _members.Length == other._members.Length &&
_members.Take(_members.Length - 1).Zip(other._members, (left, right) => left.MemberMap == right.MemberMap).All(item => item);
Expand Down
100 changes: 100 additions & 0 deletions src/IntegrationTests/CustomMapFrom/MapObjectPropertyFromSubQuery.cs
Expand Up @@ -959,4 +959,104 @@ public void Should_project_ok()
}
}
}
public class MemberWithSubQueryIdentity : AutoMapperSpecBase
{
protected override MapperConfiguration CreateConfiguration() => new MapperConfiguration(cfg =>
{
cfg.CreateProjection<AEntity, Dto>()
.ForMember(dst => dst.DtoSubWrapper, opt => opt.MapFrom(src => src));
cfg.CreateProjection<AEntity, DtoSubWrapper>()
.ForMember(dst => dst.DtoSub, opt => opt.MapFrom(src => src.BEntity.CEntities.FirstOrDefault(x => x.Id == src.CEntityId)));
cfg.CreateProjection<CEntity, DtoSub>();
});
[Fact]
public void Should_work()
{
var query = ProjectTo<Dto>(new ClientContext().AEntities);
var result = query.Single();
result.DtoSubWrapper.DtoSub.ShouldNotBeNull();
result.DtoSubWrapper.DtoSub.SubString.ShouldBe("Test");
}
public class Dto
{
public int Id { get; set; }
public DtoSubWrapper DtoSubWrapper { get; set; }
}
public class DtoSubWrapper
{
public DtoSub DtoSub { get; set; }
}
public class DtoSub
{
public int Id { get; set; }
public string SubString { get; set; }
}
public class AEntity
{
public int Id { get; set; }
public int BEntityId { get; set; }
public int CEntityId { get; set; }
public BEntity BEntity { get; set; }
}
public class BEntity
{
public int Id { get; set; }
public ICollection<CEntity> CEntities { get; set; }
}
public class CEntity
{
public int Id { get; set; }
public int BEntityId { get; set; }
public string SubString { get; set; }
public BEntity BEntity { get; set; }
}
class Initializer : DropCreateDatabaseAlways<ClientContext>
{
protected override void Seed(ClientContext context)
{
context.AEntities.Add(new AEntity
{
Id = 1,
BEntityId = 1,
CEntityId = 6,
BEntity = new BEntity
{
Id = 1,
CEntities = new List<CEntity>
{
new CEntity
{
Id = 6,
BEntityId = 1,
SubString = "Test"
}
}
},
});
}
}
class ClientContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
Database.SetInitializer(new Initializer());

modelBuilder.Entity<AEntity>()
.HasRequired(x => x.BEntity)
.WithMany()
.HasForeignKey(x => x.BEntityId);

modelBuilder.Entity<BEntity>()
.HasMany(x => x.CEntities)
.WithRequired(x => x.BEntity)
.HasForeignKey(x => x.BEntityId);

modelBuilder.Entity<CEntity>()
.Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
public DbSet<AEntity> AEntities { get; set; }
}
}
}

0 comments on commit 9ccbd7c

Please sign in to comment.