规范模式异步

Mr.*_*kin 6 c# design-patterns domain-driven-design specification-pattern async-await

我正在尝试将规范模式应用于我的验证逻辑.但我在异步验证方面遇到了一些问题.

假设我有一个AddRequest需要验证的实体(有2个字符串属性FileName和Content).

我需要创建3个验证器:

  1. 验证FileName是否包含无效字符

  2. 验证内容是否正确

  3. 异步验证数据库中是否存在具有FileName的文件.在这种情况下,我应该有类似的东西Task<bool> IsSatisfiedByAsync

但我怎么能同时实现IsSatisfiedByIsSatisfiedByAsync?我应该创建2个接口ISpecification,IAsyncSpecification或者我可以在一个接口中创建吗?

我的版本ISpecification(我只需和)

    public interface ISpecification
    {
        bool IsSatisfiedBy(object candidate);
        ISpecification And(ISpecification other);
    }
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

要验证文件是否存在,我应该使用:

 await _fileStorage.FileExistsAsync(addRequest.FileName);
Run Code Online (Sandbox Code Playgroud)

IsSatisfiedBy如果我确实需要那个异步,我怎么能写那个支票呢?

例如,这里是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
    }
}
Run Code Online (Sandbox Code Playgroud)

我需要创建FileExistsSpecification并使用如下:

var validations = new FileNameSpecification().And(new FileExistsSpecification());
if(validations.IsSatisfiedBy(addRequest)) 
{ ... }
Run Code Online (Sandbox Code Playgroud)

但是,FileExistsSpecification如果我需要异步,我该如何创建呢?

Ste*_*ary 4

但如何同时实现 IsSatisfiedBy 和 IsSatisfiedByAsync?我应该创建 2 个接口(例如 ISpecification 和 IAsyncSpecification),还是可以在一个接口中完成此操作?

您可以定义同步和异步接口,但任何通用复合实现都必须仅实现异步版本。

由于接口上的异步方法意味着“这可能是异步的”,而同步方法意味着“这必须是同步的”,所以我会使用仅异步的接口,如下所示:

public interface ISpecification
{
  Task<bool> IsSatisfiedByAsync(object candidate);
}
Run Code Online (Sandbox Code Playgroud)

如果您的许多规范是同步的,您可以使用基类来提供帮助:

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

复合材料将是:

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);
}
Run Code Online (Sandbox Code Playgroud)

以及单独的规格:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

var validations = new FileNameSpecification().And(new FileExistsSpecification());
if (await validations.IsSatisfiedByAsync(addRequest))
{ ... }
Run Code Online (Sandbox Code Playgroud)