DI / IoC,NHibernate并帮助他们一起工作

我正试图了解DI / IoC,NHibernate并让它们在我正在开发的应用程序中很好地协同工作。 我对NHibernate和DI / IoC都很陌生,所以不太确定我所做的是否是合理的方式。 这是场景:

该应用程序为用户提供了计算特定金融交易的特定值(称为保证金)的能力。 每个事务的marging值的计算是通过抽象MarginCalculator类的具体实现来执行的,并且要使用的具体实现取决于特定事务的产品类型(由产品对象的某个字段给出)。 通过产品类上的属性访问具体的计算器类。 即

public class Transaction { private double _margin; private Product _product; private Client _client; public double Margin { get; } public Product Product { get; } public Client Client { get; } public Transaction(Product p, Client c) { _product = p; _client = c; } public void CalculateMargin() { _margin = _product.MarginCalculator.CalculateMargin(); } } public class Product { private string _id; private string _productType; ... Other fields public string Id { get; } public string ProductType { get; } public MarginCalculator MarginCalculator { get { return MarginCalculatorAssembler.Instance.CreateMarginCalculatorFor(this.ProductType); } } } public class MarginCalculatorAssembler { public static readonly MarginCalculatorAssembler Instance = new MarginCalculatorAssembler(); private MarginCalculatorAssembler () { } public MarginCalculator CreateMarginCalculatorFor(string productType) { switch (productType) { case "A": return new ConcreteMarginCalculatorA(); case "B": return new ConcreteMarginCalculatorB(); default: throw new ArgumentException(); } } } public abstract class MarginCalculator { public abstract double CalculateMargin(); } public class ConcreteMarginCalculatorA : MarginCalculator { public override double CalculateMargin { // Perform actual calculation } } public class ConcreteMarginCalculatorB : MarginCalculator { public override double CalculateMargin { // Perform actual calculation } } 

用户从下拉列表中选择特定客户端和产品,并将相应的clientId和productId传递给存储库,然后使用NHibernate在将产品和客户端对象注入事务对象之前填充它们。 在我当前的设置中,事务通过构造函数dependency injection(尚未使用IoC容器)接收其产品和客户端依赖关系

 public class ProductRepository : IRepository { public Product GetById(string id) { using (ISession session = NHibernateHelper.OpenSession()) return session.Get(id); } } /* Similar repository for Clients */ IRepository clientRepository = new ClientRepository(); IRepository productRepository = new ProductRepository(); Client c = clientRepository.GetById(clientId); Product p = productRepository.GetById(productId); Transaction t = new Transaction(p, c); 

以下是我希望得到的想法:

A. 通过Product域对象访问MarginCalculator(本质上是一种服务)是否可以,或者应该按照此处的建议( http://stackoverflow.com/questions/340461/dependency-injection-with-nhibernate) -objects )重构代码以便从域对象中删除服务依赖性,而是创建一个新的TransactionProcessor类,它将抽象的MarginCalculator作为依赖项(沿着这里所描述的行( http://www.lostechies.com) /blogs/jimmy_bogard/archive/2008/03/31/ptom-the-dependency-inversion-principle.aspx)ie

 public class TransactionProcessor { private readonly MarginCalculator _marginCalculator; public TransactionProcessor(MarginCalculator marginCalculator) { _marginCalculator = marginCalculator; } public double CalculateMargin(Transaction t) { return _marginCalculator.CalculateMargin(Transaction t); } } public abstract class MarginCalculator { public abstract double CalculateMargin(Transaction t); } 

B. 是否可以使用IoC容器来获取具有NHibernate填充/生成的Product和Client依赖关系的Transaction对象? 即,给定由用户提供的productId和clientId,是否可能具有以下内容:

 // pseudocode Transaction t = IoC.Resolve(productId, clientId); 

这样容器解析了Transaction对象的Product和Client依赖关系,NHibernate用于根据productId和clientId填充Product和Client,然后将填充的Product和Client注入到Transaction中?

C.在典型的DI场景中,如果A类依赖于接口B,则可能会执行以下操作:

 IInterfaceB b = new ClassB(); A a = new A(b); interface IInterfaceB { } class B : IInterfaceB { } public class A { private IIntefaceB _b; public A(IInterfaceB b) { _b = b; } } 

然而,这实际上是如何显示DI的所有示例,假设IInterfaceB的实现者(在这种情况下为B类)在设计时是已知的。 有没有办法以这样的方式使用DI,即在运行时确定实现者?

非常感谢

马修

A)如果您要通过Product域对象访问MarginCalculator,您可能会切断中间人并让DI / IOC容器为您注入MarginCalculator。 你甚至可以摆脱MarginCalculatorAssembler,因为大多数DI / IOC容器为你做了大部分对象构造的样板代码。

B和C )这很有可能。 实际上,如果您使用LinFu ,这里的代码就是这样的:


