NHibernate + WCF + Windows服务和WcfOperationSessionContext类

时间:2011-03-22 13:47:59

标签: wcf nhibernate nhibernate-3

  • 我有一个Windows服务应用程序 我在其中创建WCF服务。
  • 其中一项服务是数据 服务:添加,删除, 读取,更新数据通过 WCF。
  • WCF使用NHibernate进行数据操作

所以我的猜测是:

  • 使用WCF进行Hibernate会话管理的任何建议(最佳实践)?

  • 任何人都知道

  

WcfOperationSessionContext (hibernate 3.0)类?

how to use it with WCF?

好吧,让它具体化:

假设我有名为 DataServices

的WCF服务
class WCFDataService .....
{

   void SaveMyEntity(MyEntity entity)
    {



         .....................?? // How to do? Best Way

         // Should i take one session  and use it all times
         // Should i take session and dipsose when operation finished then get 
         //new session for new operations?
         // If many clients call my WCF service function at the same time?
         // what may go wrong?
         // etc....


     }


 }

我需要一个 NHibernateServiceProvider

class NHibernateServiceProvider ....
{

    // How to get Session ?? Best way

     ISession GetCurrentSession(){.... }
     DisposeSession(){ ....}
}

祝福

PS:我在这里和其他网页上都读过类似的条目。但看不出“具体”的答案。

4 个答案:

答案 0 :(得分:9)

WcfOperationSessionContext类似于ThreadStaticSessionContext和WebRequestSessionContext是会话上下文的实现。会话上下文用于将ISession实例绑定(关联)到特定上下文。

可以通过调用ISessionFactory.GetCurrentSession()来检索当前上下文中的会话。

您可以找到有关session context here

的更多信息

WcfOperationSessionContext表示跨越WCF操作的整个持续时间的上下文。您仍然需要在操作开始时处理会话的绑定,并在操作结束时处理会话的解除绑定/提交/处理。

要访问wcf管道中的开始/结束操作,您需要实现IDispatchMessageInspector。您可以看到示例here

关于WCF集成:如果你使用ThreadStatic会话上下文,它似乎可以在开发上工作,但是当wcf管道中的各种组件(例如:授权,身份验证)在不同的线程上执行时,你将在生产中遇到问题。

至于你几乎把它钉在最佳实践上:使用WcfOperationSessionContext存储当前会话和IDispatchMessageInspector来开始/完成你的工作单元。

编辑 - 解决您添加的详细信息: 如果您配置了WcfOperationSessionContext并按照上面的说明进行绑定/解除绑定,那么您所要做的就是将ISessionFactory注入您的服务并使用factory.GetCurrentSession()。如果时间允许,我会发布一个样本prj。

以下是sample project

答案 1 :(得分:5)

我们用于管理与WCF的NHibernate会话的模型如下:

1)我们有自己的ServiceHost类,它继承自System.ServiceModel.ServiceHost,它也实现了ICallContextInitializer。我们将服务主机实例添加到我们服务中的每个操作中,如下所示:

protected override void InitializeRuntime()
{
    base.InitializeRuntime();
    foreach (ChannelDispatcher cd in this.ChannelDispatchers)
    {
        foreach (EndpointDispatcher ed in cd.Endpoints)
        {
            foreach (DispatchOperation op in ed.DispatchRuntime.Operations)
            {
                op.CallContextInitializers.Add(this);
             }
        }
    }
}

public void AfterInvoke(object correlationState)
{
    // We don't do anything after the invoke
}

public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
{
    OperationContext.Current.Extensions.Add(new SessionOperationContext());
    return null;
}

BeforeInvoke只是确保每个WCF调用的OperationContext都有自己的会话。我们发现IDispatchMessageInspector存在问题,其中会话在响应序列化期间不可用 - 如果使用延迟加载则会出现问题。

2)然后我们的SessionOperationContext将被调用以附加自身,我们使用OperationCompleted事件来移除自己。这样我们就可以确保会话可用于响应序列化。

public class SessionOperationContext : IExtension<OperationContext>
{

    public ISession Session { get; private set; }

