2016-05-30 8 views
1

私はWebAPIの方法があります。式<機能<T, bool>>述語2種類

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 async Task<MerchantViewModel> Get(string externalId) 
    { 
     return await _service.Get(m => m.ExternalId == externalId); 
    } 

を私はIServiceを持っています

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

.. // 
} 

TMerchantViewModel であると、私はこの方法でリポジトリを有する場合:TMerchantある

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

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

は、私はそれをどのように行うことができます。

そして今、私はそのような何かをしたいですか?

マーチャントとMerchantViewModelのプロパティは同じです。 ViewModelには何かがあります。

+0

別のオーバーロードを追加するか、商人のタイプを総称T –

+2

に置き換えます。メソッド署名にはどこに「T」がありますか?最初の2つのメソッドは、複数のオブジェクトを返します。最後のメソッドは、単一のオブジェクトを返すことを望んでいるようです。あなたが求めていることは本当にはっきりしません。 –

+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; } 

    ... 
} 

述語シグネチャのインターフェイスを使用:(this questionに基づいて)異なる溶液を型の間の述語をマッピングすることができ

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

を:

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); 
     } 
    } 
} 
0あなたのシナリオでは

のようになります。使用方法は、以下:

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

これが一般的な使用例である場合、なぜ2つの異なるモデルがあるのでしょうか?両方のレイヤーで「商人」と他のすべてのタイプを使用してみませんか? –

+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 
     } 

エラーです。 "値はnullにはできません"、 "パラメータ名:プロパティ"。

+0

それは動作します。 [この.NET Fiddle](https://dotnetfiddle.net/Dl3IjZ)をチェックしてください。 –

+0

えええええええええええええええええええええええええええ、 – Nerf

+0

おそらく 'Merchant.ExternalId'プロパティにタイプミスがありますか?コードをデバッグしましたか? 'typeof(TTo).GetProperties()'は何を返しますか? –

関連する問題