 //无需更改Transaction类
公共类交易
 {
     private double _margin;
    私人产品_product;
    私人客户_client;

    公开双保证金{get;  }
    公共产品{get;  }
    公共客户端客户端{get;  }

    公共交易(产品p,客户c)
     {
         _product = p;
         _client = c;
     }

     public void CalculateMargin()
     {
         _margin = _product.MarginCalculator.CalculateMargin();
     }
 }

如果你能让DI / IOC将产品和客户端实例注入构造函数中会很好 – 但在我们这样做之前,你需要在容器中注册依赖项。 以下是LinFu.IOC的使用方法:

 //接下来,您必须告诉LinFu自动注册您的产品类:
 [工厂(typeof运算(产品))]
公共类ProductFactory:IFactory
 {
     对象CreateInstance(IServiceRequest请求)
      {
           //从容器中获取IRepository的副本
           var repository = container.GetService>();

           //获取id(这假设您的id是Int32)
           var id =(int)request.Arguments [0];

           //返回产品本身
           return repository.GetById(id);
      }
 }

 //对Client类做同样的事情
 //(注意:我做了一个简单的剪切和粘贴以保持简单 - 请原谅重复)
 [工厂(typeof运算(客户端))]
公共类ClientFactory:IFactory
 {
     对象CreateInstance(IServiceRequest请求)
      {
           //从容器中获取IRepository的副本
           var repository = container.GetService>();

           //获取id(这假设您的id是Int32)
           var id =(int)request.Arguments [0];

           //返回客户端本身
           return repository.GetById(id);
      }
 }

 [工厂(typeof运算(事务))]
 public class TransactionFactory:IFactory
 {
     对象CreateInstance(IServiceRequest请求)
      {
         //注意:为简洁起见,已删除参数检查
         var container = request.Container;
         var arguments = request.Arguments;
         var productId =(int)arguments [0];
         var clientId =(int)arguments [1];

         //获取产品和客户端
         var product = container.GetService(productId);
         var client = container.GetService(clientId);

         //创建交易本身
        返回新的交易(产品,客户);
      }
 }

 //使此实现成为单例
 [Implements(typeof(MarginCalculator),LifecycleType.Singleton)]
 public class ConcreteMarginCalculatorA:MarginCalculator
 {
     public override double CalculateMargin()
     {
         //执行实际计算
     }
 }

一旦您在其中一个程序集中编译了所有代码,就可以将所有代码加载到容器中:

 var container = new ServiceContainer();
 container.LoadFrom(AppDomain.CurrentDomain.BaseDIrectory,“YourAssembly.dll”);

…现在是有趣的部分。 为了使用给定的产品和客户端ID创建您的事务对象,以下是您需要对LinFu.IOC的容器进行的调用:

 int productId = 12345;
 int clientId = 54321;
 string serviceName = null;

 //不是伪代码:)
 var transaction = container.GetService(serviceName,productId,clientId);

令人感兴趣的是,尽管您可能拥有多少依赖项,LinFu的IOC容器将为您处理90%的样板代码,因此您不必自己完成所有这些工作。 最好的部分是上面的所有实现都将在运行时确定/解决

您可以在程序运行时实际交换实现,甚至可以在不重新编译应用程序的情况下替换实现。 你可以在这里找到更多信息:

http://www.codeproject.com/KB/cs/LinFu_IOC.aspx

HTH 🙂

这是我对你的问题的第二次看法:

答:就最佳实践而言,只要确保依赖于接口类型,就可以将服务依赖关系保留在域对象中。 大多数(如果不是全部)容器都可以为您执行这种类型的注入,并且模拟每个服务依赖项非常简单,因此您可以测试具体类中的每个行为。 如果您想重构特定接口实现的样板实现,我建议使用抽象类,例如使用基类来执行通用CRUD持久性工作。

B和C:

很高兴知道这种function是可用的。 我想一个更重要的问题是,我正在尝试做的事实上是否是常见做法以及它是否被视为良好做法。 即

  1. 让容器解析并注入已预先填充的依赖项>使用持久性框架(例如NHibernate)和
  2. 让容器注入抽象依赖项的具体实现,其中具体实现是在运行时确定的。

另外,在IoC / DI / NHibernate术语中,我正在谈论的是什么,有一个特定的名字? 例如,它是此比较中列出的function之一还是.net IoC框架的比较? 我想了解其他IoC框架(如Castle Windsor)是否包含像LinFu这样的function,但我不知道我所描述的是否有一个特定的名称所以我不知道要搜索什么:)

我相信你实际上是指在这个链接上发布的比较。

1)AFAIK,它是执行服务注入的标准做法,但是你要引用的注入类型对于某些其他框架很难做,因为你必须在运行时使用域对象ID来解决这些依赖关系,并非所有容器都支持这种类型的动态分辨率(也称为“上下文绑定”)。 在所有条件相同的情况下(假设可以使用其他容器完成),DI / IoC似乎唯一适用的“最佳实践”是必须使用接口来实现服务依赖性。

应该如何最终构建和解决这些依赖关系应该完全取决于您,在您的情况下,只要容器本身能够消除大多数依赖关系,从持久性框架填充这些依赖关系真的无关紧要。样板解析代码给你。

2)具体的服务注入是DI / IOC框架的标准,大多数都可以在运行时解决依赖关系; 然而,这些框架在注射的方式和位置上有所不同。

