Ejemplo n.º 1
0
        public async Task Run(Func <IWorkContextScope, Task> process, string shellName)
        {
            var shellContext = _hostContainer.Resolve <IOrchardHost>().GetShellContext(new ShellSettings {
                Name = shellName
            });
            // We need a single HCA, and thus the same HttpContext throughout this scope to carry the work context. Especially
            // important for async code, see: https://github.com/OrchardCMS/Orchard/issues/4338
            // Using InstancePerLifetimeScope() inside the shell ContinerBuilder still yields multiple instantiations.
            var hca = new AppHostHttpContextAccessor();

            using (var scope = shellContext.LifetimeScope.BeginLifetimeScope(
                       RunScopeTag,
                       builder =>
            {
                builder.RegisterInstance(hca).As <IHttpContextAccessor>();
                // Needed so it gets the AppHostHttpContextAccessor instance registered in Run().
                builder.RegisterType <WorkContextAccessor>().As <IWorkContextAccessor>().SingleInstance();
                builder.RegisterType <ProcessingEngineTaskAddedHandler>()
                .As <IProcessingEngine>()
                .As <IProcessingEngineTaskAddedHandler>()
                .SingleInstance();
                builder.RegisterType <ShellChangeHandler>()
                .As <IShellChangeHandler>()
                .As <IEventHandler>()
                .Named <IEventHandler>(typeof(IShellDescriptorManagerEventHandler).Name)
                .Named <IEventHandler>(typeof(IShellSettingsManagerEventHandler).Name)
                .SingleInstance();
            }))
            {
                HttpContextBase httpContext;

                // Resolving will fail if it's just the setup shell. TryResolve() would still cause this exception.
                try
                {
                    // Will return the stub from Orchard.Mvc.MvcModule. There are some direct resolve calls to HttpContextBase in
                    // Orchard but it doesn't matter here (otherwise it does: https://github.com/OrchardCMS/Orchard/issues/4607) as the
                    // point is to keep the WorkContext in HttpContext.Items the same throughout this scope (unless a new WCS is
                    // started somewhere).
                    httpContext = scope.Resolve <HttpContextBase>();
                }
                catch (DependencyResolutionException ex)
                {
                    // Unfortunately the only way to identify the specific exception is by its message.
                    if (ex.Message.StartsWith("No scope with a Tag matching 'work' is visible from the scope in which the instance"))
                    {
                        httpContext = new AppHostHttpContextAccessor.HttpContextPlaceholder();
                    }
                    else
                    {
                        throw;
                    }
                }

                hca.Set(httpContext);

                var logger              = scope.Resolve <ILoggerService>();
                var orchardHost         = scope.Resolve <IOrchardHost>();
                var previousTenantState = TenantState.Invalid;

                orchardHost.BeginRequest();

                try
                {
                    using (var workContext = scope.CreateWorkContextScope(httpContext))
                    {
                        try
                        {
                            await process(workContext);

                            previousTenantState = workContext.Resolve <ShellSettings>().State;
                        }
                        catch (Exception ex)
                        {
                            if (ex.IsFatal())
                            {
                                throw;
                            }

                            logger.Error(ex, "Error when executing work inside Orchard App Host.");
                            throw;
                        }
                    }
                }
                finally
                {
                    var shellSettingManagerEventHandler = _hostContainer.Resolve <IShellSettingsManagerEventHandler>();
                    var shellSettings = scope.Resolve <IShellSettingsManager>().LoadSettings().SingleOrDefault(settings => settings.Name == shellName);

                    // This means that setup just run. During setup the shell-tracking services are not registered.
                    if (previousTenantState == TenantState.Invalid && shellSettings != null && shellSettings.State == TenantState.Running)
                    {
                        shellSettingManagerEventHandler.Saved(shellSettings);
                    }
                    else
                    {
                        // Due to possibly await-ed calls in the process we keep track of everything that is stored in ContextState<T> normally.
                        // This is needed because ContextState<T> looses state on thread switch.
                        // Here we re-apply every change so the necessary services will get to know everything.
                        var shellChangeHandler = scope.Resolve <IShellChangeHandler>();
                        var shellDescriptorManagerEventHandler = _hostContainer.Resolve <IShellDescriptorManagerEventHandler>();
                        foreach (var changedShellDescriptor in shellChangeHandler.GetChangedShellDescriptors())
                        {
                            shellDescriptorManagerEventHandler.Changed(changedShellDescriptor.ShellDescriptor, changedShellDescriptor.TenantName);
                        }

                        foreach (var changedShellSettings in shellChangeHandler.GetChangedShellSettings())
                        {
                            shellSettingManagerEventHandler.Saved(changedShellSettings);
                        }

                        var processingEngine = _hostContainer.Resolve <IProcessingEngine>();
                        var processingEngineTaskAddedHandler = scope.Resolve <IProcessingEngineTaskAddedHandler>();
                        foreach (var task in processingEngineTaskAddedHandler.GetAddedTasks())
                        {
                            processingEngine.AddTask(task.ShellSettings, task.ShellDescriptor, task.MessageName, task.Parameters);
                        }
                    }

                    // Either this or running tasks through IProcessingEngine and restarting shells manually.
                    // EndRequest() can have unwanted side effects in the future.
                    orchardHost.EndRequest();
                }
            }
        }
        public async Task Run(Func <IWorkContextScope, Task> process, string shellName)
        {
            var shellContext = _hostContainer.Resolve <IOrchardHost>().GetShellContext(new ShellSettings {
                Name = shellName
            });
            // We need a single HCA, and thus the same HttpContext throughout this scope to carry the work context.
            // Especially important for async code, see: https://github.com/OrchardCMS/Orchard/issues/4338
            // Using InstancePerLifetimeScope() inside the shell ContinerBuilder still yields multiple instantiations.
            var hca = new AppHostHttpContextAccessor();

            using (var scope = shellContext.LifetimeScope.BeginLifetimeScope(
                       RunScopeTag,
                       builder =>
            {
                builder.RegisterInstance(hca).As <IHttpContextAccessor>();
                // Needed so it gets the AppHostHttpContextAccessor instance registered in Run().
                builder.RegisterType <WorkContextAccessor>().As <IWorkContextAccessor>().SingleInstance();
                builder.RegisterType <ProcessingEngineTaskAddedHandler>()
                .As <IProcessingEngine>()
                .As <IProcessingEngineTaskAddedHandler>()
                .SingleInstance();
                builder.RegisterType <ShellChangeHandler>()
                .As <IShellChangeHandler>()
                .As <IEventHandler>()
                .Named <IEventHandler>(typeof(IShellDescriptorManagerEventHandler).Name)
                .Named <IEventHandler>(typeof(IShellSettingsManagerEventHandler).Name)
                .SingleInstance();
            }))
            {
                var httpContext = new MvcModule.HttpContextPlaceholder(() => "http://localhost");

                hca.Set(httpContext);

                var logger              = scope.Resolve <ILoggerService>();
                var orchardHost         = scope.Resolve <IOrchardHost>();
                var previousTenantState = TenantState.Invalid;

                orchardHost.BeginRequest();

                try
                {
                    using (var workContext = scope.CreateWorkContextScope(httpContext))
                    {
                        try
                        {
                            await process(workContext);

                            previousTenantState = workContext.Resolve <ShellSettings>().State;
                        }
                        catch (Exception ex)
                        {
                            if (ex.IsFatal())
                            {
                                throw;
                            }

                            logger.Error(ex, "Error when executing work inside Orchard App Host.");
                            throw;
                        }
                    }
                }
                finally
                {
                    var shellSettingManagerEventHandler = _hostContainer.Resolve <IShellSettingsManagerEventHandler>();
                    var shellSettings = scope
                                        .Resolve <IShellSettingsManager>()
                                        .LoadSettings()
                                        .SingleOrDefault(settings => settings.Name == shellName);

                    // This means that setup just run. During setup the shell-tracking services are not registered.
                    if (previousTenantState == TenantState.Invalid &&
                        shellSettings != null &&
                        shellSettings.State == TenantState.Running)
                    {
                        shellSettingManagerEventHandler.Saved(shellSettings);
                    }
                    else
                    {
                        // Due to possibly await-ed calls in the process we keep track of everything that is stored in
                        // ContextState<T> normally.
                        // This is needed because ContextState<T> looses state on thread switch.
                        // Here we re-apply every change so the necessary services will get to know everything.
                        var shellChangeHandler = scope.Resolve <IShellChangeHandler>();
                        var shellDescriptorManagerEventHandler = _hostContainer.Resolve <IShellDescriptorManagerEventHandler>();
                        foreach (var changedShellDescriptor in shellChangeHandler.GetChangedShellDescriptors())
                        {
                            shellDescriptorManagerEventHandler
                            .Changed(changedShellDescriptor.ShellDescriptor, changedShellDescriptor.TenantName);
                        }

                        foreach (var changedShellSettings in shellChangeHandler.GetChangedShellSettings())
                        {
                            shellSettingManagerEventHandler.Saved(changedShellSettings);
                        }

                        var processingEngine = _hostContainer.Resolve <IProcessingEngine>();
                        var processingEngineTaskAddedHandler = scope.Resolve <IProcessingEngineTaskAddedHandler>();
                        foreach (var task in processingEngineTaskAddedHandler.GetAddedTasks())
                        {
                            processingEngine.AddTask(task.ShellSettings, task.ShellDescriptor, task.MessageName, task.Parameters);
                        }
                    }

                    // Either this or running tasks through IProcessingEngine and restarting shells manually.
                    // EndRequest() can have unwanted side effects in the future.
                    orchardHost.EndRequest();
                }
            }
        }