简体   繁体   English

如何使用 FluentScheduler 正确配置 Simple Injector

[英]How to properly configure Simple Injector with FluentScheduler

I have following configuration for Simple Injector.我有以下简单注入器的配置。

public class SimpleInjectorIntegrator
{
    private static Container container;

    public static Container Setup()
    {
        container = new Container();
        container.Options.DefaultScopedLifestyle = Lifestyle.CreateHybrid(
            defaultLifestyle: new WebRequestLifestyle(),
            fallbackLifestyle: new ThreadScopedLifestyle());

        container.Register<IUserService, UserService>(Lifestyle.Scoped);
        container.Register<IJob, BackgroundScheduler>(Lifestyle.Scoped);

        JobManager.JobFactory = new SimpleInjectorJobFactory(container);
        JobManager.Initialize(new RegisterScheduler());
    }
}

public class SimpleInjectorJobFactory : IJobFactory
{
    Container Container;

    public SimpleInjectorJobFactory(Container container)
    {
        this.Container = container;
    }

    public IJob GetJobInstance<T>() where T : IJob
    {
        return Container.GetInstance<IJob>();
    }
}

The RegisterScheduler initializes and Schedules the job. RegisterScheduler 初始化并安排作业。

BackgroundScheduler looks like as below: BackgroundScheduler 如下所示:

public class BackgroundScheduler : IJob, IRegisteredObject
    {
        IUserService _userService;

        public BackgroundScheduler(IUserService userService)
        {
            _userService = userService;
        }

        public void Execute() 
        {
            _userService.GetAll();
        }
    }

The BackgroundScheduler depends on IUserService. BackgroundScheduler 依赖于 IUserService。 When I try to inject IUserService in Background scheduler I got following exception:当我尝试在后台调度程序中注入 IUserService 时,出现以下异常:

BackgroundScheduler is registered as 'Hybrid Web Request / Thread Scoped' lifestyle, but the instance is requested outside the context of an active (Hybrid Web Request / Thread Scoped) scope. BackgroundScheduler 注册为“混合 Web 请求/线程作用域”生活方式,但该实例是在活动(混合 Web 请求/线程作用域)范围的上下文之外请求的。

Stack trace:堆栈跟踪:

SimpleInjector.ActivationException was unhandled by user code
  HResult=-2146233088
  Message=The BackgroundScheduler is registered as 'Hybrid Web Request / Thread Scoped' lifestyle, but the instance is requested outside the context of an active (Hybrid Web Request / Thread Scoped) scope.
  Source=SimpleInjector
  StackTrace:
       at SimpleInjector.Scope.GetScopelessInstance[TImplementation](ScopedRegistration`1 registration)
       at SimpleInjector.Scope.GetInstance[TImplementation](ScopedRegistration`1 registration, Scope scope)
       at SimpleInjector.Advanced.Internal.LazyScopedRegistration`1.GetInstance(Scope scope)
       at lambda_method(Closure )
       at SimpleInjector.InstanceProducer.BuildAndReplaceInstanceCreatorAndCreateFirstInstance()
       at SimpleInjector.InstanceProducer.GetInstance()
       at SimpleInjector.Container.GetInstanceFromProducer(InstanceProducer instanceProducer, Type serviceType)
       at SimpleInjector.Container.GetInstanceForRootType[TService]()
       at SimpleInjector.Container.GetInstance[TService]()
       at FluentScheduler.JobManager.<>c__12`1.<GetJobAction>b__12_0() in __the_file_path_omitted__:line 76
       at System.Threading.Tasks.Task.InnerInvoke()
       at System.Threading.Tasks.Task.Execute()
  InnerException: 

I am not sure why this is happening?我不确定为什么会这样?

FleuntScheduler's IJobFactory is deprecated, while not being replaced with another extension point. FleuntScheduler 的IJobFactory已弃用,但不会被另一个扩展点取代。 Although the official documentation seems to lack any description on how to effectively resolve your jobs from your DI Container, the maintainer's point of view seems that you register your jobs as a closure .虽然官方文档似乎缺少任何关于如何有效地从您的 DI 容器中解决您的工作的描述,但维护者的观点似乎是您将您的工作注册为闭包

Since the use of a closure means resolving the job, wrapping it in a scope, and registering your job in Simple Injector, the most practical solution would be to move this logic into an extension method.由于使用闭包意味着解析作业,将其包装在一个范围内,并在 Simple Injector 中注册您的作业,因此最实用的解决方案是将此逻辑移动到扩展方法中。 This could look like this:这可能看起来像这样:

public static void AddFluentSchedulerJob<TJob>(
    this Container container, Action<Schedule> schedule)
    where TJob : class, IMyJob
{
    container.Register<TJob>();

    JobManager.AddJob(() =>
        {
            using (AsyncScopedLifestyle.BeginScope(container))
            {
                container.GetInstance<TJob>().Run();
            }
        },
        schedule);
}

In this example, IMyJob is an application-specified abstraction.在此示例中, IMyJob是应用程序指定的抽象。 When doing this, you prevent your application code from requiring to depend on FluentScheduler.这样做时,您可以防止应用程序代码依赖 FluentScheduler。

This extension method can be used as follows:此扩展方法可以按如下方式使用:

container.AddFluentSchedulerJob<MyAwesomeJob>(s => s.ToRunEvery(5).Seconds());

The other options is to use the (now deprecated) IJobFactory .其他选项是使用(现已弃用) IJobFactory This requires your jobs to implement FluentScheduler's IJob interface.这需要您的作业实现 FluentScheduler 的IJob接口。 The difficulty in implementing a job factory is that you need to find a way to wrap the operation in a scope.实现作业工厂的难点在于您需要找到一种方法将操作包装在一个范围内。

The trick is to wrap the resolve and execution of your job with a scope.诀窍是用一个范围来包装你的工作的解决和执行。 As there seem limited interception points in FluentScheduler, the only way I figured you can do this, is by altering your job factory in such way that it returns a decorator that postpones the creation of the real job until the decorator's Execute method is called.由于 FluentScheduler 中的拦截点似乎有限,我认为您可以执行此操作的唯一方法是更改您的作业工厂,使其返回一个装饰器,该装饰器会推迟实际作业的创建,直到调用装饰器的Execute方法。 The decorator's Execute can begin the scope, resolve the real job, execute that job, and dispose the scope internally.装饰器的Execute可以启动作用域,解析真正的作业,执行该作业,并在内部处理作用域。

Here's the factory that uses a scope:这是使用范围的工厂:

public class SimpleInjectorJobFactory : IJobFactory
{
    private readonly Container container;

    public SimpleInjectorJobFactory(Container container) => this.container = container;

    public IJob GetJobInstance<T>() where T : IJob
    {
        return new AsyncScopedJobDecorator(
            this.container,
            () => (IJob)this.container.GetInstance(typeof(T)));
    }

    private sealed class AsyncScopedJobDecorator : IJob
    {
        private readonly Container container;
        private readonly Func<IJob> decorateeFactory;

        public AsyncScopedJobDecorator(Container container, Func<IJob> decorateeFactory)
        {
            this.container = container;
            this.decorateeFactory = decorateeFactory;
        }

        public void Execute()
        {
            using (AsyncScopedLifestyle.BeginScope(this.container))
            {
                this.decorateeFactory().Execute();
            }
        }
    }
}

You can use that factory by setting the JobManager.JobFactory , as you are already doing:您可以通过设置JobManager.JobFactory来使用该工厂,就像您已经在做的那样:

JobManager.JobFactory = new SimpleInjectorJobFactory(container);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM