12

我只需要一些我可以阅读的文章的链接或一些关于 MVC (C#) 中使用的不同模式的基本解释。

目前我倾向于使用视图模型模式构建我的 Web 应用程序。对于每个视图,我都有一个视图模型。我喜欢这种方法纯粹是因为模型中可能不需要太多垃圾,我可以在这里使用一些基本的数据注释。

我现在还在视图模型本身内构建我的视图模型(不确定这是否正确?),以便我可以使我的控制器尽可能简单。

但是有时我发现自己在控制器中添加了很多逻辑,我认为这对我来说也很好,这就是控制器的用途。

现在基于上述内容,正如我所说,我可以非常愉快地构建我的应用程序,而不会出现任何重大问题。然而,在我正常浏览代码示例等时,我经常发现不同的开发人员使用了很多其他方法来完成我上面所做的事情,我想解释一下它们都适合在一起。

我经常看到提到“使用你的存储库来做废话”。我确实“有时”使用存储库,但这主要是用于模型查询,我知道我将来会重新使用它,它总是变成有点垃圾场。这里的最佳做法是什么?

我还看到提到的“接口”和“服务层”我在这里完全迷失了.. 对我来说,大多数示例似乎只是添加越来越多的步骤来实现相同的目标。如何/为什么使用它们?

4

3 回答 3

5

我不能说这是最佳实践,但这就是我使用的方法,以及为什么,我们开始吧:


1. 存储库。

它们的结构是这样的:

共有三个基本接口IRead<>IReadCreate<>IReadCreateDelete<>

interface IRead<T>
{ 
    T FindOne(int id);
    IQueryable<T> GetOne(int id);
    IQueryable<T> FindAll(Expression<Func<T, bool>> predicate);
}

interface IReadCreate<T> : IRead<T>
{ 
    T Create();
    void Create(T entity);
}

interface IReadCreateDelete<T> : IReadCreate<T>
{ 
    void Delete(int id);
    void Delete(T entity);
    void DeleteWhere(Expression<Func<T, bool>> predicate);
}

所有其他接口如下所示:

interface ICategoriesRepository : IReadCreate<Category>
{
    IQueryable<Category> GetAllActive();
}

它们都在它们所依赖的数据源上提供了额外的有用功能。这意味着,我无法访问我的实现存储库中的其他类型存储库。这应该在Services上完成。(往下看。)

这种方法的主要目标是显示调用代码(来自另一个程序集,因为我所有的存储库、服务和其他合同都在单独的 DLL 项目中定义(作为接口))它可以做什么(比如读取和创建项目)以及什么它不能做(比如删除项目)。


2. 服务

服务和实现业务逻辑的最佳方式。他们应该实现所有重要的逻辑方法。为了实现这种实现,他们需要一些存储库依赖,而Dependency Injector. 我更喜欢使用Ninject,因为它允许我像这样注入依赖属性:

internal class CategoriesService : ICategoryService
{
    public ICategoriesRepository CategoriesRepository { get; set; }
    public IWorkstationsRepository WorkstationsRepository { get; set; }

    // No constructor injection. I am too lazy for that, so the above properties 
    // are auto-injected with my custom ninject injection heuristic.

    public void ActivateCategory(int categoryId)
    {
        CategoriesRepository.FindOne(categoryId).IsActive = true;
    }
}

服务的目标是从控制器和存储库中消除业务逻辑。


3. 视图模型

很酷的事情,正如你所说,但原因是你为什么要自己建立它们,这是我无法理解的。我为此使用了自动映射器(带有可查询的扩展),它允许我创建如下视图:

假设我有一个需要模型的视图。IEnumerable<TicketViewModel>我要做的是:

public class FooController : Controller
{
     public IMappingEngine Mapping { get; set; } // Thing from automapper.
     public ITicketsRepository TicketsRepository { get; set; }

     public ViewResult Tickes()
     { 
         return View(TicketsRepository.GetAllForToday().Project(Mapping)
             .To<TicketViewModel>().ToArray();
     }
}

就是这样。对存储库的简单调用,它调用底层数据源(另一种模式。我不会写它,因为它的抽象仅用于测试。),它调用数据库(或你实现的任何东西IDataSource<T>)。Automapper 自动映射TickettoTicketViewModel和 form 数据库,我检索了我的 ViewModel 列所需的唯一数据库,包括单个请求中的交叉表


结论

还有很多要说的,但我希望这能给你一些思考。我使用的所有模式和程序都是:

  1. 自动映射器(映射);
  2. Ninject(依赖注入);
  3. 存储库(数据访问);
  4. 数据源(从数据源中读取的数据);
  5. 服务(数据交互);
  6. ViewModels(数据传输对象);
  7. 也许我会编辑添加其他内容。
于 2012-12-15T08:37:01.020 回答
3

当我开始阅读您的帖子时,我在想您可能正在寻找的是对 SOLID 原则的理解。最后提到接口和服务层。有趣的。

有很多文章庆祝 SOLID 和 DRY 的圣杯(许多不了解 DRY 倡导者真正提出的建议)。但是 .NET 世界中的一般想法是,不要转到 aspx 中自动生成的 Page_Load 并开始随意输入所有内容,直到页面完成它应该做的事情。MVC 来救援。

你说你有每个视图的模型。我会称之为声音。即使两个模型相同,它们也只是相等,而不是相同。例如:NewsItem 不是 EventItem。如果你想扩展一个,它不应该影响另一个。

然后您继续说您正在视图模型本身中生成模型。这听起来倒退了。但是您说这样做是为了保持控制器清洁。好的!您的思维方式中缺少的是服务。

您要做的是将实际执行任何类型工作的所有代码移动到服务中。服务可以基于一个方面、一个特性或为什么不基于一个控件。现在看一个 Web 项目,我看到:VisitorService、NewsfeedService、CalendarService、CachingService、MainMenuService、HeaderService、FooterService 等等等等。

在这种情况下,控制器只负责向执行某些工作的服务(或多个服务)请求模型。然后将该模型转发到视图。

一旦您将“业务逻辑”融入服务,您就可以轻松地将 IoC(控制反转)应用到您的项目中,如果这让您满意的话。我还没有对 IoC 投票。我有一种令人毛骨悚然的好处,它的好处并不像广告宣传的那么大,而且你可以肯定地没有代码膨胀。但 IoC 确实要求您在编写代码之前进行思考。

For a very easy going tutorial on IoC, I recommend Ninject. Not only does it feature Ninjas, but samurais, swords and shuriken as well. That's a lot cooler than cars and animals.

https://github.com/ninject/ninject/wiki/Dependency-Injection-By-Hand

于 2012-12-15T08:52:12.370 回答
2

控制器:

从理论上讲,您的控制器应该只处理“数据”。将信息片段从一个地方移动到另一个地方。

小例子:

  1. 控制器接收带有一些参数的请求“GetMessage”。
  2. 将此数据发送到服务层。在服务层中,您正在访问存储库返回消息。
  3. 控制器接收到这个消息(或者如果没有空值)并决定它是否应该将收到的消息发回,或者可能存在错误并且应该以某种方式通知用户。

所有“理论上”的业务逻辑都应该在某个服务层之后。这样您就可以轻松地测试所有内容。控制器中的逻辑使一些测试更加困难。

接口:

基于界面的设计现在非常流行。尤其是所有处理依赖注入的 IOC 容器。但是,如果您从这个概念开始,请不要担心这些关键字。如果您知道存储库模式,那么首先尝试使用 IRepository 接口,而不是通过具体类访问存储库,而是使用 IRepository。(只需将控制器中的字段从 Repository 更改为 IRepository)。

一般关于接口

您将在更复杂的场景中看到接口的好处,但是有一种技术可以向您展示这种方法的所有荣耀。单元测试+模拟。

于 2012-12-15T08:43:54.200 回答