5

私は検証ロジックにSpecification patternを適用しようとしています。しかし、非同期検証にはいくつか問題があります。仕様パターンasync

私はエンティティAddRequest(2文字列のプロパティFileNameとContentを持つ)が検証される必要があるとします。

私は3つのバリデータを作成する必要があり

  1. 検証をファイル名に無効な文字が含まれていない場合

  2. 検証内容が正しいかどう

  3. 非同期のファイル名を持つファイルが存在しているかどうかを検証データベース上でこの場合、私はTask<bool> IsSatisfiedByAsync

のようなものを持っている必要がありますが、どのように私はIsSatisfiedByIsSatisfiedByAsyncの両方を実装することができますか? ISpecificationIAsyncSpecificationのような2つのインターフェイスを作成する必要がありますか?

ファイルは、私が使うべき存在するかどうかを検証しISpecificationの私のバージョン(私は必要とし)

public interface ISpecification 
    { 
     bool IsSatisfiedBy(object candidate); 
     ISpecification And(ISpecification other); 
    } 

AndSpecification

public class AndSpecification : CompositeSpecification 
{ 
    private ISpecification leftCondition; 
    private ISpecification rightCondition; 

    public AndSpecification(ISpecification left, ISpecification right) 
    { 
     leftCondition = left; 
     rightCondition = right; 
    } 

    public override bool IsSatisfiedBy(object o) 
    { 
     return leftCondition.IsSatisfiedBy(o) && rightCondition.IsSatisfiedBy(o); 
    } 
} 

私は IsSatisfiedByを書くことができますどのように
await _fileStorage.FileExistsAsync(addRequest.FileName); 

は、そのチェックのために私は本当に非同期を行う必要がある場合?例えば

ここに私のバリデータは(1)ファイル名

public class FileNameSpecification : CompositeSpecification 
{ 
    private static readonly char[] _invalidEndingCharacters = { '.', '/' }; 

    public override bool IsSatisfiedBy(object o) 
    { 
     var request = (AddRequest)o; 
     if (string.IsNullOrEmpty(request.FileName)) 
     { 
      return false; 
     } 
     if (request.FileName.Length > 1024) 
     { 
      return false; 
     } 
     if (request.FileName.Contains('\\') || _invalidEndingCharacters.Contains(request.FileName.Last())) 
     { 
      return false; 
     } 

     return true 
    } 
} 

のために、私はFileExistsSpecificationを作成し、同様に使用する必要があります。

var validations = new FileNameSpecification().And(new FileExistsSpecification()); 
if(validations.IsSatisfiedBy(addRequest)) 
{ ... } 

しかし、私は非同期が必要な場合どのように私はFileExistsSpecificationを作成することができますか?

+2

、私は非同期に一般化し、同期の契約を破棄したいです。仕様を任意に組み合わせることができ、クライアントが仕様が非同期であるかどうかわからないことを考えると、すべてを非同期であるかのように扱うのが自然であるようです。 – plalx

+0

'string.IsNullOrEmpty(request.FileName)'は 'request.FileName.Length> 1024'より前に実行されなければなりませんか?それは非同期にすることはできません – ByeBye

+0

@ByeByeあなたはどういうことを言っていますか? 'string.IsNullOrEmpty(request.FileName)' 'request.FileName.Length> 1024の前に実行されました。コード –

答えて

2

は、しかし、どのように私はIsSatisfiedByとIsSatisfiedByAsyncの両方を実装することができますか? ISpecificationやIAsyncSpecificationのような2つのインターフェースを作成する必要がありますか?

汎用インターフェイスと非同期インターフェイスの両方を定義できますが、汎用コンポジット実装では非同期バージョンのみを実装する必要があります。 asynchronous methods on interfaces mean "this might be asynchronous"以来

同期方法は、「この同期している必要があります」を意味するのに対し、私のような、非同期専用インターフェイスで行くと思いますが:

public interface ISpecification 
{ 
    Task<bool> IsSatisfiedByAsync(object candidate); 
} 

あなた仕様の多くが同期している場合、次のことができます基底クラスを手伝う:

public abstract class SynchronousSpecificationBase : ISpecification 
{ 
    public virtual Task<bool> IsSatisfiedByAsync(object candidate) 
    { 
    return Task.FromResult(IsSatisfiedBy(candidate)); 
    } 
    protected abstract bool IsSatisfiedBy(object candidate); 
} 

複合材料は、次のようになります。

public class AndSpecification : ISpecification 
{ 
    ... 

    public async Task<bool> IsSatisfiedByAsync(object o) 
    { 
    return await leftCondition.IsSatisfiedByAsync(o) && await rightCondition.IsSatisfiedByAsync(o); 
    } 
} 

public static class SpecificationExtensions 
{ 
    public static ISpecification And(ISpeicification @this, ISpecification other) => 
     new AndSpecification(@this, other); 
} 
0123などの

と、個々の仕様:

public class FileExistsSpecification : ISpecification 
{ 
    public async Task<bool> IsSatisfiedByAsync(object o) 
    { 
    return await _fileStorage.FileExistsAsync(addRequest.FileName); 
    } 
} 

public class FileNameSpecification : SynchronousSpecification 
{ 
    private static readonly char[] _invalidEndingCharacters = { '.', '/' }; 

