Я пытаюсь разобраться с DI / IoC, NHibernate и заставить их хорошо работать вместе для приложения, которое я разрабатываю. Я довольно новичок в NHibernate и DI / IoC, поэтому не совсем уверен, что то, что я делаю, является разумным способом добиться этого. Это сценарий:
Приложение предоставляет пользователям возможность рассчитать конкретное значение (известное как маржа) для конкретной финансовой транзакции. Расчет значения разметки для каждой транзакции выполняется с помощью конкретных реализаций абстрактного класса 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 для заполнения объектов product и client перед их внедрением в объект транзакции. В моей текущей настройке Транзакция получает свои зависимости Product и Client посредством внедрения зависимостей конструктора (пока еще не использовался контейнер 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. Считается ли нормальным доступ к MarginCalculator (который по сути является службой) через объект домена Product или должен, как предлагается здесь, (/236783/vnedrenie-zavisimostei-s-obektami-nhibernate) код должен быть реструктурирован таким образом, чтобы удалить служебные зависимости из объектов домена и вместо этого создать новый класс 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 для получения объекта транзакции с введенной зависимостью заполненных / сгенерированных продуктов и клиентов NHibernate? т.е. ProductId и clientId, оба предоставленные пользователем, могут иметь что-то вроде:
// pseudocode
Transaction t = IoC.Resolve<Transaction>(productId, clientId);
таким образом, что контейнер разрешает зависимости Product и Client объекта Transaction, NHibernate используется для заполнения Product и Client на основе productId и clientId, а затем заполненные Product и Client вводятся в транзакцию?
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 таким образом, чтобы средство реализации определялось во время выполнения?
Большое спасибо
Мэтью