新的MVC3项目中的InversionOfControl

时间:2011-12-06 06:40:39

标签: c# asp.net-mvc-3 inversion-of-control

我正在使用C#,MVC3,Entity Framework 4构建一个电子商务网站,这是我第一次尝试使用MVC3和Entity Framework,所以我想确保一个健全的架构。特别是,我正在质疑我对Interfaces和Dependency Inversion的使用,因为它们与Services和Repository层有关。为了简洁和清晰起见,我将专注于系统的一个区域,即推车系统。

这是一个Interface Inversion示例,PluralSite用于解释在不考虑适当依赖性的情况下创建接口的典型趋势。

假设您有一个“BoxingMatch”类依赖的IKangaroo界面。当你添加更多“拳击手”时,比如IMikeTyson和IJoeBoxer,你现在有三个不同的界面,每个拳击手一个,“BoxingMatch”需要知道。 IKangaroo,IMikeTyson和IJoeBoxer每个只有一个具体的实现,这意味着你甚至不需要那些接口(你也可以让BoxingMatch直接依赖于具体的袋鼠,MikeTyson和JoeBoxer类)。此外,IKangaroo或IMikeTyson的实施方案甚至没有意义。因此接口是不必要的,不会给架构带来任何价值。

在此示例中反转依赖项将导致“BoxingMatch”定义将要使用的类(Kangaroo,MikeTyson和JoeBoxer)将要实现的接口。因此,“BoxingMatch”将依赖于IBoxer接口,Kangaroo,MikeTyson和JoeBoxer都将实施IBoxer。有反转,它很有道理。

现在,我的情况...... CartController构造函数注入了两个依赖参数,ICartService和IProductService。 CartService采用单个注入的构造函数参数(ICartRepository)。 CartController在CartService上调用AddItem(),CartService在CartRepository上调用AddItem()。

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

我的问题:我的架构是如何与上述示例中的课程相结合的?我真的没有看到我的CartController如何比CartService更少耦合。控制器仅依赖于CartService,而不依赖于ICartRepository。因此,我可以通过让CartController定义哪个接口CartService和ICartRepository将使用来反转控制,因为CartService和CartRepository完全是不同的层。我是对的吗?

降低一级,同样的问题。 CartService依赖于CartRepository。上述反演原则是否适用于此?或者我已经通过在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();
}

2 个答案:

答案 0 :(得分:4)

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

通过使用IoC容器,您可以管理对象生存期(例如,通过在单个Web请求的生命周期中使对象生效)。

IoC容器还处理递归依赖项。如果你创建一个IMikeTyson并且IMikeTyson依赖于IBoxingShorts,那么你不必实例化拳击短裤,它们是免费的。

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

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

我认为你正在过度思考购物车的情景。您只需要传递您所依赖的实例,而不必过分担心IoC的学术定义。

答案 1 :(得分:1)

这可能无法解答您的所有问题,但如果您在某个时候打算将Controller和Service层之间的流程分开,例如通过使用WCF,您的I *服务将成为WCF服务合同 - 这将加强通过接口耦合图层而不是直接耦合到类的决定。

在这种情况下,可以添加一个额外的Facade / indirection,通常称为服务代理,从而对控制器隐藏“确切的”SOA服务接口。这将允许服务层被类似功能之一替换,但使用不同的服务接口。

您的反转看起来很好 - 您的图层依赖于抽象(接口)并隐藏实现,因此* Service不会意识到存储库使用了Entity Framework,NHibernate或DataSets,Controller也不会知道服务层使用存储库,或直接访问数据库(注入构造函数不会暴露在图层客户端使用的接口上,因此不用担心)。