Generic expression abstraction issue

隐身守侯 提交于 2020-01-13 06:09:11

问题


I have the following method SetMapping() which is used to define some mapping setting using expressions.

public class AggregateMap<TDataEntity>
{
    protected Expression<Func<IUpdateConfiguration<TDataEntity>, object>> graphMapping;

    protected void SetMapping(Expression<Func<IUpdateConfiguration<TDataEntity>, object>> mapping)
    {
        graphMapping = mapping;
    }
}

Example calling code:

SetMapping(map => map.OwnedCollection(root => root.ChildEntities));

The above works great, but I would like to abstract this method a bit further by providing SetOwnedCollectionMapping(). This means the calling code can provide a much more basic expression.

Further abstracted method:

protected void SetOwnedCollectionMapping<T>(Expression<Func<TDataEntity, ICollection<T>>> mapping)
{
    graphMapping = map => map.OwnedCollection<TDataEntity, T>(mapping);
}

Example calling code:

SetOwnedCollectionMapping(root => root.ChildEntities);

This graphMapping field is then used in an external library (RefactorThis.GraphDiff) by calling the following method on a Entity Framework DbContext instance:

public static void UpdateGraph<T>(this DbContext context, T entity, Expression<Func<IUpdateConfiguration<T>, object>> mapping) where T : class;

The following exception is being thrown at run-time:

An exception of type 'System.InvalidCastException' occurred in RefactorThis.GraphDiff.dll but was not handled in user code

Additional information: Unable to cast object of type 'System.Reflection.RtFieldInfo' to type 'System.Reflection.PropertyInfo'.

I must be confusing my generic types up, but I cannot see the difference between the old and new implementations.

Here is the signature for the OwnedCollection method:

public static IUpdateConfiguration<T> OwnedCollection<T, T2>(this IUpdateConfiguration<T> config, Expression<Func<T, System.Collections.Generic.ICollection<T2>>> expression);

EDIT: Added UpdateGraph info to question.


回答1:


The key difference between your two implementations is that the second captures a method parameter, while the first does not. The parameter gets stored as a field in a closure, and the presence of that field access is probably causing problems in RefactorThis.GraphDiff.dll.

Try changing the second implementation as follows:

protected void SetOwnedCollectionMapping<T>(Expression<Func<TDataEntity, ICollection<T>>> mapping)
{
    //
    // Hack to resolve the `OwnedCollection` extension method.
    //
    Expression<Func<IUpdateConfiguration<TDataEntity>, object>> template = 
        _ => _.OwnedCollection(mapping);

    var map = Expression.Parameter(
        typeof(IUpdateConfiguration<TDataEntity>),
        "map");

    graphMapping = Expression.Lambda<Func<IUpdateConfiguration<TDataEntity>, object>>(
        Expression.Call(
            ((MethodCallExpression)template.Body).Method,
            map,
            Expression.Quote(mapping)),
        map);
}

The value of graphMapping should then be identical to that produced by your first implementation.



来源:https://stackoverflow.com/questions/21450933/generic-expression-abstraction-issue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!