【问题标题】:How to properly configure Simple Injector with FluentScheduler如何使用 FluentScheduler 正确配置 Simple Injector
【发布时间】:2018-12-28 04:21:48
【问题描述】:

我有以下简单注射器的配置。

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

RegisterScheduler 初始化并调度作业。

BackgroundScheduler 如下所示:

public class BackgroundScheduler : IJob, IRegisteredObject
    {
        IUserService _userService;

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

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

BackgroundScheduler 依赖于 IUserService。当我尝试在后台调度程序中注入 IUserService 时,出现以下异常:

BackgroundScheduler 注册为“混合 Web 请求/线程范围”生活方式,但在活动(混合 Web 请求/线程范围)范围的上下文之外请求实例。

堆栈跟踪:

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: 

我不知道为什么会这样?

【问题讨论】:

    标签: c# asp.net-mvc dependency-injection simple-injector fluentscheduler


    【解决方案1】:

    FleuntScheduler 的 IJobFactory 已弃用,但不会被另一个扩展点替换。虽然官方文档似乎没有任何描述如何有效地从你的 DI 容器中解决你的工作,但维护者的观点似乎是你 register your jobs as a closure

    由于使用闭包意味着解析作业,将其包装在作用域中,并在 Simple Injector 中注册您的作业,因此最实用的解决方案是将这个逻辑移动到扩展方法中。这可能如下所示:

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

    在此示例中,IMyJob 是应用程序指定的抽象。这样做时,您可以防止您的应用程序代码需要依赖 FluentScheduler。

    这个扩展方法可以使用如下:

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

    其他选项是使用(现已弃用)IJobFactory。这需要您的作业实现 FluentScheduler 的 IJob 接口。实现作业工厂的难点在于您需要找到一种方法将操作包装在作用域中。

    诀窍是用一个作用域来包装你的工作的解析和执行。由于 FluentScheduler 中的拦截点似乎有限,我认为您可以做到这一点的唯一方法是更改​​您的作业工厂,使其返回一个装饰器,该装饰器将实际作业的创建推迟到装饰器的 Execute 方法被调用.装饰器的Execute 可以开始作用域、解析实际作业、执行该作业并在内部处置作用域。

    这是使用作用域的工厂:

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

    您可以通过设置JobManager.JobFactory 来使用该工厂,就像您已经在做的那样:

    JobManager.JobFactory = new SimpleInjectorJobFactory(container);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-16
      • 1970-01-01
      相关资源
      最近更新 更多