【问题标题】:ASP.NET MVC IoC usabilityASP.NET MVC IoC 可用性
【发布时间】:2010-05-05 06:07:38
【问题描述】:

您在实际项目中将 IoC 用于控制器/DAL 的频率如何?

IoC 允许将应用程序从具体实现中抽象出来,并带有应实现的附加接口层。但是具体的实现多久改变一次?如果实现几乎不会改变,我们真的应该做两次向接口添加方法的工作吗?我参与了大约 10 个 asp.net 项目,而 DAL(类似于 ORM 和非 ORM)从未被完全重写。

看了很多视频,我清楚地知道 IoC“很酷”,是一种非常好的编程方式,但它真的需要吗?

稍后添加: 是的,IoC 允许准备更好的测试环境,但我们也有很好的方法来测试没有 IoC 的 DAL。我们将对数据库的 DAL 调用包装到未提交的事务中,而不会有使数据不稳定的风险。

【问题讨论】:

  • 不,IoC 不会让你的应用程序更容易测试,你永远不会在测试中使用你的 IoC。然而,基于契约的编程(编程接口而不是实现)确实使您的应用程序更可测试。
  • 是的,我同意,这很容易。但我有强烈的感觉,我用这种过于以界面为中心的编程做了额外的工作,这太“学术”了,漂亮的闪亮功能不是很有帮助(虽然不是有害的)。
  • 一开始它可能是“额外的工作”,但是当你学会了如何使用你的 IoC 和基于合约的编程时,你会发现它在几乎所有情况下都会少很多工作。如果做得好,您可以设置很多约定,您将不得不在应用程序中编写更少的代码,并且您编写的代码将更具可读性和可维护性。我的建议是阅读如何使用您的 IoC 并对其进行一段时间的测试。你不会后悔的。
  • 谁能用一次国际奥委会的全名,这样我就不用查了?谢谢!

标签: asp.net-mvc inversion-of-control


【解决方案1】:

IoC 不仅仅是用于编写模块化程序的模式;它还允许更轻松的测试,因为它能够交换实现与它们所代表的组件相同的接口的模拟对象。

此外,它实际上使代码在以后更容易维护。

【讨论】:

  • 是的,但是例如,我可以在没有 IoC 的情况下测试 DAL。通常我将测试包装在事务中(即未提交的)并使用开发数据库数据而不会模拟和冒险使数据不稳定。
  • 那么您实际上是在进行集成测试,因为您正在接触数据库。
  • 为什么不呢?可能是通过模拟进行测试,而不是过多地模拟它?
  • 我不同意它使代码更易于维护,因为每次方法签名都发生变化(并且在项目开始时经常发生这种情况)我必须进行两次更改(接口 + 实现)。
  • @Andrew Florko,这是 IDE 将为您做的事情,您不必担心。您只需更改接口,您就有工具可以为您更改实现。
【解决方案2】:

不是 IOC 允许您从具有附加接口层的具体实现中抽象应用程序,这就是您应该如何设计应用程序以便更加模块化和可重用的方式。另一个重要的好处是,一旦您以这种方式设计了应用程序,就可以更轻松地单独测试不同的部分,而无需依赖具体的数据库访问。

【讨论】:

  • > 更加模块化和可重用。我怀疑 DAL 库本身是可重用的,因为 > 隔离而不依赖于具体的数据库访问,例如似乎(对我来说)是不切实际且几乎从未使用过的功能。 > 好处是一旦你以这种方式设计了你的应用程序,测试不同的部分就会容易得多 似乎是次要的,因为(如果我们谈论 DAL)我可以使用未提交的事务调用来测试 DAL。
  • @Andrew Florko,那么如果您对 DAL 有依赖关系,那么如何在不使用 DAL 的情况下测试您的 BLL?您的问题似乎更多是关于基于合同的编程,然后是 IoC 容器的使用。这是两个不同的东西。
  • 是的,我的 BLL 依赖于 DAL 实现。而且我从来没有遇到过这种依赖的问题(因为 DAL 的实现从未完全改变过)。单元测试涵盖了具有未提交事务调用的 DAL“奇异函数”以及 BLL“复杂函数”。
  • @Andrew Florko,那么你不能对你的 BLL 进行单元测试,因为它也会测试你的 DAL。如果你可以简单地弄乱你的 DAL(和所有其他依赖项),那么编写测试将变得更加困难。
  • 是的,我不能。但这真的有问题吗?你有没有遇到过这样的情况,这种方法会导致不可预测的结果(不是理论上,而是在实践中)?每次我向合约接口添加方法时,我都必须创建 real-DAL 实现和 mock-DAL 实现。可能是,世界对单元测试发疯了?创建 DAL 模拟并不是真正令人愉快的工作。它可以包含数百个带有列表/字典的“内存数据库”中的函数。为什么不在真实数据上测试真实的 DAL 函数?