仅供参考,您应该注意的两个function是构造函数注入属性注入 。 根据您的代码示例,我会说您更倾向于使用构造函数注入,因此您可能需要留意每个相应框架如何为您执行此类注入。 HTH 🙂

菲利普,

感谢您的回答!

B和C

很高兴知道这种function是可用的。 我想一个更重要的问题是,我正在尝试做的事实上是否是常见做法以及它是否被视为良好做法。

  1. 让容器解析并注入已使用持久性框架(例如NHibernate)预先填充的依赖项
  2. 让容器注入抽象依赖项的具体实现,其中具体实现是在运行时确定的。

另外,在IoC / DI / NHibernate术语中,我正在谈论的是什么,有一个特定的名字? 例如,它是此比较中列出的function之一还是.net IoC框架的比较 ? 我想了解其他IoC框架(如Castle Windsor)是否包含像LinFu这样的function,但我不知道我所描述的是否有一个特定的名称所以我不知道要搜索什么:)

A:

在最佳实践方面(即松散耦合,测试等……),最好从域对象中删除服务依赖项还是将其保留在那里?

谢谢

马修

根据“领域驱动设计”,您的服务将是“域名服务”,您的域名的其余部分可以直接调用它或依赖它。

如果您打算使用Nhibernate,请查看Spring.net,这是一个非常流行的DI框架,为您提供DAOS,它已经注入了会话。 它还允许您使用声明性事务(使用属性标记方法)。 该项目的文档非常好。

最后但并非最不重要,并且不要误解我,我认为你正在使用这项技术只是因为(我没有看到你有DI的需要),如果你这样做是为了学习东西,这很酷但在其他所有情况下都是错的。

问候

巴勃罗

谢谢你的评论。

也许如果我在一个区域中详细说明我打算在项目中使用DI(不仅如你所说,了解DI而且因为我认为这是必要的)然后可以进一步评论它是否是正确使用DI的地方。

如原始post中所述,该应用程序将使用MarginCalculator服务:

 public abstract class MarginCalculator { public abstract double CalculateMargin(); } 

注意:该服务可能是抽象类或接口。

具体实现(DI术语中的组件?)将如下:

 public class ConcreteMarginCalculatorA : MarginCalculator { private IDependencyService1 _dependencyService1; private IDependencyService2 _dependencyService2; // Constructor dependency injection public ConcreteMarginCalculatorA( IDependencyService1 dependencyService1, IDependencyService2 dependencyService2) { this._dependencyService1 = dependencyService1; this._dependencyService2 = dependencyService2; } public override double CalculateMargin { // _dependencyService1 and _dependencyService2 // required here to perform calcuation. } } public class ConcreteMarginCalculatorB : MarginCalculator { private IDependencyService3 _dependencyService3; private IDependencyService4 _dependencyService4; // Constructor dependency injection public ConcreteMarginCalculatorB( IDependencyService3 dependencyService3, IDependencyService4 dependencyService4) { this._dependencyService3 = dependencyService3; this._dependencyService4 = dependencyService4; } public override double CalculateMargin { // _dependencyService3 and _dependencyService4 // required here to perform calcuation. } } 

具体的保证金计算器及其构造不是一个完美的例子,说明应该使用dependency injection的方式以及如何使用IoC容器来处理dependency injection?

我认为我正在尝试做的非常类似于DI / IoC在这篇文章和本文中的描述。

最后,我将使用工厂类,可能使用内部/子容器,以便根据参数值动态解析组件/实现器(ConcreteMarginCalculatorA,ConcreteMarginCalculatorB等…)。 要实现这一目标,我倾向于使用Autofac ( http://code.google.com/p/autofac/ ),它允许根据参数值选择实施者( http://code.google.com/p/autofac / wiki / ComponentCreation – “基于参数值选择实现者”部分:

 public class MarginCalculatorFactory { private readonly IContainer _factoryLevelContainer; public MarginCalculatorFactory(IContainer mainContainer) { _factoryLevelContainer = mainContainer.CreateChildContainer() _factoryLevelContainer.RegisterType("ConcMC1"); _factoryLevelContainer.RegisterType("ConcMC2"); } public MarginCalculator CreateCalculator(string productType) { return _factoryLevelContainer.Resolve(productType); } 

}

所以最终我能做到:

 marginCalculatorFactory.CreateCalculator(productType); 

在客户端代码中获得完全解析的计算器。 然后,计算器可以dependency injection到TransactionProcessor服务中:

 public class TransactionProcessor { private readonly MarginCalculator _marginCalculator; private readonly Transaction _transaction; public TransactionProcessor(MarginCalculator marginCalculator ,Transaction transaction) { _marginCalculator = marginCalculator; _transaction = transaction } public double CalculateMargin(Transaction t) { return _marginCalculator.CalculateMargin(transaction); } } 

我可能错了,因为我是整个IoC / DI游戏的新手,但在我看来,这恰恰是Di / IoC用于的场景。 别人怎么想?

谢谢

马修

看一下这篇文章http://fabiomaulo.blogspot.com/2008/11/entities-behavior-injection.html