代码之家  ›  专栏  ›  技术社区  ›  Carles Company

使用ASP.NET MVC的Castle项目每会话生活方式

  •  6
  • Carles Company  · 技术社区  · 15 年前

    我想要一个类来存储搜索选项:

    public interface ISearchOptions{
        public string Filter{get;set;}
        public string SortOrder{get;set;}
    }
    
    public class SearchOptions{
        public string Filter{get;set;}
        public string SortOrder{get;set;}
    }
    

    然后将其注入必须使用它的类:

    public class SearchController{
        private ISearchOptions _searchOptions;
        public SearchController(ISearchOptions searchOptions){
            _searchOptions=searchOptions;
        }
        ...
    }
    

    然后在我的web.config中配置castle,我希望有如下内容:

    <castle>
        <components>
            <component id="searchOptions" service="Web.Models.ISearchOptions, Web" type="Web.Models.SearchOptions, Web" lifestyle="PerSession" />
        </components>
    </castle>
    

    并让IoC容器处理会话对象,而不必自己显式访问它。

    谢谢

    我一直在做一些研究。基本上,我想要的是有一个会话范围的组件。我来自Java和Spring框架,在那里我有会话范围的bean,我认为它对于存储会话数据非常有用。

    4 回复  |  直到 15 年前
        1
  •  14
  •   Carl Bergquist    13 年前

    这可能就是你想要的。

    public class PerSessionLifestyleManager : AbstractLifestyleManager
        {
        private readonly string PerSessionObjectID = "PerSessionLifestyleManager_" + Guid.NewGuid().ToString();
    
        public override object Resolve(CreationContext context)
        {
            if (HttpContext.Current.Session[PerSessionObjectID] == null)
            {
                // Create the actual object
                HttpContext.Current.Session[PerSessionObjectID] = base.Resolve(context);
            }
    
            return HttpContext.Current.Session[PerSessionObjectID];
        }
    
        public override void Dispose()
        {
        }
    }
    

    然后加上

    <component
            id="billingManager"  
            lifestyle="custom"  
            customLifestyleType="Namespace.PerSessionLifestyleManager, Namespace"  
            service="IInterface, Namespace"
            type="Type, Namespace">
    </component>
    
        2
  •  4
  •   Andy McCluggage hunter    12 年前

    你需要两门课。。。

    实施 IHttpModule 处理会话管理。添加 ILifetimeScope 对象,并在会话过期时再次处理它。这对于确保正确发布组件至关重要。到目前为止,这里给出的其他解决方案没有考虑到这一点。

    public class PerWebSessionLifestyleModule : IHttpModule
    {
        private const string key = "castle.per-web-session-lifestyle-cache";
    
        public void Init(HttpApplication context)
        {
            var sessionState = ((SessionStateModule)context.Modules["Session"]);
            sessionState.End += SessionEnd;
        }
    
        private static void SessionEnd(object sender, EventArgs e)
        {
            var app = (HttpApplication)sender;
    
            var scope = GetScope(app.Context.Session, false);
    
            if (scope != null)
            {
                scope.Dispose();
            }
        }
    
        internal static ILifetimeScope GetScope()
        {
            var current = HttpContext.Current;
    
            if (current == null)
            {
                throw new InvalidOperationException("HttpContext.Current is null. PerWebSessionLifestyle can only be used in ASP.Net");
            }
    
            return GetScope(current.Session, true);
        }
    
        internal static ILifetimeScope YieldScope()
        {
            var context = HttpContext.Current;
    
            if (context == null)
            {
                return null;
            }
    
            var scope = GetScope(context.Session, true);
    
            if (scope != null)
            {
                context.Session.Remove(key);
            }
    
            return scope;
        }
    
        private static ILifetimeScope GetScope(HttpSessionState session, bool createIfNotPresent)
        {
            var lifetimeScope = (ILifetimeScope)session[key];
    
            if (lifetimeScope == null && createIfNotPresent)
            {
                lifetimeScope = new DefaultLifetimeScope(new ScopeCache(), null);
                session[key] = lifetimeScope;
                return lifetimeScope;
            }
    
            return lifetimeScope;
        }
    
        public void Dispose()
        {
        }
    }
    

    您需要的第二个类是 IScopeAccessor . 这用于弥合HttpModule与内置Windsor之间的差距 ScopedLifestyleManager

    public class WebSessionScopeAccessor : IScopeAccessor
    {
        public void Dispose()
        {
            var scope = PerWebSessionLifestyleModule.YieldScope();
            if (scope != null)
            {
                scope.Dispose();
            }
        }
    
        public ILifetimeScope GetScope(CreationContext context)
        {
            return PerWebSessionLifestyleModule.GetScope();
        }
    }
    

    internal static 方法添加到 PerWebSessionLifestyleModule 支持这一点。

    就这样,希望注册它。。。

    container.Register(Component
        .For<ISometing>()
        .ImplementedBy<Something>()
        .LifestyleScoped<WebSessionScopeAccessor>());
    

    或者,我将此注册打包到扩展方法中。。。

    public static class ComponentRegistrationExtensions
    {
        public static ComponentRegistration<TService> LifestylePerSession<TService>(this ComponentRegistration<TService> reg)
            where TService : class
        {
            return reg.LifestyleScoped<WebSessionScopeAccessor>();
        }
    }
    

    container.Register(Component
        .For<ISometing>()
        .ImplementedBy<Something>()
        .LifestylePerSession());
    
        3
  •  1
  •   Mark Seemann    15 年前

    听起来您的思路是对的,但是您的SearchOptions类需要实现ISearchOptions:

    public class SearchOptions : ISearchOptions { ... }
    

    您还需要告诉Windsor您的SearchController是一个组件,因此您可能也希望在web.config中注册它,尽管我更喜欢从代码中注册(见下文)。

    要使Windsor获取您的web.config,您应该如下实例化它:

    var container = new WindsorContainer(new XmlInterpreter());
    

    要创建SearchController的新实例,只需执行以下操作:

    var searchController = container.Resolve<SearchController>();
    

    要使用基于约定的技术注册给定程序集中的所有控制器,可以执行以下操作:

    container.Register(AllTypes
        .FromAssemblyContaining<MyController>()
        .BasedOn<IController>()
        .ConfigureFor<IController>(reg => reg.LifeStyle.Transient));
    
        4
  •  1
  •   Community paulsm4    7 年前

    我的经验是 Andy's answer 不起作用,因为 SessionStateModule.End never raised directly :

    尽管结束事件是公共的,但您只能通过在Global.asax文件中添加事件处理程序来处理它。之所以实现此限制,是因为HttpApplication实例被重用以提高性能。会话到期时, 仅执行Global.asax文件中指定的Session_oned事件 ,以防止代码调用与当前正在使用的HttpApplication实例关联的结束事件处理程序。

    SessionScopeAccessor 类别:

    public class SessionScopeAccessor : IScopeAccessor
    {
        private const string Key = "castle.per-web-session-lifestyle-cache";
    
        public void Dispose()
        {
            var context = HttpContext.Current;
    
            if (context == null || context.Session == null)
                return;
    
            SessionEnd(context.Session);
        }
    
        public ILifetimeScope GetScope(CreationContext context)
        {
            var current = HttpContext.Current;
    
            if (current == null)
            {
                throw new InvalidOperationException("HttpContext.Current is null. PerWebSessionLifestyle can only be used in ASP.Net");
            }
    
            var lifetimeScope = (ILifetimeScope)current.Session[Key];
    
            if (lifetimeScope == null)
            {
                lifetimeScope = new DefaultLifetimeScope(new ScopeCache());
                current.Session[Key] = lifetimeScope;
                return lifetimeScope;
            }
    
            return lifetimeScope;
        }
    
        // static helper - should be called by Global.asax.cs.Session_End
        public static void SessionEnd(HttpSessionState session)
        {
            var scope = (ILifetimeScope)session[Key];
    
            if (scope != null)
            {
                scope.Dispose();
                session.Remove(Key);
            }
        }
    }
    

    }

    给客户打电话是很重要的 SessionEnd global.asax.cs 文件:

    void Session_OnEnd(object sender, EventArgs e)
    {
        SessionScopeAccessor.SessionEnd(Session);
    }
    

    这是处理SessionEnd事件的唯一方法。