Wcf NHibernate会话管理

时间:2014-01-04 11:34:15

标签: wcf nhibernate fluent-nhibernate castle-windsor

我是Castle,NHibernate和WCF的新手。

我根据以下文章为我的MVC应用程序实现了会话管理,因为它似乎是我目前所阅读的所有帖子中最先进的实现: http://nhibernate.info/blog/2011/03/02/effective-nhibernate-session-management-for-web-apps.html

我遇到的唯一问题是,它使用了我的WCF服务中没有的一些Asp.net特定功能,如(HttpContext.Current.Items)。

我开始使用WcfFacility

 public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddFacility<WcfFacility>().Register
    (

         Component.For<IRepository>().ImplementedBy(typeof(RepositoryBase<,>)),
         Component.For<ITimeService>()
                  .ImplementedBy<myTimeMvc.Webservice.TimeService>()
                  .Named("myTimeMvc.Webservice.TimeService"));


        container.Register(
        Component.For<IServiceBehavior>()
            .ImplementedBy<WcfSessionPerRequestBehavior>()
        );
    }

我的持久性配置:

 public class PersistenceInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {

        container.Kernel.AddFacility<TypedFactoryFacility>();

        container.Register(Component.For<ISessionFactory>().UsingFactoryMethod(k => CreateNhSessionFactory()));

        container.Register(Component.For<ISessionFactoryProvider>().AsFactory());

        container.Register(Component.For<IEnumerable<ISessionFactory>>().UsingFactoryMethod(k => k.ResolveAll<ISessionFactory>()));

        container.Register(Classes.FromAssembly(Assembly.GetAssembly(typeof(HdtRepository))).InSameNamespaceAs<HdtRepository>().WithService.DefaultInterfaces().LifestyleTransient());
    }

    /// <summary>
    /// Creates NHibernate Session Factory.
    /// </summary>
    /// <returns>NHibernate Session Factory</returns>
    private static ISessionFactory CreateNhSessionFactory()
    {
        var connStr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
        return Fluently.Configure()


            .Database(
                        MsSqlConfiguration.MsSql2008
                        .UseOuterJoin()
                        .ConnectionString(x => x.FromConnectionStringWithKey("DefaultConnection"))
                        .ShowSql()
            )
            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<TimeRecord>())
           .ExposeConfiguration(cfg =>
               cfg.Properties[Environment.CurrentSessionContextClass] = typeof(LazySessionContext).AssemblyQualifiedName

               )


            .BuildSessionFactory();
    }

}

然后我尝试通过添加自定义扩展来解决“HttpContext.Current.Items”的问题:

namespace MyTimeService.WcfExtension
{
///<summary>
/// This class incapsulates context information for a service instance
///</summary>
public class WcfInstanceContext : IExtension<InstanceContext>
{
    private readonly IDictionary items;

    private WcfInstanceContext()
    {
        items = new Hashtable();
    }

    ///<summary>
    /// <see cref="IDictionary"/> stored in current instance context.
    ///</summary>
    public IDictionary Items
    {
        get { return items; }
    }

    ///<summary>
    /// Gets the current instance of <see cref="WcfInstanceContext"/>
    ///</summary>
    public static WcfInstanceContext Current
    {
        get
        {
            WcfInstanceContext context =      OperationContext.Current.InstanceContext.Extensions.Find<WcfInstanceContext>();
            if (context == null)
            {
                context = new WcfInstanceContext();
                OperationContext.Current.InstanceContext.Extensions.Add(context);
            }
            return context;
        }
    }

    /// <summary>
    /// <see cref="IExtension{T}"/> Attach() method
    /// </summary>
    public void Attach(InstanceContext owner) { }

    /// <summary>
    /// <see cref="IExtension{T}"/> Detach() method
    /// </summary>
    public void Detach(InstanceContext owner) { }
}
}

以下列方式注册:

<extensions>
  <behaviorExtensions>
    <add name="WcfInstanceContext" type="MyTimeService.WcfExtension, MyTimeService.WcfExtension.WcfInstanceContext" />
  </behaviorExtensions>
</extensions>

然后我创建了一个自定义的ServiceBehavior

 public class WcfSessionPerRequestBehavior : IServiceBehavior
{
    private ISessionFactoryProvider _sfp;