    public static SessionOperationContext Current
    {
        get
        {
            OperationContext oc = OperationContext.Current;
            if (oc == null) throw new InvalidOperationException("Must be in an operation context.");
            return oc.Extensions.Find<SessionOperationContext>();
        }
    }

    public void Attach(OperationContext owner)
    {
        // Create the session and do anything else you required
        this.Session = ... // Whatever instantiation method you use

        // Hook into the OperationCompleted event which will be raised
        // after the operation has completed and the response serialised.
        owner.OperationCompleted += new EventHandler(OperationCompleted);
    }

    void OperationCompleted(object sender, EventArgs e)
    {
        // Tell WCF this extension is done
        ((OperationContext)sender).Extensions.Remove(this);
    }

    public void Detach(OperationContext owner)
    {
        // Close our session, do any cleanup, even auto commit 
        // transactions if required.
        this.Session.Dispose();
        this.Session = null;
    }
}

我们在高负载应用程序中成功使用了上述模式,它似乎运行良好。

总之,这类似于新的WcfOperationSessionContext所做的(当我们弄清楚上面的模式时,它不在我们身边;-))但也克服了延迟加载的问题。

关于提出的其他问题:如果您使用上述模型,则只需执行以下操作:

void SaveMyEntity(MyEntity entity)
{
    SessionOperationContext.Current.Session.Save(entity);
}

您可以保证会话始终存在,并且一旦WCF操作完成,它将被丢弃。如果需要,您可以以正常方式使用交易。

答案 2 :(得分:3)

这是一个post,详细描述了注册和使用WcfOperationSessionContext的所有步骤。它还包括将其与agatha-rrsl项目一起使用的说明。

答案 3 :(得分:1)

好的,在阅读互联网帖子等几天后,互联网中显示的所有方法似乎都是错误的。当我们使用带有nhibernate事务的NH 3 ^的UnitOfWork模式时,所有的aprochaes都会产生异常。为了测试它并证明我们需要使用MSMQ事务队列创建测试环境,需要在其上设置具有事务所需的OneWay操作契约的特殊接口。这种方法应该是这样的: 我们将事务性消息放入队列中。 2.服务从队列中获取事务消息。 所有工作队列都是空的。

在某些情况下,对互联网方法不太满意,这种方式无法正常运行。所以这里是我们测试的示例,这是错误的,为什么:

  1. Fabio Maulo方法:使用ICallContextInitializer - 在BeforeCall上打开NH会话/事务,在WCF执行服务方法之后,在上下文初始化器中的AfterCall上调用session.Flush + transaction.commit。当事务范围提交操作时,将自动保存会话。在调用transaction时的情况。将抛出完成异常WCF服务将关闭!问题可以,所以在try / catch clausule中执行transaction.Complete - 太棒了! - 没有错!然后事务范围将提交事务,消息将从队列中获取,但数据将不会保存!
  2. 另一种方法是使用IDispatchMessageInspector - 昨天我认为这是最好的方法。这里我们需要在调用消息调度程序检查器BeforeSendReply的WCF调用服务操作之后,在方法AfterReceiveRequest中打开会话/事务。在这个方法中,我们有关于[回复]的信息,它在OneWay操作中为空,但如果在调用服务方法时发生,则填充故障信息。太棒了,我想 - 就是这个!但不是!问题是,在WCF处理管道的这一点,我们没有交易!因此,如果transaction.Complete抛出错误或session.Flush将抛出它,我们将没有数据保存在数据库中,并且消息将不会返回队列出错的地方。
  3. 解决方案是什么?

    IOperationInvoker ,只有这个!

    您需要在默认调用者上将此接口实现为装饰器模式。在调用之前的方法调用中,我们打开会话/事务打开然后我们调用invoke默认调用者,然后在最后的clausule中调用transaction.complete,我们调用session.flush。这解决了什么类型的问题: 1.我们在此级别上有事务范围,因此当完成抛出异常消息将返回队列并且WCF不会关闭。 2.当调用时会抛出异常transaction.complete将不会被调用什么不会改变数据库状态

    我希望这能清除每个人的错误信息。

    在一些空闲时间里,我会尝试写一些例子。