2014-04-30 41 views
0

我想弄清楚在我的服務層和WebApi控制器之間獲取錯誤消息的最佳解決方案。正確使用工廠模式?

我有一個類ModelStateDictionaryWrapper實現接口IValidationDictionary

ModelStateDictionaryWrapper

public class ModelStateDictionaryWrapper : IValidationDictionary 
{ 
    private readonly ModelStateDictionary modelStateDictionary; 

    public bool IsValid 
    { 
     get 
     { 
      return this.modelStateDictionary.IsValid; 
     } 
    } 

    public ModelStateDictionaryWrapper(ModelStateDictionary modelStateDictionary) 
    { 
     Enforce.ArgumentNotNull(modelStateDictionary, "modelStateDictionary"); 

     this.modelStateDictionary = modelStateDictionary; 
    } 

    public void AddError(string key, string message) 
    { 
     this.modelStateDictionary.AddModelError(key, message); 
    } 
} 

IValidationDictionary

public interface IValidationDictionary 
{ 
    bool IsValid { get; } 
    void AddError(string key, string message); 
} 

在我的API控制,我這樣做:

public class CategoryController : ControllerBase<ICategoryService> 
{ 
    private ICategoryService categoryService; 


    public CategoryController(ICategoryService categoryService) 
    { 
     this.categoryService = categoryService; 
     this.categoryService.ValidationDictionary = 
      new ModelStateDictionaryWrapper(this.ModelState); 
    } 

    public IEnumerable<CategoryViewModel> Get() 
    { 
     return Mapper.Map<CategoryViewModel[]>(this.Service.GetCategories()); 
    } 
} 

我的問題,這是我在服務的構造作出新的ModelStateDictionaryWrapper和我不喜歡這一點。

所以我想改變這個,採取一廠,像這樣:

public interface IModelStateWrapperFactory 
{ 
    IValidationDictionary GetModelStateWrapper(ModelStateDictionary modelStateDictionary); 
} 

public class ModelStateWrapperFactory : IModelStateWrapperFactory 
{ 
    public IValidationDictionary GetModelStateWrapper(
     ModelStateDictionary modelStateDictionary) 
    { 
     return new ModelStateDictionaryWrapper(modelStateDictionary); 
    } 
} 

然後API的控制器是這樣的(構造函數):

public CategoryController(ICategoryService categoryService, 
    IModelStateWrapperFactory modelStateWrapperFactory) 
    { 
     this.categoryService = categoryService; 
     this.categoryService.ValidationDictionary = 
      modelStateWrapperFactory.GetModelStateWrapper(this.ModelState); 
    } 

我想我已刪除緊密的耦合。這看起來是一個很好的解決方案嗎?

回答

0

更好的方法是將這部分完全從控制器中刪除。它應該被移出控制器,因爲:

  • 這實際上是一個橫切關注點,您的控制器不應該關心它;你違反了單一責任原則。
  • 你的控制器大多數(如果不是全部的話)將需要這個構造,這意味着你必須在整個地方重複它,使得在某些地方很容易忘記它;你違反了不要重複自己(DRY)的原則。
  • 此構造僅在需要驗證的類直接注入到控制器的情況下才有可能,但並非總是如此。有時候你需要在對象圖的更深處進行驗證,或者你可能想用一個裝飾器或攔截器來包裝服務,這使得這種方法無用 - 或者至少非常麻煩。

這種方法有幾種解決方案。第一個是在我腦海中是移動ModelState的設立,走出CategoryController的構造函數,例如:

public IHttpController Create(HttpRequestMessage request, 
    HttpControllerDescriptor descriptor, Type type) 
{ 
    var wrapper = new ModelStateDictionaryWrapper(); 

    var controller = new CategoryController(new CategoryService(wrapper)); 

    wrapper.ModelState = controller.ModelState; 

    return controller; 
} 

另一個 - 完全不同 - 方法是不使用ModelState酒店在所有,但讓您的業務層拋出特定的驗證異常,並將它們捕獲到調用堆棧的更高層,並將它們轉換爲Web API狀態碼。

投擲異常對於業務層來說是一個更好的方法,因爲這可以防止驗證錯誤被忽視。此外,填充字典時出現驗證錯誤的設計與Web API和MVC相關,並不是您的業務層應該關注的內容。

你可以在你的控制器以下時,你的BL拋出驗證異常:

public class CategoryController : ControllerBase<ICategoryService> 
{ 
    private ICategoryService categoryService; 

    public CategoryController(ICategoryService categoryService) 
    { 
     this.categoryService = categoryService; 
    } 

    public HttpResponseMessage Update(CategoryViewModel model) 
    { 
     try 
     { 
      this.categoryService.Update(model.Category); 
     } 
     catch (ValidationException ex) 
     { 
      return WebApiValidationHelper.ToResponseCode(ex); 
     } 
    } 
} 

下行這裏當然是用電話向WebApiValidationHelper.ToResponseCode你的try-catch語句將在所有的控制器被複制的;你會違反DRY。

因此,您可以改爲將此代碼提取到DelegatingHandler中。我的首選項總是使用裝飾器,但不幸的是Web API使它不可能裝飾ApiControllers,due to a quirk in its design。所以,你可以注入以下DelegatingHandler到Web API管線:

public class ValidationDelegationHandler : DelegatingHandler 
{ 
    protected override async Task<HttpResponseMessage> SendAsync(
     HttpRequestMessage request, CancellationToken cancellationToken) 
    { 
     try 
     { 
      return await base.SendAsync(request, cancellationToken); 
     } 
     catch (ValidationException ex) 
     { 
      return WebApiValidationHelper.ToResponseCode(ex); 
     } 
    } 
} 

該處理器可以被注入如下:

config.MessageHandlers.Add(new ValidationDelegationHandler());