DI / IoC,NHibernate,帮助他们一起工作 [英] DI/IoC, NHibernate and help in getting them to work together

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

问题描述

我试图让我的头脑在DI / IoC,NHibernate,并让他们一起工作,我正在开发的应用程序。我对NHibernate和DI / IoC都很新,所以不太清楚我在做的是一个合理的方法。这是场景:



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

  public class Transaction 
{
private double _margin;
私人产品_产品;
私人客户端_client;

public double保证金{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;
...其他字段

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

私人MarginCalculatorAssembler()
{
}

public MarginCalculator CreateMarginCalculatorFor(string productType)
{
switch(productType)
{
caseA:
return new ConcreteMarginCalculatorA();
caseB:
return new ConcreteMarginCalculatorB();
default:
throw new ArgumentException();
}
}
}

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

public class ConcreteMarginCalculatorA:MarginCalculator
{
public override double CalculateMargin
{
//执行实际计算
}
}

public class ConcreteMarginCalculatorB:MarginCalculator
{
public override double CalculateMargin
{
//执行实际计算
}
}

用户从下拉列表中选择特定的客户端和产品,相应的clientId和productId为传递到存储库,然后使用NHibernate在产品和客户端对象注入事务对象之前填充它们。在我目前的设置中,交易通过构造函数依赖注入(不再使用IoC容器)来收到其产品和客户端依赖项,即

  public ProductRepository类:IRepository< Product> 
{
public Product GetById(string id)
{
using(ISession session = NHibernateHelper.OpenSession())
return session.Get< Product>(id) ;
}
}

/ *客户端的类似仓库* /

IRepository< Client> clientRepository = new ClientRepository();
IRepository< Product> productRepository = new ProductRepository();
客户端c = clientRepository.GetById(clientId);
产品p = productRepository.GetById(productId);

事务t =新事务(p,c);

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



A。 是否可以通过产品域对象访问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-in version-principle.aspx )ie

  public class TransactionProcessor 
{
私人只读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填充/生成的产品和客户端依赖关系的事务对象? ie给定一个由用户提供的productId和clientId,可以有一些东西如:

  //伪代码
事务t = IoC.Resolve< Transaction>(productId,clientId);

,以便容器解析Transaction对象的Product和Client依赖项,NHibernate用于填充基于productId和clientId的产品和客户端,然后将填充的产品和客户端注入到交易中?



C。在典型的DI如果类A对接口B有依赖关系,则可能会执行以下操作:

  IInterfaceB b = new ClassB(); 
A a = new A(b);

接口IInterfaceB
{
}

类B:IInterfaceB
{
}

公共课A
{
private IIntefaceB _b;

public A(IInterfaceB b)
{
_b = b;
}
}

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





Matthew

解决方案

这是我第二次提出你的问题: / p>

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



B和C:


很高兴知道这种功能可用。我想一个更重要的问题是,我想要做的是事实上是常见的做法,是否被认为是良好的做法。即


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

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


我相信你其实是指在此链接



1)AFAIK是服务注入的标准做法,但是您所指的注射类型对于其他一些框架来说将难以做到,因为您必须使用域对象ID来在运行时解析这些依赖关系,并不是所有的容器都支持这种类型的动态分辨率(也称为上下文绑定)。所有的事情都是平等的(假设可以用其他容器来完成),DI / IoC中唯一适用的最佳实践就是使用接口来满足您的服务依赖性。



如何最终构建和解决这些依赖关系应该完全取决于您,在您的情况下,如果您从持久性框架只要容器本身能够消除大部分的样板解析码。 2)具体的服务注入是DI / IOC框架中的标准,大部分可以在运行时解决依赖关系。然而,这些框架在注射的方式和位置上有所不同。



FYI,您应该注意的两个功能是构造函数注入属性注入。根据你的代码示例,我会说你会更倾向于使用构造函数注入,所以你可能想要注意每个框架如何为你注入类型。 HTH:)


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:

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

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

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

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?

Many thanks

Matthew

解决方案

Here's my second take on your questions:

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 and 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. 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.

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

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

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) 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天全站免登陆