InversionOfControl在一个新的MVC3项目

本文关键字:MVC3 项目 一个 InversionOfControl | 更新日期: 2023-09-27 18:16:49

我正在使用c#, MVC3,实体框架4建立一个电子商务网站,我第一次尝试MVC3和实体框架,所以我想确保一个健全的体系结构。特别是,我正在质疑我对接口和依赖倒置的使用,因为它们属于服务和存储库层。为了简洁明了,我将集中讨论系统的一个方面,即购物车系统。

下面是一个接口反转的例子,PluralSite用它来解释在没有考虑适当依赖的情况下创建接口的典型趋势。

假设你有一个"BoxingMatch"类依赖的IKangaroo接口。当你添加更多的"拳击手",如iiketyson和IJoeBoxer,你现在有三个不同的接口,一个对应一个拳击手,"BoxingMatch"需要知道这些接口。IKangaroo、iiketyson和IJoeBoxer各自只有一个具体的实现,这意味着您甚至不需要这些接口(您也可以让BoxingMatch直接依赖于具体的Kangaroo、MikeTyson和JoeBoxer类)。此外,IKangaroo或iiketyson有多个实现是没有意义的。所以接口是不必要的,不会给架构带来任何价值。

颠倒本例中的依赖关系将导致"BoxingMatch"定义其将要使用的类(Kangaroo, MikeTyson和JoeBoxer)将要实现的接口。因此,"BoxingMatch"将依赖于IBoxer接口,而Kangaroo、MikeTyson和JoeBoxer都将实现IBoxer。这是倒置,这很有意义。

现在,我的情况…CartController构造函数注入了两个依赖参数(ICartService和IProductService)。CartService接受一个注入的构造函数参数(ICartRepository)。CartController在CartService上调用AddItem(), CartService在cartrerepository上调用AddItem()。

ICartRepository有两个实现:CartRepository(在主Web项目中)和TestCartRepository(在测试项目中)。ICartService只有一个实现。

我的问题是:我的架构如何与上面例子中的教训相结合?我真的看不出我的CartController如何能比它和CartService更少耦合。控制器只依赖于CartService,而不是icartrerepository。所以我似乎不能在这里通过让CartController定义CartService和ICartRepository将要使用的接口来反转控制,因为CartService和CartRepository是完全不同的层。我说的对吗?

下一级,同样的问题。CartService依赖于cartrerepository。上面的倒置原则在这里适用吗?或者我已经通过在CartService构造函数中要求一个ICartRepository注入的参数来反转依赖关系了吗?

所以我的问题是,我做的"对"吗?

如有任何想法或建议,我将不胜感激。

我的代码,供参考:

CartController:

//constructor
public CartController(ICartService cartService, IProductService productService)
{
    _cartService = cartService;
    _productService = productService;
}
public RedirectToRouteResult AddItem(Cart cart, int productId)
{
    var product = _productService.GetProduct(productId);
    if (product != null)
    {
        _cartService.AddItem(cart, product, 1);
    }
    return RedirectToAction("Index");
}

CartService(实现):

//constructor
public CartService(ICartRepository repository)
{
    _repository = repository;
}
public void AddItem(Cart cart, Product product, int quantity)
{
    //simplified for brevity
    var cartProduct = _repository.CartProducts().SingleOrDefault(cp => cp.CartId == cart.CartId && cp.ProductId == product.ProductId);
    _repository.AddCartItem(cartProduct);
}

购物车存储库(实现):

public void AddCartItem(CartProduct cartProduct)
{
    _context.CartProducts.Add(cartProduct);
    _context.SaveChanges();
}

InversionOfControl在一个新的MVC3项目

您似乎错误地认为使用IoC的唯一原因是提供多个实现。事实上,这是使用IoC最没用的原因之一。

通过使用IoC容器,你可以管理对象的生命周期(例如,通过使对象在单个web请求的生命周期内存活)。

IoC容器还处理递归依赖。如果你创建了一个iiketyson,而iiketyson依赖于IBoxingShorts,那么你就不需要实例化拳击短裤,它们是免费的。

所有这些都忽略了使用IoC的重要原因,那就是使单元测试更容易。通过使用接口,您可以为BoxingMatch的单元测试提供模拟的IMikeTysons,这样您就可以为BoxingMatch提供已知值,而不必在每次测试后重置数据库。

而且,IoC和基于接口的编程还带来了大约100个其他好处。

我觉得你想太多了。您只需要传递所依赖的实例,而不必过于担心IoC的学术定义。

这可能不能回答你所有的问题,但是如果在某些时候你确实打算在控制器层和服务层之间分离过程,例如通过使用WCF,那么你的I*服务将成为WCF服务契约——这将加强通过接口耦合层的决定,而不是直接耦合到类。

在这种情况下,可以添加额外的facade/间接性,通常称为服务代理,从而对控制器隐藏"确切的"SOA服务接口。这将允许服务层被一个具有类似功能的层取代,但使用不同的服务接口。

你的反转看起来很好-你的层依赖于抽象(接口)并隐藏实现,所以*服务不会意识到存储库使用实体框架,NHibernate或数据集,控制器也不会知道服务层使用存储库,或直接访问数据库(注入构造函数不会暴露在该层的客户端所使用的接口上,所以这里不用担心)。