在Controller的构造函数外部使用依赖注入

时间:2012-02-22 19:46:55

标签: c# asp.net-mvc-3 dependency-injection

所以这就是问题,我的mvc3项目使用依赖注入,并有一个基本的通用IRepository类,其他存储库派生出来。

所以我可以提前做并在控制器中执行此操作:

public class SomethingController
{
    IOrderRepository repository;

    public SomethingController(IOrderRepository repo)
    {
        this.repository = repo;
    }

    public ActionResult SaveOrder(Order order)
    {
        repository.add(order)
        unitOfWork.CommitChanges();  // THIS works!
    }
}

但现在我需要在这样的自定义静态非控制器中使用其中一个存储库:

static class OrderParser
{
    private IOrderRepository repo;

    public static DoWork()
    {
        repo = DependencyResolver.Current.GetService<IOrderRepository>();

        var ordersInDB = repo.GetAllOrders(); //THIS works!

        //But!
        var ordersForInsertion = new List<Order>();


        //do some backgroundworker magic                     
        //fetch txt files from an ftp server
        var ordersForInsertion = ParseTextFilesIntoOrders();

        foreach order in ordersForInsertion 
             repo.add(order)
        unitOfWork.CommitChanges();
        // THIS doesnt commit anything into the database
        // It also doesnt throw any exceptions
        // and repo isnt null or any of that
    }
}

所以,作为测试,我尝试过:

repo = DependencyResolver.Current.GetService<IOrderRepository>();

在第一个例子中的控制器类内部,看看它是否也没有提交东西,但它没有。 (以正确的方式执行[通过构造函数注入存储库和unitOfWork]工作!)

所以它必须与DependencyResolver有关,对吧?

注意:如果您需要我发布更多代码,请立即离开,我会在这里快速编辑它!

注2:Thanx!

EDIT1:

关于w0lf的超快答案 这里有更多信息:

我的OrderParser类包含一个backgroundWorker,它应该是:

  • 睡一小时
  • 列出FTP服务器中的所有文件(纯文本文件)。
  • 丢弃已经解析为数据库的那些。
  • 将新文件解析为Order对象。
  • 将对象提交到db。
  • 一遍又一遍地开始直到电源熄灭或者其他东西:)

所有这一切都必须在没有任何用户操作的情况下发生,这意味着该操作不是来自控制器,因此我所做的就是:

在我的引导程序类

Initialise()
{
    //Unrelated stuff
    OrderParser.DoWork()
}

这就是为什么我将它作为静态类实现的原因(很容易变成非静态的)

EDIT2:

这将是:

class OrderParser
{
    private IOrderRepository repo;

    public OrderParser(IOrderRepository foo)
    {
        this.repo = foo;
    }
    public static DoWork()
    {
        //use repo var!
    }
}

但是当我在bootstrapper Initialize()方法中实例化时,我该怎么做,例如:

class bootstrapper
{
    Initialize()
    {
        var parser = new OrderParser(/*how do i pass the dependency here?*/)
        parser.DoWork();
    }
}

EDIT3:

这是一些更多的测试,请耐心等待我!

这是我的OrderParser:

class OrderParser
{
    public OrderParser(IOrderRepository foo, IContext unitOfWork)
    {
        foo.getall(); 

        foo.add(some_order);
        unitOfWork.commit(); 

    }
}

测试1:

public class SomeController
{
    IOrderRepository repository;

    public SomeController(IOrderRepository repo)
    {
        this.repository = repo;
    }

    public ActionResult SomeMethod(Order order)
    {
        repository.GetAll();    //WORKS

        repository.add(order)
        unitOfWork.CommitChanges();  // WORKS
    }
}

TEST2:

class bootstrapper
{
    Initialize()
    {
        //Build unity container..
        //set resolver..

        var parser = new OrderParser(container.Resolve<IOrderRepository>, container.Resolve<IContext>)
        //can getAll, cant commit.
    }
}

TEST3:

public class SomeController
{
    IOrderRepository controllers_repository;

    public SomeController(IOrderRepository repo)
    {
        this.controllers_repository = repo;
    }

    public ActionResult SomeMethod(Order order)
    {
        var parser = new OrderParser(DependencyResolver.Current.GetService<IOrderRepository>,
        DependencyResolver.Current.GetService<IContext>)   
        //can do getall, no commits


        var parser = new OrderParser(controllers_repository, controllers_icontext)
        // obviously works (can getall and commit)
    }
}

顺便说一句,当我说“不能提交”时,并不是我得到一个例外或者存储库是null,nope。代码运行就像它没关系,只有DB不会改变。

3 个答案:

答案 0 :(得分:5)

一种可能的解决方案是使OrderParser类非静态,并在Controller的构造函数中注入一个触发操作的实例(DoWork)。

然后让OrderParser的构造函数获取IOrderRepository参数,IoC容器很乐意处理它。

另外,请注意以下事项:

DependencyResolver.Current.GetService<ISomeInterface>();

这称为服务定位器,它是considered to be an anti-pattern。如果可能,请避免使用它。

基本上,您应该引用DependencyResolver.Current.GetService的唯一地方是IControllerFactory的实施,它首先启用了DI。

<强>更新

最好是在另一个应用程序中执行此操作而不是MVC网站。两种选择是:

  • 基于计时器执行该操作的Windows服务
  • 每小时使用Windows任务计划程序运行的控制台应用程序

这些作为单独的应用程序将有自己的Composition roots来处理对象实例化/依赖注入问题。

但是,如果您不得不通过Web应用程序执行此操作(例如 - 您拥有仅允许Web应用程序的托管),那么您可能会发现对“不要使用Dependencey Resolver直接“在应用程序启动时统治并做这样的事情:

var runner = DependencyResolver.Current.GetService<OrderParsingRunner>();
runner.StartWorking();

当然,OrderParsingRunner类看起来像这样:

public class OrderParsingRunner
{
    private readonly OrderParser orderParser;

    public OrderParsingRunner(OrderParser orderParser)
    {
        this.orderParser = orderParser;
    }

    public StartWorking()
    {
        TaskFactory.StartNew(() => 
            { 
                DoWorkHourly();
            });
    }

    private DoWorkHourly()
    {
        while(true)
        {
            Thread.Sleep(TimeSpan.FromHours(1));

            orderParser.DoWork();
        }
    }
}

免责声明:我实际上没有编译/运行此代码,我只是为了说明这个概念而编写它。

请注意,这是一种解决方法,而不是实际的解决方案。如果可能,建议您使用其他应用程序执行后台任务。

答案 1 :(得分:2)

使用DI时,您不需要静态辅助类。您可以将所有内容视为“服务”,并在构造函数中声明您的依赖项。这就是我的想法。然后在您需要时为您创建所有内容。

因此,我将静态类更改为非静态类,并通过构造函数将其注入需要的地方。

回答编辑2

将容器传递给bootstrap类。

class bootstrapper
{
    Initialize(DependencyResolver container)
    {
        var parser = new OrderParser(container.Resolve<IOrderRepository>());
        parser.DoWork();
    }
}

修改

我实际上会这样做......

var parser = container.Resolve<OrderParser>();

让依赖解析器解决所有问题!

答案 2 :(得分:1)

由于这是后台任务,因此请勿在Web应用程序中运行此任务。而是在Windows中使用服务或计划的应用程序。 然后,您可以在应用程序初始化期间或使用[Dependency]属性

解析您的引用

http://msdn.microsoft.com/en-us/library/zt39148a(v=vs.100).aspx http://stackoverflow.com/questions/888479/using-unity-framework-inject-into-system-windows-forms-form-page