gpt4 book ai didi

c# - DI/IoC、NHibernate 并帮助它们协同工作

转载 作者:太空狗 更新时间:2023-10-29 23:42:45 24 4
gpt4 key购买 nike

我正在努力了解 DI/IoC、NHibernate,并让它们在我正在开发的应用程序中很好地协同工作。我对 NHibernate 和 DI/IoC 都很陌生,所以不太确定我正在做的事情是否是明智的做法。这是场景:

该应用程序为用户提供了为特定金融交易计算特定值(称为 margin )的能力。每笔交易的 margin 值的计算是通过抽象 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 填充产品和客户端对象,然后再将它们注入(inject)交易对象。在我当前的设置中,事务通过构造函数依赖注入(inject)(尚未使用 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);

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

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);
}

B. 是否可以使用 IoC 容器来获取事务对象,其中注入(inject)了 NHibernate 填充/生成的产品和客户端依赖项?即,给定一个由用户提供的 productId 和 clientId,是否有可能是这样的:

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

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

C. 在典型的 DI 场景中,如果类 A 依赖于接口(interface) 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 容器为您注入(inject) MarginCalculator。您甚至可以摆脱 MarginCalculatorAssembler,因为大多数 DI/IOC 容器都会为您完成对象构造的大部分样板代码。

B 和 C) 很有可能。事实上,如果你使用 LinFu,你的代码会是这样的:

// No need to change the Transaction classpublic 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();    }}

如果您可以使用 DI/IOC 将产品和客户端实例注入(inject)构造函数,那就太好了——但在我们这样做之前,您需要向容器注册依赖项。以下是使用 LinFu.IOC 的方法:

// Next, you'd have to tell LinFu to automatically register your product class:[Factory(typeof(Product))]public class ProductFactory : IFactory{     object CreateInstance(IServiceRequest request)     {          // Grab a copy of the IRepository from the container          var repository = container.GetService>();          // Get the id (this assumes that your id is an Int32)          var id = (int)request.Arguments[0];          // Return the product itself          return repository.GetById(id);     }}// Do the same thing with the Client class// (Note: I did a simple cut and paste to keep things simple--please forgive the duplication)[Factory(typeof(Client))]public class ClientFactory : IFactory{     object CreateInstance(IServiceRequest request)     {          // Grab a copy of the IRepository from the container          var repository = container.GetService>();          // Get the id (this assumes that your id is an Int32)          var id = (int)request.Arguments[0];          // Return the client itself          return repository.GetById(id);     }}[Factory(typeof(Transaction))]public class TransactionFactory : IFactory{     object CreateInstance(IServiceRequest request)     {        // Note: Argument checking has been removed for brevity        var container = request.Container;        var arguments = request.Arguments;        var productId = (int)arguments[0];        var clientId = (int)arguments[1];        // Get the product and the client        var product = container.GetService(productId);        var client = container.GetService(clientId);        // Create the transaction itself        return new Transaction(product, client);     }}// Make this implementation a singleton[Implements(typeof(MarginCalculator), LifecycleType.Singleton)]public class ConcreteMarginCalculatorA : MarginCalculator{    public override double CalculateMargin()    {        // Perform actual calculation    }}

将所有代码编译到一个程序集中后,只需执行以下操作即可将其加载到容器中:

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

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

int productId = 12345;int clientId = 54321;string serviceName = null;// Not pseudocode :)var transaction = container.GetService(serviceName, productId, clientId);

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

您几乎可以在程序运行时交换实现,甚至无需重新编译您的应用程序就可以替换实现。您可以在此处找到更多信息:

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

HTH:)

关于c# - DI/IoC、NHibernate 并帮助它们协同工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/387783/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com