在控制器的构造函数之外使用依赖注入 [英] Using Dependency Injection outside of a Controller's constructor

查看:80
本文介绍了在控制器的构造函数之外使用依赖注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这就是问题所在,我的mvc3项目使用了依赖注入,并且有一个基类Generic IRepository类,其他存储库都来自该类.

So here's the issue, my mvc3 project uses Dependency Injection and has a base Generic IRepository class from which other repositories derive.

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

So I can co ahead and do this in a controller:

public class SomethingController
{
    IOrderRepository repository;

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

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

但是现在我需要像这样在自定义静态非控制器中使用这些存储库之一:

But now i need to use one of those repositories in a custom static non-controller like this:

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
    }
}

因此,作为测试,我尝试这样做:

So, as a test, i tried doing:

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

在第一个示例中的控制器类内

,以查看它是否也没有提交东西,也没有提交东西.(以正确的方式[通过构造函数注入存储库和unitOfWork]起作用!)

inside a controller class like in the first example to see if it also didnt commit stuff, and it doesn't. (Doing it the right way [injecting repositories and the unitOfWork trough the constructors] works!)

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

So it has to be something to do with the DependencyResolver, right?

注意:如果您还需要我发布更多代码,请问一下,我将在此处快速对其进行编辑!

Note: if there is any more code you need me to post, ask away and I'll edit it in here in a flash!

注意2:感谢!

关于w0lf的超快速解答这是更多信息:

Regarding w0lf's super fast answer Here's some more info:

我的OrderParser类实现了一个backgroundWorker,它应该:

My OrderParser class implments a backgroundWorker which is supposed to:

  • 睡眠一个小时
  • 列出FTP服务器中的所有文件(纯txt文件).
  • 丢弃那些已经解析到数据库中的数据.
  • 将新文件解析为Order对象.
  • 将对象提交到数据库中.
  • 从头开始,直到断电或其他原因:)

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

All that has to happen without any user action, meaning, the action is not originated from a controller, hence all I do is:

在我的引导程序类中

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

这也是为什么我将其实现为静态类(易于更改为非静态类)的原因

And that's also why I implemented it as a static class ( easily changable to a non-static )

那会是这样的:

class OrderParser
{
    private IOrderRepository repo;

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

但是当我在bootstrapper Initialize()方法中将其实例化时,我将如何执行该操作,例如:

But then when i instance it in the bootstrapper Initialize() method, how would i do that, e.g.:

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

这里还有更多测试,请多多包涵!

Here's some more testing, please bear with me!

再次是我的OrderParser:

Here's my OrderParser again:

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

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

    }
}


Test1:

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)
    }
}

顺便说一句,当我说无法提交"时,并不是我得到了异常,或者存储库为空,不是.代码运行正常,只有数据库不会更改.

By the way, when i say "can't commit" it's not that i get an exception or the repositories are null, nope. the code runs as if it were okay, only the DB won't change.

推荐答案

一种可行的解决方案是使 OrderParser 类成为非静态类,并将其实例注入到触发该方法的Controller的构造函数中.操作( DoWork ).

One possible solution is to make the OrderParser class non-static and inject an instance of it in the constructor of the Controller that triggers the action (DoWork).

然后使 OrderParser 的构造函数接受IOrderRepository参数,然后IoC容器将很乐意处理它.

Then make OrderParser's constructor take an IOrderRepository parameter and the IoC container will gladly take care of it.

还要提防类似的事情:

DependencyResolver.Current.GetService<ISomeInterface>();

这称为服务定位器,它被认为是反模式.尽可能避免.

This is called Service Locator and it's considered to be an anti-pattern. Avoid it if possible.

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

Basically, the only place where you should reference DependencyResolver.Current.GetService is your implementation of IControllerFactory that enables DI in the first place.

更新:

最好在MVC网站以外的其他应用程序中执行此操作.有两种选择:

It would be best if you did this in another application than your MVC website. Two alternatives would be:

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

这些作为单独的应用程序将具有自己的组合根将处理对象实例化/依赖注入问题.

These, being separate applications would have their own Composition roots that would deal with the object instantiation / dependency injection issue.

但是,如果您被限制从Web应用程序执行此操作(例如-您的主机只允许Web应用程序),那么您可能会发现可以接受对不要使用直接依从解析器"规则,并在应用程序启动时执行以下操作:

If, however, you are constrained to do this from your web app (for example - you have a hosting that only allows web apps), then you may find it acceptable to make an exception to the "Don't use the Dependencey Resolver directly" rule and do somehing like this on the application startup:

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

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

Of course, the OrderParsingRunner class would look something like this:

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();
        }
    }
}

免责声明:我实际上并未编译/运行此代码,我只是编写了此代码以说明概念.

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

Please note that this is a workaround rather than an actual solution. It's recommended that you use another application for the background tasks if possible.

这篇关于在控制器的构造函数之外使用依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