我有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)
T
为Merchant
的位置。
现在我想做那样的事情:
public async Task<MerchantViewModel> Get(Expression<Func<MerchantViewModel, bool>> predicate)
{
var domainMerchant = this._repository.GetItemAsync(predicate)
}
我怎么能这样做?
Merchant和MerchantViewModel具有相同的属性。 ViewModel还有更多功能。
答案 0 :(得分:1)
解决方案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)
{
...
}
...
}
解决方案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。
请注意,此解决方案在使用方面可能看起来更好,但如果类型不匹配,则会导致运行时错误,而不是编译时错误。这使得它实际上更容易出错并且更难以管理。