Skip to content

Commit

Permalink
fix MapsterMapper#309 fix EF Core with param
Browse files Browse the repository at this point in the history
  • Loading branch information
chaowlert committed Feb 24, 2021
1 parent c21b773 commit c93c882
Showing 1 changed file with 58 additions and 4 deletions.
62 changes: 58 additions & 4 deletions src/Mapster.EFCore/TypeAdapterBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Reflection;
using Mapster.EFCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;

namespace Mapster
{
Expand Down Expand Up @@ -42,7 +43,7 @@ public static TypeAdapterBuilder<TSource> EntityFromContext<TSource>(this TypeAd
Expression.Convert(
Expression.Property(
Expression.Property(
Expression.Property(null, current),
Expression.Property(null, current!),
nameof(MapContext.Parameters)),
indexer,
Expression.Constant(dbKey)),
Expand All @@ -55,7 +56,7 @@ public static TypeAdapterBuilder<TSource> EntityFromContext<TSource>(this TypeAd
var setAssign = Expression.Assign(set, Expression.Call(db, setMethod));
var getters = keys.Select(key => arg.DestinationType.GetProperty(key))
.Select(prop => new PropertyModel(prop))
.Select(prop => new PropertyModel(prop!))
.Select(model => arg.Settings.ValueAccessingStrategies
.Select(s => s(src, model, arg))
.FirstOrDefault(exp => exp != null))
Expand All @@ -67,7 +68,7 @@ public static TypeAdapterBuilder<TSource> EntityFromContext<TSource>(this TypeAd
Expression find = Expression.Call(set, nameof(DbContext.Find), null,
Expression.NewArrayInit(typeof(object), getters));
if (arg.MapType == MapType.MapToTarget)
find = Expression.Coalesce(find, dest);
find = Expression.Coalesce(find, dest!);
var ret = Expression.Coalesce(
find,
Expression.New(arg.DestinationType));
Expand All @@ -82,7 +83,60 @@ public static TypeAdapterBuilder<TSource> EntityFromContext<TSource>(this TypeAd
public static IQueryable<TDestination> ProjectToType<TDestination>(this IAdapterBuilder<IQueryable> source)
{
var queryable = source.Source.ProjectToType<TDestination>(source.Config);
return new MapsterQueryable<TDestination>(queryable, source);
if (!source.HasParameter || source.Parameters.All(it => it.Key.StartsWith("Mapster.")))
return new MapsterQueryable<TDestination>(queryable, source);

var call = (MethodCallExpression) queryable.Expression;
var project = call.Arguments[1];
var mapContext = typeof(MapContext);
var current = mapContext.GetProperty(nameof(MapContext.Current), BindingFlags.Public | BindingFlags.Static);
var properties = mapContext.GetProperty(nameof(MapContext.Parameters), BindingFlags.Public | BindingFlags.Instance);
var item = typeof(Dictionary<string, object>)
.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance)!
.GetMethod;

var map = new Dictionary<Expression, Expression>();
foreach (var (key, value) in source.Parameters)
{
if (key.StartsWith("Mapster."))
continue;
var currentEx = Expression.Property(null, current!);
var propertiesEx = Expression.Property(currentEx, properties!);
var itemEx = Expression.Call(propertiesEx, item!, Expression.Constant(key));

map.Add(itemEx, Parameterize(value).Body);
}

var replaced = new ExpressionReplacer(map).Visit(project);
var methodCallExpression = Expression.Call(call.Method, call.Arguments[0], replaced!);
var replacedQueryable = queryable.Provider.CreateQuery<TDestination>(methodCallExpression);
return new MapsterQueryable<TDestination>(replacedQueryable, source);
}

private static Expression<Func<object>> Parameterize(object value)
{
return () => value;
}
}

internal class ExpressionReplacer : ExpressionVisitor
{
private readonly Dictionary<Expression, Expression> _map;

public ExpressionReplacer(Dictionary<Expression, Expression> map)
{
_map = map;
}

public override Expression Visit(Expression node)
{
foreach (var (key, value) in _map)
{
if (ExpressionEqualityComparer.Instance.Equals(node, key))
return value;
}

return base.Visit(node);
}
}
}

0 comments on commit c93c882

Please sign in to comment.