如何使用Simple Injector执行属性注入。
你使用的Ninject如下:
[Inject]
public IUnitOfWork UnitOfWork { get; set; }
如何使用Simple Injector完成相同的操作。我尝试在线寻找解决方案,但没有运气。
为什么我要使用Property Injection?
我想使用属性注入在我的基本控制器中设置工作单元,以便它创建一个新的工作单元OnActionExecuting
并提交更改OnResultExecuted
。这也意味着我不必通过构造函数创建的每个新控制器传递UoW。
答案 0 :(得分:3)
另一种选择是使用RegisterInitializer方法:
container.RegisterInitializer<BaseControllerType>(controller =>
{
controller.UnitOfWork = container.GetInstance<IUnitOfWork>();
}
它保留了组合根目录中的所有配置,并且不会使用各种属性污染您的代码库。
更新:(按照承诺)
虽然这是对您的问题的直接回答,但我必须为您提供更好的选择,因为由于多种原因,对此基础类的使用是IMO不正确的设计。
关注第2点。当您想要对从基本控制器继承的控制器进行单元测试时,您无法知道此控制器是否依赖于IUnitOfWork
。这可以通过使用构造函数注入而不是属性注入来解决:
protected abstract class BaseController : Controller
{
protected readonly IUnitOfWork uoW;
protected BaseController (IUnitOfWork uoW)
{
this.uoW = uoW;
}
}
public class SomeController : BaseController
{
public SomeController(IUnitOfWork uoW) : base(uoW) { }
}
虽然这解决了第2点,但第1点仍然潜伏着。正如您所说,您想要这个的主要原因是您不希望在每个Action
方法中提交更改。必须在请求完成时通过上下文保存更改。以这种方式思考设计是一件好事,因为保存更改是,或者可以被视为cross cutting concern,而您实现此方法的方式或多或少被称为AOP。
如果它来到AOP,特别是如果你在控制器的动作方法中使用原子动作,那么可以使用更好,更SOLID更灵活的设计来处理这非常好。
我指的是Command / Handler模式,详细描述了here(也读this for the query part of your application)。
使用这种模式,您不会注入通用的IUnitOfWork
抽象,而是注入特定的所需ICommandHandler<TCommand>
抽象。
操作方法将为此特定操作触发负责的commandhandler。所有命令处理程序都可以通过单个开放式通用SaveChangesCommandHandlerDecorator
,“ValidationDecorator”,“CheckPermissionsDecorator”等进行简单修饰......
一个简单的例子:
public class MoveCustomerCommand
{
public int CustomerId;
public Address NewAddress;
}
public class MoveCustomerCommandHandler : ICommandHandler<MoveCustomerCommand>
{
public void Handle(MoveCustomerCommand command)
{
// retrieve customer from database
// change address
}
}
public class SaveChangesCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> decoratee;
private readonly DbContext db;
public SaveChangesCommandHandlerDecorator(
ICommandHandler<TCommand> decoratee, DbContext db)
{
this.decoratee = decoratee;
this.db = db;
}
public void Handle(TCommand command)
{
this.decoratee.Handle(command);
this.db.SaveChanges();
}
}
// Register as
container.Register(typeof(ICommandHandler<>), new []{Assembly.GetExecutingAssembly() });
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(SaveChangesCommandHandlerDecorator<>));
// And use in controller as
public ActionResult MoveCustomer(int customerId, Address address)
{
var command = new MoveCustomerCommand
{ CustomerId = customerId, Address = address };
this.commandHandler.Handle(command);
return View(new ResultModel());
}
这样可以保持控制器的清洁,让它完成它必须做的事情,即业务逻辑(本例中为commandhandler实现)和视图之间的层。
答案 1 :(得分:2)
需要创建以下内容:
首先创建属性类
[System.AttributeUsage(System.AttributeTargets.Property]
public class Inject : Attribute
{
}
然后创建自定义属性行为
class PropertySelectionBehavior<TAttribute> : IPropertySelectionBehavior
where TAttribute : Attribute
{
public bool SelectProperty(Type type, PropertyInfo prop)
{
return prop.GetCustomAttributes(typeof(TAttribute)).Any();
}
}
最后告诉容器使用自定义行为
container.Options.PropertySelectionBehavior = new PropertySelectionBehavior<Inject>();
剩下要做的就是用属性
装饰属性[Inject]
public IUnitOfWork UnitOfWork { get; set; }