NHCF会话管理 [英] Wcf NHibernate Session management

查看:243
本文介绍了NHCF会话管理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我基于下面的文章实现了MVC应用程序的会话管理,因为它似乎是最到目前为止我读过的所有帖子的高级实现:
http://nhibernate.info/blog/2011/03/02/effective-nhibernate-session-management-for-web-apps.html



我得到的唯一问题是,它使用了一些Asp.net特定的功能,这些功能在我的WCF服务(HttpContext.Current.Items)中不可用。



我开始使用WcfFacility

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


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


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

$ / code>

我的持久性配置:

I

{
public void Install(IWindsorContainer container,IConfigurationStore store)
{

.Kernel.AddFacility< TypedFactoryFacility>();

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

container.Register(Component.For< ISessionFactoryProvider>()。AsFactory()); ());使用工厂方法(k => k.ResolveAll< ISessionFactory>()));

container.Register(Component.For< IEnumerable< ISessionFactory>>

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

///< summary>
///创建NHibernate会话工厂。
///< / summary>
///<返回> NHibernate会话工厂< / returns>
private static ISessionFactory CreateNhSessionFactory()
{
var connStr = ConfigurationManager.ConnectionStrings [DefaultConnection]。ConnectionString;
返回流利.Configure()

$ b。数据库(
MsSqlConfiguration.MsSql2008 $ b $ .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>
///这个类包含服务实例的上下文信息
///< / summary>
public class WcfInstanceContext:IExtension< InstanceContext>
{
private只读IDictionary项目;

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

///< summary>
///< see cref =IDictionary/>存储在当前实例上下文中。
///< / summary>
public IDictionary Items
{
get {return items; }
}

///< summary>
///获取< 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);
}
返回上下文;
}
}

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

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



<注册以下方式:

 < extensions> 
< behaviorExtensions>
< add name =WcfInstanceContexttype =MyTimeService.WcfExtension,MyTimeService.WcfExtension.WcfInstanceContext/>
< /行为扩展>
< / extensions>

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

  public class WcfSessionPerRequestBehavior:IServiceBehavior 
{
private ISessionFactoryProvider _sfp;


public WcfSessionPerRequestBehavior(ISessionFactoryProvider sfp)
{
_sfp = sfp; (ServiceDescription serviceDescription,ServiceHostBase serviceHostBase)
{
}

public void AddBindingParameters(ServiceDescription serviceDescription,ServiceHostBase serviceHostBase)


public void Validate ,Collection< ServiceEndpoint>端点,BindingParameterCollection bindingParameters)
{
}

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




$ b code $


后跟一个自定义ICallContextInitializer:

pre code $ 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;


$ b public Object BeforeInvoke(InstanceContext instanceContext,IClientChannel channel,Message message)
{
foreach(sfp.GetSessionFactories()中的var sf)
{
var localFactory = sf;
LazySessionContext.Bind(new Lazy< NHibernate.ISession>(()=> BeginSession(localFactory)),sf);
}
返回null;


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



private static NHibernate.ISession BeginSession(ISessionFactory sf)
{
var session = sf.OpenSession();
session.BeginTransaction();
返回会话;


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



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




$ p $最后我调整了ICurrentSessionContext:

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

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

///< summary>
///检索会话工厂的当前会话。
///< / summary>
///<返回>< /返回>
public NHibernate.ISession CurrentSession()
{
懒惰< NHibernate.ISession>初始化;
var currentSessionFactoryMap = GetCurrentFactoryMap();
if(currentSessionFactoryMap == null ||!currentSessionFactoryMap.TryGetValue(factory,out initializer))
{
return null;
}
return initializer.Value;
}

///< summary>
///将一个新的sessionInitializer绑定到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>
///取消绑定会话工厂的当前会话。
///< / summary>
///< param name =sessionFactory>< / param>
///<返回>< /返回>
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>
///提供SessionFactory的CurrentMap。
///如果没有地图创建/存储并返回一个新的。
///< / summary>
///<返回>< /返回>
private static IDictionary< ISessionFactory,Lazy< NHibernate.ISession>> GetCurrentFactoryMap()
{

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

$ b 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;




$ b $ p
$ b这似乎有效,但由于我是新来的所有的东西,我不能说如果我正确地做到了这一点。
任何人都可以看看它,并给我反馈?
$ b $干杯,
斯蒂芬

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

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



例如:

 流利的.Configure()
...
.ExposeConfiguration(cfg => ; cfg.SetProperty(
Environment.CurrentSessionContextClass,
wcf)

流利.Configure()... CurrentSessionContext< WcfOperationSessionContext>()


I'm new to Castle, NHibernate and WCF.

I implemented the session management for my MVC application based on the following article because it seemd to be the most advanced implementation of all posts I've read so far : http://nhibernate.info/blog/2011/03/02/effective-nhibernate-session-management-for-web-apps.html

The only problem I got was that this uses some Asp.net specific functionality that isn't available in my WCF service like (HttpContext.Current.Items).

I started to use 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>()
        );
    }

My Persistance configuration:

 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();
    }

}

Then i tried to solve the problem with "HttpContext.Current.Items" by adding a custom extension:

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) { }
}
}

registered the following way:

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

Then I created a custom 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));
                    }
                }
            }
        }
    }

followed by a custom 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();
    }
}

and at last I adjusted the 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;
    }
}

This seems to work but since I'm new to all that stuff I can't say if I did this corretly. Can anyone have a look at it and give me feedback?

Cheers, Stefan

解决方案

Your are using OperationContext.Current which is the correct way to do per request context implementations for WCF services, so it looks good to me...

Question is, why are you not simply using the default implementation which comes out of the box with nhibernate? The implementation is in NHibernate.Context.WcfOperationSessionContext and you would just have to use this one within your session factory setup

For example:

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

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

这篇关于NHCF会话管理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