简单的注射器属性注射

时间:2016-01-21 22:51:41

标签: ninject inversion-of-control ioc-container simple-injector

如何使用Simple Injector执行属性注入。

你使用的Ninject如下:

[Inject]
public IUnitOfWork UnitOfWork { get; set; }

如何使用Simple Injector完成相同的操作。我尝试在线寻找解决方案,但没有运气。

为什么我要使用Property Injection?

我想使用属性注入在我的基本控制器中设置工作单元,以便它创建一个新的工作单元OnActionExecuting并提交更改OnResultExecuted。这也意味着我不必通过构造函数创建的每个新控制器传递UoW。

2 个答案:

答案 0 :(得分:3)

另一种选择是使用RegisterInitializer方法:

container.RegisterInitializer<BaseControllerType>(controller =>
{
   controller.UnitOfWork = container.GetInstance<IUnitOfWork>();
}

它保留了组合根目录中的所有配置,并且不会使用各种属性污染您的代码库。

更新:(按照承诺)

虽然这是对您的问题的直接回答,但我必须为您提供更好的选择,因为由于多种原因,对此基础类的使用是IMO不正确的设计。

  1. 抽象类可以成为真正的PITA类,因为它们往往会朝着具有各种交叉问题的神级增长。
  2. 抽象类,特别是与属性注入一起使用时,隐藏了所需的依赖项。
  3. 关注第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; }