如何在带参数的简单注入器中实现工厂接口
How to implement Factory Interface in Simple Injector With Parameters
This answer 展示了如何使用不带参数的工厂接口解析实例。
我正在使用以下代码
public interface ISimpleBarFactory
{
Bar CreateBar(int value);
}
public sealed class SimpleBarFactory : ISimpleBarFactory
{
private readonly Container _container;
public SimpleBarFactory (Container container)
{
_container = container;
}
public Bar CreateBar(int value)
{
_container.Register(() => new Bar(vlue));
return _container.GetInstance<Bar>();
}
}
解析具有构造函数参数的实例。
但是,在使用工厂实例化服务时出现以下异常class:
The container can't be changed after the first call to GetInstance, GetAllInstances and Verify.
使用带参数的工厂接口解析实例的正确方法是什么?
更新
以下是我的代码。我正在从 Ninject.
迁移代码
public interface IFormsUIFactory
{
AccountUI CreateAccountUI(Account account);
}
public class FormsUIFactory
{
private readonly IFormsUIFactory _uiFactory;
public FormsUIFactory(IFormsUIFactory uiFactory)
{
_uiFactory = uiFactory;
}
public void CreateAccountUI(Account account)
{
_uiFactory.CreateAccountUI(account);
}
}
UI class 待注入
public partial class AccountUI : Form
{
private readonly IAccountMaintenanceProcessor _processor;
private readonly Account _account;
public AccountUI(IAccountMaintenanceProcessor accountProcessor, Account account)
{
_processor = accountProcessor;
_account = account;
}
}
实例化代码:
var account = new Account();
// Populate values for the account
var frm = _uiFactory.CreateAccountUI(account);
您遇到的问题是由于您将运行时数据(您的Account
)对象与组件混合在一起。您的 DI 容器负责构建组件的对象图。这些组件通常应该是无状态的,运行时数据应该使用方法调用流经对象图。在组件的构造函数中注入运行时数据,使这些组件具有状态,并以许多不同的方式使您的应用程序复杂化;您今天正在目睹这些并发症之一。但是这样做有很多缺点。例如,将运行时数据注入构造函数使得无法验证您的对象图(使用自动化测试)并使您的应用程序很容易在运行时中断。所以这绝不是 Simple Injector 特定的,但 Simple 确实通过在解析服务时不允许传递运行时值来使问题更加突出。
你可以找到关于这个的详细解释 here.
因此,请尽量让您的组件保持无状态,并且至少让运行时数据远离构造函数。实现此目的的一种简单方法是将 属性 添加到允许设置您正在使用的实体的表单。可以将通用界面添加到表单以允许这样做:
interface IForm<TEntity>
{
TEntity Entity { get; set; }
}
此通用接口可用于 IFormFactory
抽象:
interface IFormFactory
{
TForm CreateFormFor<TForm, TEntity>(TEntity entity)
where TForm : Form, IForm<TEntity>;
}
为 IFormFactory
创建一个实现就这么简单:
public class FormFactory : IFormFactory
{
private readonly Container container;
public FormFactory(Container container) {
this.container = container;
}
public TForm CreateFormFor<TForm, TEntity>(TEntity entity)
where TForm : Form, IForm<TEntity> {
var form = this.container.GetInstance<TForm>();
form.Entity = entity;
return form;
}
}
This answer 展示了如何使用不带参数的工厂接口解析实例。
我正在使用以下代码
public interface ISimpleBarFactory
{
Bar CreateBar(int value);
}
public sealed class SimpleBarFactory : ISimpleBarFactory
{
private readonly Container _container;
public SimpleBarFactory (Container container)
{
_container = container;
}
public Bar CreateBar(int value)
{
_container.Register(() => new Bar(vlue));
return _container.GetInstance<Bar>();
}
}
解析具有构造函数参数的实例。
但是,在使用工厂实例化服务时出现以下异常class:
The container can't be changed after the first call to GetInstance, GetAllInstances and Verify.
使用带参数的工厂接口解析实例的正确方法是什么?
更新
以下是我的代码。我正在从 Ninject.
迁移代码public interface IFormsUIFactory
{
AccountUI CreateAccountUI(Account account);
}
public class FormsUIFactory
{
private readonly IFormsUIFactory _uiFactory;
public FormsUIFactory(IFormsUIFactory uiFactory)
{
_uiFactory = uiFactory;
}
public void CreateAccountUI(Account account)
{
_uiFactory.CreateAccountUI(account);
}
}
UI class 待注入
public partial class AccountUI : Form
{
private readonly IAccountMaintenanceProcessor _processor;
private readonly Account _account;
public AccountUI(IAccountMaintenanceProcessor accountProcessor, Account account)
{
_processor = accountProcessor;
_account = account;
}
}
实例化代码:
var account = new Account();
// Populate values for the account
var frm = _uiFactory.CreateAccountUI(account);
您遇到的问题是由于您将运行时数据(您的Account
)对象与组件混合在一起。您的 DI 容器负责构建组件的对象图。这些组件通常应该是无状态的,运行时数据应该使用方法调用流经对象图。在组件的构造函数中注入运行时数据,使这些组件具有状态,并以许多不同的方式使您的应用程序复杂化;您今天正在目睹这些并发症之一。但是这样做有很多缺点。例如,将运行时数据注入构造函数使得无法验证您的对象图(使用自动化测试)并使您的应用程序很容易在运行时中断。所以这绝不是 Simple Injector 特定的,但 Simple 确实通过在解析服务时不允许传递运行时值来使问题更加突出。
你可以找到关于这个的详细解释 here.
因此,请尽量让您的组件保持无状态,并且至少让运行时数据远离构造函数。实现此目的的一种简单方法是将 属性 添加到允许设置您正在使用的实体的表单。可以将通用界面添加到表单以允许这样做:
interface IForm<TEntity>
{
TEntity Entity { get; set; }
}
此通用接口可用于 IFormFactory
抽象:
interface IFormFactory
{
TForm CreateFormFor<TForm, TEntity>(TEntity entity)
where TForm : Form, IForm<TEntity>;
}
为 IFormFactory
创建一个实现就这么简单:
public class FormFactory : IFormFactory
{
private readonly Container container;
public FormFactory(Container container) {
this.container = container;
}
public TForm CreateFormFor<TForm, TEntity>(TEntity entity)
where TForm : Form, IForm<TEntity> {
var form = this.container.GetInstance<TForm>();
form.Entity = entity;
return form;
}
}