DI/IoC、NHibernate 并帮助它们协同工作 [英] DI/IoC, NHibernate and help in getting them to work together

查看:27
本文介绍了DI/IoC、NHibernate 并帮助它们协同工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to get my head around DI/IoC, NHibernate and getting them to work nicely together for an application that i'm developing. I'm quite new to both NHibernate and DI/IoC so not quite sure whether what i'm doing is the sensible way to be going about it. This is the scenario:

该应用程序使用户能够计算特定金融交易的特定价值(称为保证金).每笔交易的保证金值的计算由抽象 MarginCalculator 类的具体实现进行,具体实现取决于特定交易的产品类型(由产品对象的某个字段给出).具体的计算器类是通过产品类的属性访问的.即

The application provides users with the ability to calculate a particular value (known as the margin) for a particular financial transaction. The calculation of the marging value for each transaction is carried out by concrete implementations of an abstract MarginCalculator class and the concrete implementation to be used depends on the type of the product for the particular transaction (given by a certain field of the product object). The concrete calculator class is accessed via a property on the product class. i.e.

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

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

Users select a particular client and Product from dropdowns and the corresponding clientId and productId are passed to repositories that then use NHibernate to populate product and client objects before they're injected into the transaction object. In my current setup the Transaction receives its Product and Client dependencies via constructor dependency injection (no IoC container used as yet) i.e.

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

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

The following are what i'm hoping to get ideas on:

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) 即

A. Is it considered OK to be accessing the MarginCalculator (which is essentially a service) through the Product domain object or should, as suggested here, (http://stackoverflow.com/questions/340461/dependency-injection-with-nhibernate-objects) the code be restructured so as to remove service dependencies from the domain objects and instead create a new TransactionProcessor class that takes the abstract MarginCalculator as a dependency (along the lines of what's described here (http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/03/31/ptom-the-dependency-inversion-principle.aspx) i.e.

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 填充/生成的产品和客户端依赖项的事务对象?即给定一个 productId 和 clientId,两者都由用户提供,是否有可能是这样的:

B. Is it possible to use an IoC Container to get a Transaction object with NHibernate populated/generated Product and Client dependencies injected? i.e. Given a productId and clientId, both provided by the user, is it possible to have something like:

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

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

such that the container resolves the Product and Client dependencies of the Transaction object, NHibernate is utilised to populate the Product and Client based on the productId and clientId and then the populated Product and Client are injected into the Transaction?

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

C. In a typical DI scenario, if class A has a dependency on interface B then the following might be done:

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?

However, this, which is virtually how all examples of DI are shown, assumes that the implementor of IInterfaceB (in this case Class B) is known at design time. Is there a way to use DI in such a way that the implementor is determined at runtime?

非常感谢

马修

推荐答案

这是我对您的问题的第二个看法:

Here's my second take on your questions:

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

A: In terms of best practice, you can leave the service dependency into the domain object as long as you make sure that you're depending on an interface type. Most (if not all) containers can do that type of injection for you, and it's pretty trivial to mock out each service dependency so you can test every behavior in your concrete classes. I only recommend using abstract classes if you want to refactor out the boilerplate implementation for a particular interface implementation, such as using a base class to do your generic CRUD persistence work.

B 和 C:

很高兴知道这种功能可用.我想一个更重要的问题是我正在尝试做的实际上是否是普遍做法,以及它是否被认为是好的做法.即

It's good to know that this kind of functionality is available. I suppose a more important question is whether what i'm trying to do is in fact common practice and whether it's considered good practice. i.e.

  1. 使用持久性框架(例如 NHibernate)和
  2. 让容器解析并注入已预先填充的依赖项
  3. 让容器注入抽象依赖项的具体实现,其中具体实现是在运行时确定的.
  1. Have a container resolve and inject dependencies that have been pre-populated >using a persistence framework (e.g. NHibernate) and
  2. Have the container inject concrete implementation of abstract dependencies where the concrete implementation are determined at runtime.

此外,在 IoC/DI/NHibernate 术语中,我所说的是否有特定名称?例如,它是此比较或 .net IoC 框架的比较中列出的功能之一吗?我想了解其他 IoC 框架(如 Castle Windsor)是否包含像 LinFu 那样的这些功能,但我不知道我所描述的内容是否具有特定名称,所以我不知道要搜索什么:)

Also, in IoC/DI/NHibernate terminology, does what i'm talking about, have a particular name? Is it, for example, one of the features listed in this comparison or this comparison of .net IoC frameworks? I'd like to read about whether other IoC frameworks (like Castle Windsor) include these functionalities like LinFu does but i don't know whether what i'm describing has a particular name so i don't know what to search for :)

我相信您实际上是指在 this 上发布的比较链接.

I believe you're actually referring to the comparison posted at this link.

1) AFAIK,这是进行服务注入的标准做法,但是您所指的注入类型对于其他一些框架来说很难做到,因为您必须使用域对象 ID 来解决这些依赖项运行时,并非所有容器都支持这种类型的动态解析(又名上下文绑定").在所有条件相同的情况下(并假设这可以通过其他容器完成),似乎适用于 DI/IoC 的唯一最佳实践"是您必须为服务依赖项使用接口.

1) AFAIK, it's standard practice to do service injection, but the type of injection that you're referring to would be difficult to do for some of the other frameworks since you have to use domain object IDs to resolve these dependencies at run time, and not all containers support that type of dynamic resolution (aka 'contextual binding'). All things being equal (and assuming that this can be done with the other containers), the only 'best practice' that seems to apply with DI/IoC is that you must use interfaces for your service dependencies.

最终应该如何构建和解决这些依赖项应该完全取决于您,在您的情况下,是否从持久性框架填充这些依赖项并不重要,只要容器本身能够消除大部分样板解析代码都适合你.

How these dependencies should be ultimately constructed and resolved should be completely up to you, and in your case, it really doesn't matter if you get these dependencies populated from a persistence framework as long as the container itself is able to eliminate most of the boilerplate resolution code for you.

2) 具体的服务注入是DI/IOC框架中的标准,大部分都可以在运行时解析依赖;然而,这些框架在注入的方式和位置上有所不同.

2) Concrete service injection is standard among DI/IOC frameworks, and most of them can resolve dependencies at runtime; however, these frameworks differ on how and where that injection can be done.

仅供参考,您应该注意的两个特性是构造函数注入属性注入.根据您的代码示例,我会说您更倾向于使用构造函数注入,因此您可能需要注意每个框架如何为您执行这种类型的注入.哈:)

FYI, the two features that you should pay attention to are Constructor Injection and Property Injection. Based on your code examples, I'd say that you'd be more inclined to use constructor injection, so you might want to keep an eye out for how each respective framework does that type of injection for you. HTH :)

这篇关于DI/IoC、NHibernate 并帮助它们协同工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