可以使用设计模式重构此MVC代码吗?

RPM*_*984 8 c# asp.net-mvc events design-patterns asp.net-mvc-3

我的ASP.NET MVC 3站点上都有这样的控制器代码:

[HttpPost]
public ActionResult Save(PostViewModel viewModel)
{
   // VM -> Domain Mapping. Definetely belongs here. Happy with this.
   var post = Mapper.Map<PostViewModel, Post>(viewModel);

   // Saving. Again, fine. Controllers job to update model.
   _postRepository.Save(post);

   // No. Noooo..caching, thread spawning, something about a user?? Why....
   Task.Factory.StartNew(() => {
       _cache.RefreshSomeCache(post);
       _cache2.RefreshSomeOtherCache(post2);
       _userRepository.GiveUserPoints(post.User);
       _someotherRepo.AuditThisHappened();
   });

   // This should be the 3rd line in this method.
   return RedirectToAction("Index");
}
Run Code Online (Sandbox Code Playgroud)

基本上,我指的是线程块中的代码.所有事情都需要发生,但用户不需要等待它们(对于后台线程来说是好的,对吧?).

为了清楚起见,我在整个站点使用缓存(常规ASP.NET数据缓存),其中大部分都有"无过期"缓存策略,因此我在需要时手动逐出(如上所述).

用户部分基本上是为用户代表做某事(比如Stack).

让我们回顾一下:我们有缓存,用户信誉处理,审计,一体化.真的不属于一个地方吗.因此,当前代码的问题,以及试图找出如何移动它的问题.

我想重构这个的原因有以下几个原因:

  1. 难以进行单元测试.多线程和单元测试并不是很好.
  2. 可读性.这很难读.乱.
  3. SRP.控制器做得/知道太多.

我解决了1)将线程产生的代码包装到一个接口中,然后只是嘲笑/伪造它.

但我想做某种模式,我的代码看起来像这样:

[HttpPost]
public ActionResult Save(PostViewModel viewModel)
{
   // Map.
   var post = Mapper.Map<PostViewModel, Post>(viewModel);

   // Save.
   _postRepository.Save(post);

   // Tell someone about this.
   _eventManager.RaiseEvent(post);

   // Redirect.
   return RedirectToAction("Index");
}
Run Code Online (Sandbox Code Playgroud)

基本上,把责任放在"别的东西"上做出反应,而不是控制器.

我听说过有关任务,命令,事件等的内容,但还没有看到在ASP.NET MVC空间中实现的内容.

初步想法会告诉我创建某种"事件管理器".但后来我想,这是怎么回事?在域名?那么它如何处理与缓存的交互,这是一个基础设施问题.然后是线程,这也是一个基础设施问题.如果我想要做的是同步,而不是异步?是什么决定的?

我不想只是把所有这些逻辑都堆积在其他地方.理想情况下,它应该被重新考虑到可管理和有意义的组件,而不是转移责任,如果这是有道理的.

有什么建议?

jga*_*fin 2

第一个想法会告诉我创建某种“事件管理器”。但后来我想,这会去哪里呢?在域中?

这就是我解决问题的方法。我将事件管理器视为基础设施。但实际事件属于领域。

那么它如何处理与缓存的交互,这是一个基础设施问题。然后是线程,这也是一个基础设施问题。如果我想做的是同步而不是异步怎么办?是什么做出了这个决定?

异步很好,但使事务处理变得复杂。如果您使用 IoC 容器,您已经拥有明确定义的范围和可在事件传播期间使用的事务。

恕我直言,如果订阅者知道事件处理需要时间,则由订阅者来安排/线程其任务。

建议的解决方案:

使用 IoC 容器发布事件。我会让存储库发布事件(或者PostUpdated取决于EntityUpdated您想要对事件执行的操作)而不是控制器(以减少代码重复)。

我为 autofac 做了一个 IoC 实现,它允许您:

DomainEventDispatcher.Current.Dispatch(new EntityUpdated(post));
Run Code Online (Sandbox Code Playgroud)

订阅:

public class CacheService : IAutoSubscriberOf<EntityUpdated>
{
    public void Handle(EntityUpdated domainEvent) {};
}
Run Code Online (Sandbox Code Playgroud)

https://github.com/sogeti-se/Sogeti.Pattern/wiki/Domain-events

典型用法

  1. 实施IServiceResolver(针对您的容器)
  2. 分配它:ServiceResolver.Assign(new yourResolver(yourContainer))
  3. 按照此处所述使用。