C# 表达式<;Func<;T、 布尔>&燃气轮机;谓词有两种类型

C# 表达式<;Func<;T、 布尔>&燃气轮机;谓词有两种类型,c#,C#,我有WebAPI方法: public async Task<MerchantViewModel> Get(string externalId) { return await _service.Get(m => m.ExternalId == externalId); } 其中T为商户 现在我想做一些类似的事情: public async Task<MerchantViewModel> Get(Expression<Func&l

我有WebAPI方法:

public async Task<MerchantViewModel> Get(string externalId)
    {
        return await _service.Get(m => m.ExternalId == externalId);
    }
其中
T
商户

现在我想做一些类似的事情:

public async Task<MerchantViewModel> Get(Expression<Func<MerchantViewModel, bool>> predicate)
{
    var domainMerchant = this._repository.GetItemAsync(predicate)
}
公共异步任务Get(表达式谓词)
{
var domainMerchant=this.\u repository.GetItemAsync(谓词)
}
我怎么能做到


Merchant和MerchantViewModel具有相同的属性。ViewModel还有更多功能

解决方案1

首先,将
MerchantViewModel
Merchant
之间重叠的属性抽象到一个接口中:

public IMerchantFilter
{
    public string ExternalId { get; set; }

    ...
}
然后让
MerchantViewModel
Merchant
从该接口继承

public MerchantViewModel : IMerchantFilter
{
    public string ExternalId { get; set; }

    ...
}

public Merchant : IMerchantFilter
{
    public string ExternalId { get; set; }

    ...
}
使用谓词签名中的接口:

public class MerchantService : IMerchantService
{
    public Task<MerchantViewModel> Get(Expression<Func<IMerchantFilter, bool>> predicate)
    {
        ...
    }

    ...
}

public class MerchantRepository : ...
{
    public async Task<IEnumerable<Merchant>> GetItemsAsync(
        Expression<Func<IMerchantFilter, bool>> predicate = null)
    {
        ...
    }

    ...
}
公共类商品服务:IMerchantService
{
公共任务Get(表达式谓词)
{
...
}
...
}
公共类商品仓:。。。
{
公共异步任务GetItemsAsync(
表达式谓词=null)
{
...
}
...
}

解决方案2

不同的解决方案(基于)可以在类型之间映射谓词:

using System;
using System.Linq.Expressions;

public static class PredicateMapper
{
    public static Expression<Func<TTo, bool>> CastParameter<TFrom, TTo>(
        this Expression<Func<TFrom, bool>> predicate)
    {
        var parameter = Expression.Parameter(typeof(TTo));
        var body = new ParameterReplacer<TTo>(parameter).Visit(predicate.Body);

        return Expression.Lambda<Func<TTo, bool>>(body, parameter);
    }

    private class ParameterReplacer<TTo> : ExpressionVisitor
    {
        private readonly ParameterExpression parameter;

        public ParameterReplacer(ParameterExpression parameter)
        {
            this.parameter = parameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            return this.parameter;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            var matchingMember = typeof(TTo).GetProperty(node.Member.Name);
            return Expression.Property(this.Visit(node.Expression), matchingMember);
        }
    }
}
使用系统;
使用System.Linq.Expressions;
公共静态类谓词映射器
{
公共静态表达式参数(
此表达式(谓词)
{
var参数=表达式参数(typeof(TTo));
var body=新参数replacer(parameter.Visit)(predicate.body);
返回表达式.Lambda(主体,参数);
}
私有类参数替换器:ExpressionVisitor
{
私有只读参数expression参数;
公共参数替换器(ParameterExpression参数)
{
this.parameter=参数;
}
受保护的重写表达式VisitParameter(ParameterExpression节点)
{
返回此参数;
}
受保护的重写表达式VisitMember(MemberExpression节点)
{
var matchingMember=typeof(TTo).GetProperty(node.Member.Name);
返回Expression.Property(this.Visit(node.Expression),matchingMember);
}
}
}
在您的场景中,用法如下所示:

public async Task<MerchantViewModel> Get(
    Expression<Func<MerchantViewModel, bool>> predicate)
{
    var domainMerchant = this._repository.GetItemAsync(
        predicate.CastParameter<MerchantViewModel, Merchant>());
}
公共异步任务获取(
表达式(谓词)
{
var domainMerchant=this.\u repository.GetItemAsync(
predicate.CastParameter());
}
有关此实现的更多详细信息,请参阅


请注意,此解决方案在使用方面可能会更好,但如果类型不匹配,则会导致运行时错误而不是编译时错误。这实际上使它更容易出错,也不易维护。

添加另一个重载或使用通用TW传递替换商户类型方法签名中有
T
?前两个方法返回多个对象。最后一个方法似乎想要返回单个对象。现在还不清楚你在问什么。试着检查一下这一个,这是一个很好的方法,但如果我有XX个类要用这种方法做呢?没有简单的方法?如果这是一个常见的用例,那么为什么有两个不同的模型?为什么不在这两层上使用
Merchant
和所有其他类型?因为有不同的视图模型和域模型是一种很好的模式。。tchem之间有一些小的变化。我看到的另一个选择是使用某种转换器,它接受一个
表达式
,并从中创建一个
表达式
。这可能很难实现,但很容易使用。对于第二种方法,名称应该改进<首先,code>MapParameter会更好,但即使这样,这里也不是真正的情况
CastParameter
可能会准确地描述您在这里所做的事情。这就是说,一个简单的
泛型约束,其中TFrom:TTo
将确保编译时类型安全。此外,如果提供具有多个参数的lambda,则您的
ParameterReplace
将严重失败;名称应该反映这一点,或者它实际上应该替换提供的参数。
public MerchantViewModel : IMerchantFilter
{
    public string ExternalId { get; set; }

    ...
}

public Merchant : IMerchantFilter
{
    public string ExternalId { get; set; }

    ...
}
public class MerchantService : IMerchantService
{
    public Task<MerchantViewModel> Get(Expression<Func<IMerchantFilter, bool>> predicate)
    {
        ...
    }

    ...
}

public class MerchantRepository : ...
{
    public async Task<IEnumerable<Merchant>> GetItemsAsync(
        Expression<Func<IMerchantFilter, bool>> predicate = null)
    {
        ...
    }

    ...
}
using System;
using System.Linq.Expressions;

public static class PredicateMapper
{
    public static Expression<Func<TTo, bool>> CastParameter<TFrom, TTo>(
        this Expression<Func<TFrom, bool>> predicate)
    {
        var parameter = Expression.Parameter(typeof(TTo));
        var body = new ParameterReplacer<TTo>(parameter).Visit(predicate.Body);

        return Expression.Lambda<Func<TTo, bool>>(body, parameter);
    }

    private class ParameterReplacer<TTo> : ExpressionVisitor
    {
        private readonly ParameterExpression parameter;

        public ParameterReplacer(ParameterExpression parameter)
        {
            this.parameter = parameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            return this.parameter;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            var matchingMember = typeof(TTo).GetProperty(node.Member.Name);
            return Expression.Property(this.Visit(node.Expression), matchingMember);
        }
    }
}
public async Task<MerchantViewModel> Get(
    Expression<Func<MerchantViewModel, bool>> predicate)
{
    var domainMerchant = this._repository.GetItemAsync(
        predicate.CastParameter<MerchantViewModel, Merchant>());
}