c# - Func 在选择器类型之间的 AutoMapper

标签 c# linq

我有两种类型:CatDog .我想使用 Func<Dog, bool> 选择猫.为此,我需要一种在某种映射器中将 Cat 的属性映射到 Dog 的方法(类似于 AutoMapper 将一个对象的属性映射到另一种类型的对象的方式)。

我正在想象这样的事情:

public Cat GetCat(Func<Dog, bool> selector)
{
    Func<Cat, bool> mappedSelector = getMappedSelector(selector);
    return _catRepository.Get(mappedSelector);
}

private Func<Cat, bool> getMappedSelector(Func<Dog, bool> selector)
{
    //some code here to map from one function type to another

    //something like AutoMapper would be sweet... 
    //something that I can configure how I want the properties to be mapped.
}

要么已经有一些东西可以做到这一点,要么应该有。

最佳答案

这是一个使用 AutoMapper 的解决方案:

Func<Cat, bool> GetMappedSelector(Func<Dog, bool> selector)
{
    Func<Cat, Dog> mapper = Mapper.CreateMapExpression<Cat, Dog>().Compile();
    Func<Cat, bool> mappedSelector = cat => selector(mapper(cat));
    return mappedSelector;
}

更新:自从我第一次回答这个问题已经 1.5 年了,我想我现在应该扩展我的答案,因为人们问当你有一个表达式而不是一个表达式时如何做到这一点代表。

解决方案原则上是相同的 - 我们需要能够 compose将两个函数(selectormapper)合并为一个函数。不幸的是,由于在 C# 中无法从另一个表达式“调用”一个表达式(就像我们可以使用委托(delegate)一样),因此我们无法直接在代码中表示这一点。例如,下面的代码将无法编译:

Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
    Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
    Expression<Func<Cat, bool>> mappedSelector = cat => selector(mapper(cat));
    return mappedSelector;
}

因此,创建组合函数的唯一方法是构建 expression tree我们自己使用 System.Linq.Expressions 类。

我们真正需要做的是修改selector 函数的主体,使其参数的所有实例都被mapper 函数的主体替换。这将成为我们新函数的主体,它将接受 mapper 的参数。

为了替换参数,我创建了 ExpressionVisitor 的子类可以遍历表达式树并用任意表达式替换单个参数的类:

class ParameterReplacer : ExpressionVisitor
{
    private ParameterExpression _parameter;
    private Expression _replacement;

    private ParameterReplacer(ParameterExpression parameter, Expression replacement)
    {
        _parameter = parameter;
        _replacement = replacement;
    }

    public static Expression Replace(Expression expression, ParameterExpression parameter, Expression replacement)
    {
        return new ParameterReplacer(parameter, replacement).Visit(expression);
    }

    protected override Expression VisitParameter(ParameterExpression parameter)
    {
        if (parameter == _parameter)
        {
            return _replacement;
        }
        return base.VisitParameter(parameter);
    }
}

然后我创建了一个扩展方法 Compose(),它使用访问者组合两个 lambda 表达式,一个外部表达式和一个内部表达式:

public static class FunctionCompositionExtensions
{
    public static Expression<Func<X, Y>> Compose<X, Y, Z>(this Expression<Func<Z, Y>> outer, Expression<Func<X, Z>> inner)
    {
        return Expression.Lambda<Func<X ,Y>>(
            ParameterReplacer.Replace(outer.Body, outer.Parameters[0], inner.Body),
            inner.Parameters[0]);
    }
}

现在,有了所有这些基础设施,我们可以修改我们的 GetMappedSelector() 方法来使用我们的 Compose() 扩展:

Expression<Func<Cat, bool>> GetMappedSelector(Expression<Func<Dog, bool>> selector)
{
    Expression<Func<Cat, Dog>> mapper = Mapper.CreateMapExpression<Cat, Dog>();
    Expression<Func<Cat, bool>> mappedSelector = selector.Compose(mapper);
    return mappedSelector;
}

我创建了一个 simple console application来测试一下。希望我的解释不会太含糊;但不幸的是,并没有真正更简单的方法来做你想做的事情。如果您仍然一头雾水,至少您可以重用我的代码,并了解处理表达式树的细微差别和复杂性!

关于c# - Func 在选择器类型之间的 AutoMapper,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7424501/

相关文章:

c# - Visual Studio 扩展命名空间产生错误

c# - FileMode.Create 和 FileMode.Truncate 有什么区别?

c# - Linq 中使用哪些方法强制从数据库中检索数据

c# - 什么是 PartialEvaluationExceptionExpression 以及如何修复它?

c# - 如果字段是实例成员,则回调处理程序异常

c# - 一个 dll 中的多个函数还是单独的?

c# - 获取两个日期时间之间的所有月份和年份

c# - XML 到 IEnumerable<T>

c# - 当技能不能在 C# 之外转移时,LINQ 中的 "think"是否不好?

c# - 通过 LINQ 将函数应用于集合的所有元素