C# 使用ASP.NET MVC的Castle项目每会话生活方式
我对温莎城堡真的很陌生。我想知道是否有一种使用IoC容器存储会话变量的方法。我在想这样的事情: 我想要一个类来存储搜索选项:C# 使用ASP.NET MVC的Castle项目每会话生活方式,c#,asp.net-mvc,session,castle-windsor,ioc-container,C#,Asp.net Mvc,Session,Castle Windsor,Ioc Container,我对温莎城堡真的很陌生。我想知道是否有一种使用IoC容器存储会话变量的方法。我在想这样的事情: 我想要一个类来存储搜索选项: public interface ISearchOptions{ public string Filter{get;set;} public string SortOrder{get;set;} } public class SearchOptions{ public string Filter{get;set;} public stri
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 Framework,在那里我有会话范围的bean,我认为它们对于存储会话数据非常有用。听起来你的思路是对的,但是你的SearchOptions类需要实现ISearchOptions:
public class SearchOptions : ISearchOptions { ... }
您还需要告诉Windsor您的SearchController是一个组件,因此您可能也希望在web.config中注册它,尽管我更喜欢从代码中注册(见下文)
要使Windsor获取您的web.config,您应该如下实例化它:
var container = new WindsorContainer(new XmlInterpreter());
container.Register(AllTypes
.FromAssemblyContaining<MyController>()
.BasedOn<IController>()
.ConfigureFor<IController>(reg => reg.LifeStyle.Transient));
要创建SearchController的新实例,只需执行以下操作:
var searchController = container.Resolve<SearchController>();
var searchController=container.Resolve();
要使用基于约定的技术注册给定程序集中的所有控制器,可以执行以下操作:
var container = new WindsorContainer(new XmlInterpreter());
container.Register(AllTypes
.FromAssemblyContaining<MyController>()
.BasedOn<IController>()
.ConfigureFor<IController>(reg => reg.LifeStyle.Transient));
container.Register(所有类型
.FromAssemblyContaining()
.BasedOn()
.ConfigureFor(reg=>reg.lifety.Transient));
这可能就是你想要的
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>
此解决方案适用于Windsor 3.0及以上版本。信息技术s基于PerWebRequest生活方式的实现,并利用温莎3.0中引入的新范围生活方式 你需要两门课
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
的实现。这用于弥合HTTPM模块与内置WindsorScopedLifestyleManager
类之间的差距
public class WebSessionScopeAccessor : IScopeAccessor
{
public void Dispose()
{
var scope = PerWebSessionLifestyleModule.YieldScope();
if (scope != null)
{
scope.Dispose();
}
}
public ILifetimeScope GetScope(CreationContext context)
{
return PerWebSessionLifestyleModule.GetScope();
}
}
PerWebSessionLifestyleModule
中添加了两个internalstatic
方法来支持这一点
就这样,希望注册它
container.Register(Component
.For<ISometing>()
.ImplementedBy<Something>()
.LifestyleScoped<WebSessionScopeAccessor>());
container.Register(组件
.对于()
.由()实施
.lifestylescope());
或者,我将此注册打包到扩展方法中
public static class ComponentRegistrationExtensions
{
public static ComponentRegistration<TService> LifestylePerSession<TService>(this ComponentRegistration<TService> reg)
where TService : class
{
return reg.LifestyleScoped<WebSessionScopeAccessor>();
}
}
公共静态类ComponentRegistrationExtensions
{
公共静态组件注册生活方式会话(此组件注册注册表)
where-TService:class
{
return reg.LifestyleScoped();
}
}
所以可以这样称呼它
container.Register(Component
.For<ISometing>()
.ImplementedBy<Something>()
.LifestylePerSession());
container.Register(组件
.对于()
.由()实施
.生活方式();
我的经验是,这不起作用,因为会话状态模块.End
是:
尽管结束事件是公共的,但您只能通过在Global.asax文件中添加事件处理程序来处理它。之所以实现此限制,是因为HttpApplication实例被重用以提高性能。会话过期时,仅执行Global.asax文件中指定的会话ONED事件,以防止代码调用与当前正在使用的HttpApplication实例关联的结束事件处理程序
因此,添加一个什么都不做的HttpModule变得毫无意义。我已将Andy的答案改编为一个会话范围访问器类:
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);
}
}
}
}
从global.asax.cs
文件调用SessionEnd
方法非常重要:
void Session_OnEnd(object sender, EventArgs e)
{
SessionScopeAccessor.SessionEnd(Session);
}
这是处理SessionEnd事件的唯一方法。谢谢,这正是我要找的。我想您应该将字段名从PerRequestObjectID更改为PerSessionObjectID。事实上,我相信查看文档可以让您受益匪浅:)@TigerShark Correct:)我已经修复了它。这是否允许我们编写类似于cr=>cr.lifety.PerSession.Named(cr.Implementation.Name)的内容;这不包括释放组件。必须将某些内容连接到会话结束事件中才能发生这种情况。请看下面我的答案,IIRC会话只会在InProc会话中触发。看起来是这样的。我真的没有考虑过这件事。我认为在我的情况下这是好的。我肯定我只会使用InProc会话。如果会话过期,我管理的对象类型将永远不需要持久化。但这绝对是其他人应该意识到的。我不确定如果没有会话结束事件,您如何处理会话生活方式。事实上,这是不可能的。不久前我为温莎写了一个perwebsession生活方式,请看@Andy,我在app.config(XML)中找不到如何配置它,你知道吗?@John Landheer与PerWebRequest相同。向
和/或
元素添加元素。这将在web.config中