我會強烈反對忠告使用這樣的結構。由於幾個原因:
- 它不是控制器(或在控制器裝飾屬性上)提交數據上下文的責任。
- 這會導致大量重複的代碼(您將不得不使用該屬性修飾大量方法)。
- 同時在執行(在
OnActionExecuted
法)該點是否是實際安全提交數據。
特別是第三點,應該已經引起你的注意。模型是有效的這一事實並不意味着提交數據上下文的變化是可以的。看看這個例子:
[UnitOfWorkAttribute]
public View MoveCustomer(int customerId, Address address)
{
try
{
this.customerService.MoveCustomer(customerId, address);
}
catch { }
return View();
}
當然這個例子有點幼稚。你幾乎不會吞下每一個異常,那簡直是錯誤的。但它所表現的是,當數據不應被保存時,動作方法很可能成功完成。
但除此之外,提交事務真的是MVC的一個問題,如果你決定這個問題,你是否仍然想用這個屬性來修飾所有的操作方法。如果你只是在不需要在控制器層面上做任何事情的情況下實現這一點,會不會更好?因爲,在這之後你會添加哪些屬性?授權屬性?記錄屬性?追蹤屬性?它停在哪裏?
您可以嘗試的方法是對所有需要在事務中運行的業務操作進行建模,以允許您動態添加此行爲,而無需更改任何現有代碼或在整個過程中添加新屬性地點。一種方法是爲這些業務操作定義一個接口。例如:
public interface ICommandHandler<TCommand>
{
void Handle(TCommand command);
}
使用這個接口,您的控制器是這樣的:
private readonly ICommandHandler<MoveCustomerCommand> handler;
// constructor
public CustomerController(
ICommandHandler<MoveCustomerCommand> handler)
{
this.handler = handler;
}
public View MoveCustomer(int customerId, Address address)
{
var command = new MoveCustomerCommand
{
CustomerId = customerId,
Address = address,
};
this.handler.Handle(command);
return View();
}
對於系統中的每個業務操作你定義一個類(一個DTO和Parameter Object)。在例子MoveCustomerCommand
類。這個類只包含數據。該實現是在一個實現了ICommandHandler<MoveCustomerCommand>
的類中定義的。例如:
public class MoveCustomerCommandHandler
: ICommandHandler<MoveCustomerCommand>
{
private readonly IDataContext context;
public MoveCustomerCommandHandler(IDataContext context)
{
this.context = context;
}
public void Handle(MoveCustomerCommand command)
{
// TODO: Put logic here.
}
}
這看起來像一個可怕的很多額外的無用代碼,但其實這是非常有用的(如果你仔細看,它是不是真的那麼多額外的代碼反正)。
有趣的,這是你現在可以定義處理所有的命令處理程序在系統中的交易一個單一的裝飾:
public class TransactionalCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly IDataContext context;
private readonly ICommandHandler<TCommand> decoratedHandler;
public TransactionalCommandHandlerDecorator(IDataContext context,
ICommandHandler<TCommand> decoratedHandler)
{
this.context = context;
this.decoratedHandler = decoratedHandler;
}
public void Handle(TCommand command)
{
this.decoratedHandler.Handle(command);
this.context.SubmitChanges();
}
}
這不是太多的代碼比你UnitOfWorkAttribute
,但不同的是,這個處理程序可以被包裝在任何實現中並注入任何控制器,而無需控制器知道這一點。執行命令後直接執行是真正的唯一安全的地方,您可以真正知道是否可以保存更改。
您可以找到有關這篇文章中設計應用程序的這種方式的更多信息:Meanwhile... on the command side of my architecture
很好的答案,博客文章,這需要重寫整個應用程序,所以它可能不會在目前的答案,但它提供了很好的食物的想法。 – Giedrius
對於我而言,如果沒有看到您的應用程序代碼,我很難說這些,但在大多數情況下,添加此設計並不難。您可以逐步介紹它,並通過讓處理程序簡單地傳遞數據開始。然而,這可能是一個很大的轉變。 – Steven
也google搜索「不使用CQRS」給出了很多的想法也一樣,所以我想我需要自己試試吧,看看它如何適應。 – Giedrius