2016-05-30 145 views
1

我的WebAPI方法:表达<Func键<T, bool>>谓词两种类型

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) 

.. // 
} 

哪里TMerchantViewModel ,我有资源库和方法:

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有更多的东西。

+0

添加另一个重载或传递替换商品类型与通用T –

+2

方法签名中的T是哪里?前两个方法返回多个对象。最后一种方法似乎想要返回一个对象。这是不是很清楚你在问什么。 –

+0

尝试检查这一个http://stackoverflow.com/questions/18337692/entity-framework-filter-expressionfunct-bool –

回答

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

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

+0

这是一个很好的方法来做到这一点,但如果我有XX类与这样做呢?有没有简单的方法? – Nerf

+0

如果这是一个常见用例,那么为什么即使有两个不同的模型?为什么不在两个层上使用'Merchant'和所有其他类型? –

+0

因为它具有不同的视图模型和领域模型的好模式.. tchem之间有小的变化。 – Nerf

0

@Abbondanza解决方案2很漂亮,但不起作用。

商家:

public class Merchant : AggregateRoot 
{ 
    public string CompanyName { get; set; } 
    public string ExternalId { get; set; } 
    public string NIP { get; set; } 
    public string Status { get; set; } 

    public string Url { get; set; } 
    public virtual Address Address { get; set; } 
    public virtual Group Group { get; set; } 
    public virtual ICollection<Brand> Brands { get; set; } 

    public Merchant() 
    { 
     this.Brands = new List<Brand>(); 
    } 
} 

MerchantViewModel:

public class MerchantViewModel 
{ 
    public string Id { get; set; } 
    public string CompanyName { get; set; } 
    public string ExternalId { get; set; } 
    public string NIP { get; set; } 
    public string Status { get; set; } 

    public string Url { get; set; } 
    public virtual AddressViewModel Address { get; set; } 
    public virtual GroupViewModel Group { get; set; } 
    public virtual ICollection<BrandViewModel> Brands { get; set; } 
} 

PredicateMapperTest:

[Fact] 
    public void CastParameter_ValidTypes_ShouldReturnValidPredicate() 
    { 
     // Arrange 
     string externalId = "Test externalId"; 
     Expression<Func<MerchantViewModel, bool>> inputPredicate = viewModel => viewModel.ExternalId == externalId; 
     Expression<Func<Merchant, bool>> outputPredicate; 

     // Act 
     outputPredicate = PredicateMapper.CastParameter<MerchantViewModel, Merchant>(inputPredicate); 

     // Assert 
     outputPredicate.Should().NotBeNull(); 
    } 

还有:

protected override Expression VisitMember(MemberExpression node) 
     { 
      var matchingMember = typeof(TTo).GetProperty(node.Member.Name); 
      return Expression.Property(this.Visit(node.Expression), matchingMember); // HERE 
     } 

是错误。 “值不能为空”,“参数名称:属性”。

+0

它确实有效。检查[这个.NET小提琴](https://dotnetfiddle.net/Dl3IjZ)。 –

+0

嗯,是在.NET小提琴它的作品,但在我的代码没有。 – Nerf

+0

也许在“Merchant.ExternalId”属性中存在拼写错误?你调试了代码吗? 'typeof(TTo).GetProperties()'返回什么? –

相关问题