Castle.Windsor lifestyle depending on context?

后端 未结 5 968
孤街浪徒
孤街浪徒 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:42

    Ok, I figured out a very clean way to do this!

    First of all we'll need an implementation of IHandlerSelector, this can select a handler based on our opinion on the matter, or remain neutral (by returning null, which means "no opinion").

    /// 
    /// Emits an opinion about a component's lifestyle only if there are exactly two available handlers and one of them has a PerWebRequest lifestyle.
    /// 
    public class LifestyleSelector : IHandlerSelector
    {
        public bool HasOpinionAbout(string key, Type service)
        {
            return service != typeof(object); // for some reason, Castle passes typeof(object) if the service type is null.
        }
    
        public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
        {
            if (handlers.Length == 2 && handlers.Any(x => x.ComponentModel.LifestyleType == LifestyleType.PerWebRequest))
            {
                if (HttpContext.Current == null)
                {
                    return handlers.Single(x => x.ComponentModel.LifestyleType != LifestyleType.PerWebRequest);
                }
                else
                {
                    return handlers.Single(x => x.ComponentModel.LifestyleType == LifestyleType.PerWebRequest);
                }
            }
            return null; // we don't have an opinion in this case.
        }
    }
    

    I made it so the opinion is very limited on purpose. I'll be having an opinion only if there are exactly two handlers and one of them has PerWebRequest lifestyle; meaning the other one is probably the non-HttpContext alternative.

    We need to register this selector with Castle. I do so before I start registering any other components:

    container.Kernel.AddHandlerSelector(new LifestyleSelector());
    

    Lastly I wish I had any clue as to how I could copy my registration to avoid this:

    container.Register(
        AllTypes
            .FromAssemblyContaining()
            .Where(t => t.Name.EndsWith("Service"))
            .WithService.Select(IoC.SelectByInterfaceConvention)
            .LifestylePerWebRequest()
    );
    
    container.Register(
        AllTypes
            .FromAssemblyContaining()
            .Where(t => t.Name.EndsWith("Service"))
            .WithService.Select(IoC.SelectByInterfaceConvention)
            .LifestylePerThread()
    );
    

    If you can figure out a way to clone a registration, change the lifestyle and register both of them (using either container.Register or IRegistration.Register), please post it as an answer here! :)

    Update: In testing, I need to uniquely name the identical registrations, I did so like this:

    .NamedRandomly()
    
    
        public static ComponentRegistration NamedRandomly(this ComponentRegistration registration) where T : class
        {
            string name = registration.Implementation.FullName;
            string random = "{0}{{{1}}}".FormatWith(name, Guid.NewGuid());
            return registration.Named(random);
        }
    
        public static BasedOnDescriptor NamedRandomly(this BasedOnDescriptor registration)
        {
            return registration.Configure(x => x.NamedRandomly());
        }
    

提交回复
热议问题