表达式<func <t,bool =“”>&gt;谓词两种类型

时间:2016-05-30 10:52:34

标签: c#

我有WebAPI方法:

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

我有IService:

public interface IService<T> 
    where T : class 
{
    Task<T> Get(Expression<Func<T, bool>> predicate);
    Task<IEnumerable<T>> GetAll(Expression<Func<T, bool>> predicate = null);
    void Add(T viewModel);
    void Delete(string id);
    void Update(T viewModel);
}

和IMerchantService:

 public interface IMerchantService : IService<MerchantViewModel>
{
}

使用方法实施:

  public class MerchantService : IMerchantService { // ... 
public async Task<IEnumerable<MerchantViewModel>> GetAll(Expression<Func<MerchantViewModel, bool>> predicate = null)

.. //
 }

T MerchantViewModel的位置 我有方法存储库:

public async Task<IEnumerable<Merchant>> GetItemsAsync(Expression<Func<Merchant, bool>> predicate = null)

TMerchant的位置。

现在我想做那样的事情:

public async Task<MerchantViewModel> Get(Expression<Func<MerchantViewModel, bool>> predicate)
{
    var domainMerchant = this._repository.GetItemAsync(predicate)
}

我怎么能这样做?

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

1 个答案:

答案 0 :(得分:1)

解决方案1 ​​

首先,将MerchantViewModelMerchant之间重叠的属性抽象为接口:

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

    ...
}

然后让MerchantViewModelMerchant继承此接口。

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)
    {
        ...
    }

    ...
}

解决方案2

另一种解决方案(基于this question)可以在类型之间映射谓词:

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>());
}

有关此实施的更多详细信息,请参阅the working example

请注意,此解决方案在使用方面可能看起来更好,但如果类型不匹配,则会导致运行时错误,而不是编译时错误。这使得它实际上更容易出错并且更难以管理。