5 c# nhibernate dependency-injection inversion-of-control
我正试图了解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
}
}
Run Code Online (Sandbox Code Playgroud)
用户从下拉列表中选择特定客户端和产品,并将相应的clientId和productId传递给存储库,然后使用NHibernate在将产品和客户端对象注入事务对象之前填充它们.在我当前的设置中,事务通过构造函数依赖注入(尚未使用IoC容器)接收其产品和客户端依赖关系
public class ProductRepository : IRepository<Product>
{
public Product GetById(string id)
{
using (ISession session = NHibernateHelper.OpenSession())
return session.Get<Product>(id);
}
}
/* Similar repository for Clients */
IRepository<Client> clientRepository = new ClientRepository();
IRepository<Product> productRepository = new ProductRepository();
Client c = clientRepository.GetById(clientId);
Product p = productRepository.GetById(productId);
Transaction t = new Transaction(p, c);
Run Code Online (Sandbox Code Playgroud)
以下是我希望得到的想法:
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)即
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);
}
Run Code Online (Sandbox Code Playgroud)
B. 是否可以使用IoC容器来获取具有注入的NHibernate填充/生成的产品和客户端依赖关系的Transaction对象?即,给定由用户提供的productId和clientId,是否可能具有如下内容:
// pseudocode
Transaction t = IoC.Resolve<Transaction>(productId, clientId);
Run Code Online (Sandbox Code Playgroud)
如果容器解析了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;
}
}
Run Code Online (Sandbox Code Playgroud)
然而,这实际上是如何显示DI的所有示例,假设IInterfaceB的实现者(在这种情况下为B类)在设计时是已知的.有没有办法以这样的方式使用DI,以便在运行时确定实现者?
非常感谢
马修
A) 如果您要通过 Product 域对象访问 MarginCalculator,您不妨去掉中间人,让 DI/IOC 容器为您注入 MarginCalculator。您甚至可以摆脱 MarginCalculatorAssembler,因为大多数 DI/IOC 容器会为您完成大部分对象构造的样板代码。
B 和 C ) 这很有可能。事实上,如果您使用LinFu ,您的代码将如下所示:
// 无需更改Transaction类
公开课交易
{
私人双_margin;
私人产品_产品;
私人客户端_client;
公共双保证金{得到; }
公共产品 产品 { 得到; }
公共客户端客户端{获取; }
公开交易(产品p,客户c)
{
_产品=p;
_客户端=c;
}
公共无效计算保证金()
{
_margin = _product.MarginCalculator.CalculateMargin();
}
}
如果您可以使用 DI/IOC 将产品和客户端实例注入到构造函数中,那就太好了,但在此之前,您需要向容器注册依赖项。以下是使用 LinFu.IOC 的方法:
// 接下来,您必须告诉 LinFu 自动注册您的产品类别:
[工厂(类型(产品))]
公共类 ProductFactory : IFactory
{
对象CreateInstance(IServiceRequest请求)
{
// 从容器中获取 IRepository 的副本
var 存储库 = 容器.GetService>();
// 获取 id(假设你的 id 是 Int32)
var id = (int)request.Arguments[0];
// 返回产品本身
返回存储库.GetById(id);
}
}
// 对 Client 类做同样的事情
// (注意:我做了一个简单的剪切和粘贴以使事情简单 - 请原谅重复)
[工厂(类型(客户端))]
公共类 ClientFactory : IFactory
{
对象CreateInstance(IServiceRequest请求)
{
// 从容器中获取 IRepository 的副本
var 存储库 = 容器.GetService>();
// 获取 id(假设你的 id 是 Int32)
var id = (int)request.Arguments[0];
// 返回客户端本身
返回存储库.GetById(id);
}
}
[工厂(类型(事务))]
公共类 TransactionFactory : IFactory
{
对象CreateInstance(IServiceRequest请求)
{
// 注意:为了简洁起见,参数检查已被删除
var 容器 = request.Container;
var 参数 = request.Arguments;
var ProductId = (int)参数[0];
var clientId = (int)arguments[1];
// 获取产品和客户
var 产品 = 容器.GetService(productId);
var client = container.GetService(clientId);
// 创建交易本身
返回新交易(产品,客户);
}
}
// 使这个实现成为单例
[实现(typeof(MarginCalculator),LifecycleType.Singleton)]
公共类 ConcreteMarginCalculatorA :MarginCalculator
{
公共重写 doubleCalculateMargin()
{
// 进行实际计算
}
}
将所有代码编译到某个程序集中后,将其加载到容器中所需执行的操作如下:
var 容器 = new ServiceContainer(); container.LoadFrom(AppDomain.CurrentDomain.BaseDIRECTORY, "YourAssembly.dll");
...现在是有趣的部分。为了使用给定的产品和客户端 ID 创建交易对象,您需要对 LinFu.IOC 的容器进行调用:
int 产品ID = 12345; int clientId = 54321; 字符串服务名称 = null; // 不是伪代码:) var transaction = container.GetService(serviceName, ProductId, clientId);
有趣的是,尽管您可能有很多依赖项,LinFu 的 IOC 容器将为您处理 90% 的样板代码,因此您不必自己完成所有这些工作。最好的部分是上面的所有实现都将在运行时确定/解决。
您实际上可以在程序运行时交换实现,甚至可以替换实现而无需重新编译应用程序。您可以在这里找到更多信息:
http://www.codeproject.com/KB/cs/LinFu_IOC.aspx
哈:)
| 归档时间: |
|
| 查看次数: |
2804 次 |
| 最近记录: |