7
  • 我有一个 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:我已经阅读了类似的条目here和其他网页。但看不到“具体”的答案。

4

4 回答 4

9

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

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

您可以在此处找到有关会话上下文的更多信息。

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

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

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

至于最佳实践,您几乎掌握了它:使用 WcfOperationSessionContext 存储当前会话并使用 IDispatchMessageInspector 开始/完成您的工作单元。

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

这是示例项目

于 2011-03-22T15:07:08.230 回答
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 操作完成,它将被释放。如果需要,您可以按正常方式使用事务。

于 2011-03-23T05:35:03.307 回答
3

这是一篇文章,详细描述了注册和使用 WcfOperationSessionContext 的所有步骤。它还包括在 agatha-rrsl 项目中使用它的说明。

于 2011-03-30T16:42:26.967 回答
1

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

在某些情况下,对互联网方法不那么讨厌,这不能正常工作。所以这里是我们测试的错误示例以及原因:

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

解决办法是什么?

IOperationInvoker只有这个!

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

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

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

于 2013-03-16T15:31:06.000 回答