ASP.NetMVCサイトに検証を実装するための戦略に取り組んできました。過剰なエンジニアリングのリスクを冒して、どのプロジェクトでも一貫して展開できる緩いカップルの実装を開発しようとしています。すべての可動部分を考慮して、私はSOの人々に、改善についての意見や考えがあるかどうかを確認するように依頼したいと思いました。コードは明らかに考案されたものであり、すべてがどのように連携しているかを理解したかっただけです。
関心のある可動部分:
- データアクセス用のEFを備えたリポジトリレイヤー
- 入力検証のためのモデルデータ注釈
- ビジネスルール検証のためのサービスレイヤー
- Unity for DI
単一のコントローラーアクション中に同じEFコンテキストを使用したい場合、作業単位パターンを使用して、コントローラー内の複数のサービスに同じDataContectを注入します。
public class OrderController : Controller
{
private IUnitOfWork _unitOfWork;
private IOrderService _recipeService;
private IInventoryService _inventoryService;
public OrderController(IUnitOfWork unitOfWork, IOrderService orderService, IInventoryService inventoryService)
{
_unitOfWork = unitOfWork;
_orderService = orderService;
_inventoryService = inventoryService
//Use property injection to apply the Unit of Work context and validation state to our services
_orderService.Context = _unitOfWork;
_orderService.ValidationState = new ModelStateWrapper(this.ModelState);
_inventoryService.Context = _unitOfWork;
_inventoryService.ValidationState = new ModelStateWrapper(this.ModelState);
}
さらに工夫されたコードを続けて、たとえば、作成アクションで、製品の注文を作成し、その製品を在庫から削除したいとします。
public ActionResult Create(CreateEditOrderViewModel model)
{
try
{
Product product = Mapper.Map<ProductDTO, Product>(model.ProductDTO);
if(_orderService.Insert(product) &&
_inventoryService.Remove(product) &&
ModelState.IsValid)
{
_unitOfWork.Save();
return RedirectToAction("Index");
}
}
catch (DataException exc)
{
//Log the error (add a variable name after DataException)
ModelState.AddModelError("", "Unable to save changes, please check the log for errors.");
}
return View(model);
}
私のサービスでは、http://www.asp.net/mvc/tutorials/older-versions/models-(data)/ validating-with-a-service-layer-csに従ってビジネスルールの検証を行います:
public class OrderService : IOrderService
{
public bool Insert(Recipe orderToCreate)
{
// Validation logic
if (!ValidateOrder(orderToCreate))
return false;
// Database logic
try
{
_context.OrderRepository.Insert(orderToCreate);
}
catch
{
return false;
}
return true;
}
protected bool ValidateOrder(Order orderToValidate)
{
Product p = orderToValidate.Product;
//Ensure inventory has product before creating order
if (_context.InventoryRepository.HasProduct(p)
_validationState.AddError("Product", "That product cannot be added to the order as we don't have it in stock");
return _validationState.IsValid;
}
public IUnitOfWork Context
{
get
{
return _context;
}
set
{
_context = value;
}
}
public IValidationDictionary ValidationState
{
get
{
return _validationState;
}
set
{
_validationState = value;
}
}
}
そして、単純な注文モデルは次のようになります。
public class Order: IModel
{
[Key]
public int ID { get; set; }
[Required(ErrorMessage="A buyer is required.")]
public string Buyer { get; set; }
public virtual ICollection<Product> Products{ get; set; }
}
したがって、現状では、データアノテーションの検証はモデルのバインド中に行われ、ビジネスルールの検証はサービスのCRUDメソッドが呼び出されたときに行われます。サービスは、リポジトリへの参照を含む同じ作業単位オブジェクトを使用するため、すべてのサービスCRUDメソッドは同じEFコンテキスト内で実行され、トランザクションや同時実行性などの機能を提供します。
コントローラーで、作成アクション内の複数のサービスを呼び出しています。代わりに、OrderServiceを1回呼び出してから、InventoryService自体を呼び出す方が望ましいでしょうか。
各サービスに同じUoAオブジェクトが必要な場合、Unityを介してUnit of Workオブジェクトをサービスにアタッチする方法はありますか?サービスごとに異なるインスタンスになってしまうことのない方法を考えることはできませんでした。
誰かが何か考えや提案があれば、私はそれらを聞いてみたいです!
ありがとう!
クリス