Castle.Windsor lifestyle depending on context?

后端 未结 5 976
孤街浪徒
孤街浪徒 2020-12-29 04:44

I have a web application where many components are registered using .LifestylePerWebRequest(), now I\'ve decided to implement Quartz.NET, a .NE

5条回答
  •  忘掉有多难
    2020-12-29 05:29

    I've had a very similar problem recently - I wanted to be able to run initialisation code based off my container in the Application startup, when HttpContext.Request does not yet exist. I didn't find any way of doing it, so I modified the source of the PerWebRequestLifestyleModule to allow me to do what I wanted. Unfortunately it didn't seem possible to make this change without recompiling Windsor - I was hoping I would be able to do it in an extensible way so I could continue to use the main distribution of Windsor.

    Anyway, to make this work, I modified the GetScope function of the PerWebRequestLifestyleModule so that if it was NOT running in a HttpContext (or if HttpContext.Request throws an exception, like it does in Application_Start) then it will look for a Scope started from the container instead. This allows me to use my container in Application_Start using the following code:

    using (var scope = container.BeginScope())
    {
        // LifestylePerWebRequest components will now be scoped to this explicit scope instead
        // _container.Resolve<...>()
    
    }
    

    There's no need to worry about explicitly disposing of things, because they will be disposed when the Scope is.

    I've put the full code for the module below. I had to shuffle a couple of other things around within this class for it to work, but it's essentially the same.

    public class PerWebRequestLifestyleModule : IHttpModule
    {
        private const string key = "castle.per-web-request-lifestyle-cache";
        private static bool allowDefaultScopeOutOfHttpContext = true;
        private static bool initialized;
    
        public void Dispose()
        {
        }
    
        public void Init(HttpApplication context)
        {
            initialized = true;
            context.EndRequest += Application_EndRequest;
        }
    
        protected void Application_EndRequest(Object sender, EventArgs e)
        {
            var application = (HttpApplication)sender;
            var scope = GetScope(application.Context, createIfNotPresent: false);
            if (scope != null)
            {
                scope.Dispose();
            }
        }
    
        private static bool IsRequestAvailable()
        {
            if (HttpContext.Current == null)
            {
                return false;
            }
    
            try
            {
                if (HttpContext.Current.Request == null)
                {
                    return false;
                }
                return true;
            }
            catch (HttpException)
            {
                return false;
            }
        }
    
        internal static ILifetimeScope GetScope()
        {
            var context = HttpContext.Current;
            if (initialized)
            {
                return GetScope(context, createIfNotPresent: true);
            }
            else if (allowDefaultScopeOutOfHttpContext && !IsRequestAvailable())
            {
                // We're not running within a Http Request.  If the option has been set to allow a normal scope to 
                // be used in this situation, we'll use that instead
                ILifetimeScope scope = CallContextLifetimeScope.ObtainCurrentScope();
                if (scope == null)
                {
                    throw new InvalidOperationException("Not running within a Http Request, and no Scope was manually created.  Either run from within a request, or call container.BeginScope()");
                }
                return scope;
            }
            else if (context == null)
            {
                throw new InvalidOperationException(
                        "HttpContext.Current is null. PerWebRequestLifestyle can only be used in ASP.Net");
            }
            else
            {
                EnsureInitialized();
                return GetScope(context, createIfNotPresent: true);
            }
        }
    
        /// 
        ///   Returns current request's scope and detaches it from the request context.
        ///   Does not throw if scope or context not present. To be used for disposing of the context.
        /// 
        /// 
        internal static ILifetimeScope YieldScope()
        {
            var context = HttpContext.Current;
            if (context == null)
            {
                return null;
            }
            var scope = GetScope(context, createIfNotPresent: true);
            if (scope != null)
            {
                context.Items.Remove(key);
            }
            return scope;
        }
    
        private static void EnsureInitialized()
        {
            if (initialized)
            {
                return;
            }
            var message = new StringBuilder();
            message.AppendLine("Looks like you forgot to register the http module " + typeof(PerWebRequestLifestyleModule).FullName);
            message.AppendLine("To fix this add");
            message.AppendLine("");
            message.AppendLine("to the  section on your web.config.");
            if (HttpRuntime.UsingIntegratedPipeline)
            {
                message.AppendLine(
                    "Windsor also detected you're running IIS in Integrated Pipeline mode. This means that you also need to add the module to the  section under .");
            }
            else
            {
                message.AppendLine(
                    "If you plan running on IIS in Integrated Pipeline mode, you also need to add the module to the  section under .");
            }
    #if !DOTNET35
            message.AppendLine("Alternatively make sure you have " + PerWebRequestLifestyleModuleRegistration.MicrosoftWebInfrastructureDll +
                               " assembly in your GAC (it is installed by ASP.NET MVC3 or WebMatrix) and Windsor will be able to register the module automatically without having to add anything to the config file.");
    #endif
            throw new ComponentResolutionException(message.ToString());
        }
    
        private static ILifetimeScope GetScope(HttpContext context, bool createIfNotPresent)
        {
            var candidates = (ILifetimeScope)context.Items[key];
            if (candidates == null && createIfNotPresent)
            {
                candidates = new DefaultLifetimeScope(new ScopeCache());
                context.Items[key] = candidates;
            }
            return candidates;
        }
    }
    

提交回复
热议问题