【解决方案3】:

除了改变实现的能力之外,还有更多关于 IoC 的内容:

  • 测试
  • 显式依赖 - 不隐藏在私有 DataContext 中
  • 自动实例化 - 你在构造函数中声明你需要一些东西,然后你得到了它 - 解决了所有深层嵌套的依赖关系
  • 程序集的分离 - 查看 S#arp 架构,了解 IoC 如何避免引用 NHibernate 和其他特定程序集,否则您将不得不引用它们
  • 生命周期管理 - 能够指定对象的每个请求/单例/传递生命周期并在一个地方更改它,而不是在几十个控制器中进行更改
  • 能够执行动态操作,例如在模型绑定器中获取正确的数据上下文,因为使用 IoC,您现在拥有有关依赖项的元数据;这表明 IoC 对您的对象依赖项的作用可能与反射对 C# 编程的作用一样 - 很多新的可能性,您甚至从未想过

等等,我确定我错过了很多积极的东西。虽然我能想到的(以及你提到的)唯一“坏”的事情是接口的重复,这对于现代 IDE 对重构的支持不存在问题。

好吧,如果您的数据接口每天都在变化,并且您有数百个 - 您可能希望避免 IoC。

但是,您是否会因为难以遵循良好的设计实践而回避它们?您是否复制和粘贴代码而不是提取方法/类,只是因为这样做需要更多时间和更多代码?您是否将业务逻辑放在视图中只是因为创建视图模型并将它们与域模型同步更难?如果是,那么你可以避免IoC,没问题。

【讨论】:

    【解决方案4】:

    您认为使用 IOC 比不使用它需要更多的代码。我不同意。

    这是我使用 LinqToSql 的一个项目的整个 DAL IOC 配置。 ContextProvider 类只是一个线程安全的 LinqToSql 上下文工厂。

    container.Register(Component.For<IContextProvider<LSDataContext>, IContextProvider>().LifeStyle.PerWebRequest.ImplementedBy<ContextProvider<LSDataContext>>();
    container.Register(Component.For<IContextProvider<WorkSheetDataContext>, IContextProvider>().LifeStyle.PerWebRequest.ImplementedBy<ContextProvider<WorkSheetDataContext>>();
    container.Register(Component.For<IContextProvider<OffersReadContext>, IContextProvider>().LifeStyle.PerWebRequest.ImplementedBy<ContextProvider<OffersReadContext>>();
    

    这是我使用 NHibernate 和存储库模式的一个项目的整个 DAL 配置:

    container.Register(Component.For<NHSessionBuilder>().LifeStyle.Singleton);
    container.Register(Component.For(typeof(IRepository<>)).ImplementedBy(typeof(NHRepositoryBase<>)));
    

    这是我在 BLL 中使用 DAL 的方式(使用依赖注入):

    public class ClientService
    {
        private readonly IRepository<Client> _Clients;
    
    
    public ClientService(IRepository&lt;Client&gt; clients)
    {
        _Clients = clients;
    }
    
    public IEnumerable&lt;Client&gt; GetClientsWithGoodCredit()
    {
        return _Clients.Where(c => c.HasGoodCredit);
    }
    

    }

    请注意,我的 IRepository 接口继承了 IQueryable 所以这段代码非常简单!

    以下是在不连接数据库的情况下测试 BLL 的方法:

    public void GetClientsWithGoodCredit_ReturnsClientWithGoodCredit()
    {
        var clientWithGoodCredit = new Client() {HasGoodCredit = true};
        var clientWithBadCredit = new Client() {HasGoodCredit = false};
        var clients = new List<Client>() { clientWithGoodCredit, clientWithBadCredit }.ToTestRepository();
        var service = new ClientService(clients);
    
    
    var clientsWithGoodCredit = service.GetClientsWithGoodCredit();
    
    Assert(clientsWithGoodCredit.Count() == 1);
    Assert(clientsWithGoodCredit.First() == clientWithGoodCredit);
    

    }

    ToTestRepository() 是一个扩展方法,它返回一个使用内存列表的假 IRepository。

    您无法争辩说这比在您的 BLL 上更新 DAL 更复杂。

    编写上述测试的唯一方法是连接到数据库,保存一些测试客户端,然后进行查询。我保证执行时间比这要长 100 倍以上。 (乘以 1000 次测试,您可以在等待时去喝杯咖啡。)

    此外,通过使用未提交的事务进行测试,您会引入由不查询未提交实体的 ORM 导致的调试噩梦。

    【讨论】:

    • 我应该补充一点,我引用的其中一个项目有超过 100 个数据库表!
    猜你喜欢
    • 2011-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-19
    相关资源
    最近更新 更多