    public WcfSessionPerRequestBehavior(ISessionFactoryProvider sfp)
    {
        _sfp = sfp;
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (var cdb in serviceHostBase.ChannelDispatchers)
        {
            var channelDispatcher = cdb as ChannelDispatcher;
            if (null != channelDispatcher)
            {
                foreach (var endpointDispatcher in channelDispatcher.Endpoints)
                {
                    foreach (var dispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
                    {
                        dispatchOperation.CallContextInitializers.Add(new WcfSessionPerRequestCallContextInitializer(_sfp));
                    }
                }
            }
        }
    }

后跟自定义ICallContextInitializer:

  public class WcfSessionPerRequestCallContextInitializer : ICallContextInitializer
{
    private ILogger logger = NullLogger.Instance;

    public ILogger Logger
    {
        get { return logger; }
        set { logger = value; }
    }


    private ISessionFactoryProvider sfp;


    public WcfSessionPerRequestCallContextInitializer(ISessionFactoryProvider s)
    {
        this.sfp = s;

    }

    public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
    {
        foreach (var sf in sfp.GetSessionFactories())
        {
            var localFactory = sf;
            LazySessionContext.Bind(new Lazy<NHibernate.ISession>(() => BeginSession(localFactory)), sf);
        }
        return null;

    }

    public void AfterInvoke(object correlationState)
    {
        foreach (var sf in sfp.GetSessionFactories())
        {
            var session = LazySessionContext.UnBind(sf);
            if (session == null) continue;
            EndSession(session);
        }
    }

    private static NHibernate.ISession BeginSession(ISessionFactory sf)
    {
        var session = sf.OpenSession();
        session.BeginTransaction();
        return session;
    }

    private void ContextEndRequest(object sender, EventArgs e)
    {
        foreach (var sf in sfp.GetSessionFactories())
        {
            var session = LazySessionContext.UnBind(sf);
            if (session == null) continue;
            EndSession(session);
        }
    }

    private static void EndSession(NHibernate.ISession session)
    {
        if (session.Transaction != null && session.Transaction.IsActive)
        {
            session.Transaction.Commit();
        }
        session.Dispose();
    }
}

最后我调整了ICurrentSessionContext:

  public class LazySessionContext : ICurrentSessionContext
{
    private readonly ISessionFactoryImplementor factory;
    private const string CurrentSessionContextKey = "NHibernateCurrentSession";

    public LazySessionContext(ISessionFactoryImplementor factory)
    {
        this.factory = factory;
    }

    /// <summary>
    /// Retrieve the current session for the session factory.
    /// </summary>
    /// <returns></returns>
    public NHibernate.ISession CurrentSession()
    {
        Lazy<NHibernate.ISession> initializer;
        var currentSessionFactoryMap = GetCurrentFactoryMap();
        if (currentSessionFactoryMap == null || !currentSessionFactoryMap.TryGetValue(factory, out initializer))
        {
            return null;
        }
        return initializer.Value;
    }

    /// <summary>
    /// Bind a new sessionInitializer to the context of the sessionFactory.
    /// </summary>
    /// <param name="sessionInitializer"></param>
    /// <param name="sessionFactory"></param>
    public static void Bind(Lazy<NHibernate.ISession> sessionInitializer, ISessionFactory sessionFactory)
    {
        var map = GetCurrentFactoryMap();
        map[sessionFactory] = sessionInitializer;
    }

    /// <summary>
    /// Unbind the current session of the session factory.
    /// </summary>
    /// <param name="sessionFactory"></param>
    /// <returns></returns>
    public static NHibernate.ISession UnBind(ISessionFactory sessionFactory)
    {
        var map = GetCurrentFactoryMap();
        var sessionInitializer = map[sessionFactory];
        map[sessionFactory] = null;
        if (sessionInitializer == null || !sessionInitializer.IsValueCreated) return null;
        return sessionInitializer.Value;
    }

    /// <summary>
    /// Provides the CurrentMap of SessionFactories.
    /// If there is no map create/store and return a new one.
    /// </summary>
    /// <returns></returns>
    private static IDictionary<ISessionFactory, Lazy<NHibernate.ISession>> GetCurrentFactoryMap()
    {

        //var currentFactoryMap = (IDictionary<ISessionFactory, Lazy<NHibernate.ISession>>)HttpContext.Current.Items[CurrentSessionContextKey];


        var currentFactoryMap = (IDictionary<ISessionFactory, Lazy<NHibernate.ISession>>)WcfInstanceContext.Current.Items[CurrentSessionContextKey];

        if (currentFactoryMap == null)
        {
            currentFactoryMap = new Dictionary<ISessionFactory, Lazy<NHibernate.ISession>>();
            WcfInstanceContext.Current.Items[CurrentSessionContextKey] = currentFactoryMap;
        }
        return currentFactoryMap;
    }
}

这似乎有效,但由于我对所有这些东西都是新手,我不能说我是否正确地做了这件事。 任何人都可以查看它并给我反馈吗?

干杯, 斯蒂芬

2 个答案:

答案 0 :(得分:1)

你正在使用OperationContext.Current这是为WCF服务的每个请求上下文实现的正确方法,所以它对我来说很好......

问题是,为什么你不是简单地使用nhibernate开箱即用的默认实现?实施在NHibernate.Context.WcfOperationSessionContext,您只需在会话工厂设置中使用此

例如:

Fluently.Configure()
    ...
    .ExposeConfiguration(cfg => cfg.SetProperty(
                                    Environment.CurrentSessionContextClass,
                                    "wcf")

Fluently.Configure()...CurrentSessionContext<WcfOperationSessionContext>()

答案 1 :(得分:1)

您也可以简单地设置aspNetCompatibilityEnabled=true,您将获得MVC和WCF可用的HttpContext。