11

我正在尝试在我的 WCF 应用程序中实现每请求会话模型,并且我已经阅读了无数关于此主题的文档,但似乎没有完整的演示。我实际上遇到了一些非常有用的文章,例如:

NHibernate 的 ISession,作用于单个 WCF 调用

但这些都是从过去 NHibernate 和 Ninject 没有 WCF 特定实现的时候开始的,因此他们通过实现他们的自定义服务提供程序等实现了我所需要的。由于 Ninject 和 NHibernate 现在都支持 WCF,我想保持一致通过使用他们的模块,但我最终来到了这里......

基本设置和流程应该是这样的:

  1. 在休眠配置中将 CurrentSessionContext 设置为 WcfOperationSessionContext
  2. 在服务启动、开始请求或初始化时间附近的任何地方,打开会话并将其绑定到当前上下文
  3. 存储库使用 SessionFactory.GetCurrentSession() 方法获取当前会话实例
  4. 在生命周期结束时解除绑定和关闭会话

我最初的问题是我无法访问 wcf 生命周期来处理我的绑定。在深入研究了ninject代码之后,我设法将我的方法与ServiceHost的Opening / Closing事件挂钩,而没有进行太多更改,但是由于它是线程静态的,所以我无法访问OperationContext。

后来我尝试启用 asp.net 兼容性并使用 Application_BeginRequest 和 Application_EndRequest,它看起来很有希望,但我认为这不是最好的解决方案,因为我应该将内容绑定到服务实例,而不是 http 请求。

有没有人使用 ninject 的内置 wcf 扩展库来实现这一点?或者关于我可能做错了什么的任何想法?

4

3 回答 3

2

我在IDispatchMessageInspector的帮助下实现了每个请求会话的生命周期。可能您可以为 Ninject 实现自定义生命周期管理器以实现每个 Web 请求。

于 2011-02-02T07:56:12.620 回答
1

您可以执行以下操作:

public class DomainModule : NinjectModule
{
    private const string RealSessionIndicator = "RealSession";

    private readonly ProxyGenerator proxyGenerator = new ProxyGenerator();

    public override void Load()
    {
        this.Bind<ISession>().ToMethod(ctx => ctx.Kernel.Get<ISessionFactory>().OpenSession())
            .When(r => r.Parameters.Any(p => p.Name == RealSessionIndicator))
            .InRequestScope();

        this.Bind<Func<ISession>>().ToMethod(ctx => () => ctx.Kernel.Get<ISession>(new Parameter(RealSessionIndicator, (object)null, true)));

        this.Bind<ISession>()
            .ToMethod(this.CreateSessionProxy)
            .InTransientScope();

        this.Bind<ISessionFactory>().ToMethod(ctx => ctx.Kernel.Get<Configuration>().BuildSessionFactory()).InSingletonScope();
    }

    private ISession CreateSessionProxy(IContext ctx)
    {
        var session = (ISession)this.proxyGenerator.CreateInterfaceProxyWithoutTarget(typeof(ISession), new[] { typeof(ISessionImplementor) }, ctx.Kernel.Get<SessionInterceptor>());
        return session;
    }
}

public class SessionInterceptor : IInterceptor
{
    private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

    private readonly Func<ISession> sessionProvider;

    public SessionInterceptor(Func<ISession> sessionProvider)
    {
        this.sessionProvider = sessionProvider;
    }

    public void Intercept(IInvocation invocation)
    {
        try
        {
            var session = this.sessionProvider();
            invocation.ReturnValue = invocation.Method.Invoke(session, invocation.Arguments);
        }
        catch (TargetInvocationException exception)
        {
            Log.Error(exception);
            throw;
        }
    }
}

有了它,您可以在任何地方使用 ISession 而无需关心细节。您可以使用 InScope(ctx => OperationContext.Current) 编辑 InRequestScope 以使用 WCF 范围

于 2011-06-02T14:21:22.883 回答
0

您可以使用IInstanceContextProvider接口中提供的扩展点来完成此操作。

于 2011-02-02T07:16:57.943 回答