    public override bool IsSatisfiedBy(object o) 
    { 
    var request = (AddRequest)o; 
    if (string.IsNullOrEmpty(request.FileName)) 
     return false; 
    if (request.FileName.Length > 1024) 
     return false; 
    if (request.FileName.Contains('\\') || _invalidEndingCharacters.Contains(request.FileName.Last())) 
     return false; 
    return true; 
    } 
} 

使用法:あなたは絶対に非同期の仕様をサポートする必要がある場合

var validations = new FileNameSpecification().And(new FileExistsSpecification()); 
if (await validations.IsSatisfiedByAsync(addRequest)) 
{ ... } 
-1

なぜ私は同期駆動パターンで非同期操作が必要かわかりません。

最初の結果がfalseで、2つ以上の非同期チェックがあるとしたら、パフォーマンスが浪費されるとします。

あなたが戻って同期した非同期要求を取得する方法を、知りたい場合は、以下を使用しようとすることができます:

public class FileExistsSpecification : CompositeSpecification 
{ 
    public override bool IsSatisfiedBy(object o) 
    { 
     var addRequest = (AddRequest)o 
     Task<bool> fileExistsResult = _fileStorage.FileExistsAsync(addRequest.FileName); 
     fileExistsResult.Wait(); 

     return fileExistsResult.Result; 
    } 
} 

またジェネリックアプローチを使用する必要があります。

+0

_最初の結果がfalseで、2つ以上の非同期チェックがあると、それは無駄でしょう。<= "パフォーマンス"は異なることを意味します。完了までの時間は明らかにここで優先されます(そうでなければ、長時間実行される仕様評価を連続的に実行するだけです)。システムがピーク時の使用量を下回っている場合は、電気以外の無駄な処理もありません。 また、2つの作業のうち最も早い段階から早期に戻ると、もう一方の作業が中止され、無駄を避けることができます。実際、2つの同期操作の最初の操作が成功した場合、もう一方が失敗した場合は常に「無駄」になります。 – Iucounu

+0

たとえば、詳細な複合エラーメッセージが役に立つかもしれない状況があるかもしれません - ブールを返す代わりに(結果的にブールとして暗黙的にオーバーロードされる) 'Result'タイプでこれを行いました – Julian

-1

ここでの主な目標は、複合仕様を評価するためにできるだけ早くコードが終了するようにすることです。子仕様を実行すると1つまたは複数の時間がかかることがありますか?パターン実装の外部でコードを呼び出すと、仕様を非同期に呼び出すことが常に可能です。その時点であなたの懸念事項ではありません。

だから、あなたのISpecificationに余分なプロパティを与えるのはどうですか?

public interface ISpecification 
{ 
    bool IsAsynchronous { get; } 
    bool IsSatisfiedBy(object o); 
} 

次に、非コンポジット同期または非同期タイプの仕様では、IsAsynchronousの戻り値をハードコードします。しかし、複合のもので、子供たちにそれをベースに、ウィットに:

public class AndSpecification : ISpecification 
{ 
    private ISpecification left; 
    private ISpecification right; 

    public AndSpecification(ISpecification _left, ISpecification _right) 
    { 
     if (_left == null || _right == null) throw new ArgumentNullException();   
     left = _left; 
     right = _right; 
    } 

    public bool IsAsynchronous { get { return left.IsAsynchronous || right.IsAsynchronous; } 

    public override bool IsSatisfiedBy(object o) 
    { 
     if (!this.IsAsynchronous)    
      return leftCondition.IsSatisfiedBy(o) && rightCondition.IsSatisfiedBy(o); 

     Parallel.Invoke(
      () => { 
       if (!left.IsSatisfiedBy(o)) return false; 
      }, 
      () => { 
       if (!right.IsSatisfiedBy(o)) return false; 
      } 
     ); 

     return true; 
    } 
} 

しかし、ビットさらにこれを取って、あなたはパフォーマンスを無駄にしたくありません。なぜなら、同期と非同期が1つの場合、最初に高速で同期の子を評価しないのはなぜですか?ここでは基本的な考え方の近いツー完成版です:

public class AndSpecification : ISpecification 
{ 
    private ISpecification left; 
    private ISpecification right; 

    public AndSpecification(ISpecification _left, ISpecification _right) 
    { 
     if (_left == null || _right == null) throw new ArgumentNullException();   
     left = _left; 
     right = _right; 
    } 

    public bool IsAsynchronous { get { return left.IsAsynchronous || right.IsAsynchronous; } 

    public override bool IsSatisfiedBy(object o) 
    { 
     if (!left.IsAsynchronous) 
     { 
      if (!right.IsAsynchronous) 
      { 
       return left.IsSatisfiedBy(o) && right.IsSatisfiedBy(o); 
      } 
      else 
      { 
       if (!left.IsSatisfiedBy(o)) return false; 
       return right.IsSatisfiedBy(o); 
      } 
     } 
     else if (!right.IsAsynchronous) 
     { 
      if (!right.IsSatisfiedBy(o)) return false; 
      return left.IsSatisfiedBy(o); 
     } 
     else 
     { 
      Parallel.Invoke(
       () => { 
        if (!left.IsSatisfiedBy(o)) return false; 
       }, 
       () => { 
        if (!right.IsSatisfiedBy(o)) return false; 
       } 
      ); 

      return true; 
     } 
    } 
}