c# - 使用静态数据的WCF服务遇到问题

标签 c# .net wcf

更新:在Henk的帮助下,确定正在调用public Dispose(),而后者又调用了私有Dispose(true)。这是我第一次实现IDisposable接口,所以不确定它是否正确。我不会在任何地方明确呼叫Dispose。似乎WCF体系结构正在从每个OperationContract成员退出时调用它。

现在从Dispose中删除了非托管的清理代码,并且多个调用都可以访问静态数据。似乎在调用返回时,对所有本地分配的对象都调用了Dispose(),即使静态存储中有对对象的引用也是如此。在.net世界中不确定如何解决此问题,以便IDisposable接口将被正确调用。我猜这些对象在某些时候也会被垃圾收集。

这是调用Dispose时从第一次调用返回时的调用堆栈:


  
    BossISeriesCwbxService.dll!BossISeriesCwbxServices.DataContracts.ISeriesSystem.Dispose(bool
    bDisposed = true)第119行C#
    BossISeriesCwbxService.dll!BossISeriesCwbxServices.DataContracts.ISeriesSystem.Dispose()
    第107行+ 0xd字节C#
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.DisposeParametersCore()
    + 0x56字节System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.DisposeParameters()
    + 0xf字节System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessageCleanup(ref
    System.ServiceModel.Dispatcher.MessageRpc
    rpc =
    {System.ServiceModel.Dispatcher.MessageRpc})
    + 0x135字节System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(ref
    System.ServiceModel.Dispatcher.MessageRpc
    rpc =
    {System.ServiceModel.Dispatcher.MessageRpc})
    + 0x1bf字节System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(ref
    System.ServiceModel.Dispatcher.MessageRpc
    rpc)+ 0x80字节
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(ref
    System.ServiceModel.Dispatcher.MessageRpc
    rpc)+ 0x36字节
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(ref
    System.ServiceModel.Dispatcher.MessageRpc
    rpc)+ 0x43字节
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(ref
    System.ServiceModel.Dispatcher.MessageRpc
    rpc)+ 0xd7字节
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.Process(布尔
    isOperationContextSet = false)+ 0x9b
    个字节
    System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Dispatch(ref
    System.ServiceModel.Dispatcher.MessageRpc
    rpc,bool isOperationContextSet)+
    0x2d字节
  
  
  System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext
  请求=
  {System.ServiceModel.Security.SecuritySessionServerSettings.SecuritySessionRequestContext},
  布尔cleanThread,
  System.ServiceModel.OperationContext
  currentOperationContext)+ 0x20c
  个字节
  System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext
  请求,
  System.ServiceModel.OperationContext
  currentOperationContext)+ 0xdf字节
  
  System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult
  结果)+ 0x43字节
  System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.OnContinueAsyncReceive(对象
  状态)+ 0x45字节
  System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2()
  + 0x46字节System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.OnSecurityContextCallback(object
  o)+ 0x28字节
  mscorlib.dll!System.Security.SecurityContext.Run(System.Security.SecurityContext
  securityContext,
  System.Threading.ContextCallback
  回调,对象状态)+ 0x55字节
  
  System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke()
  + 0x4d字节
  
  System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks()
  + 0x180字节System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(对象
  状态)+ 0x7a字节
  System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(uint
  errorCode,uint numBytes,
  System.Threading.NativeOverlapped *
  nativeOverlapped)+ 0xf字节
  SMDiagnostics.dll!System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(uint
  错误,uint bytesRead,
  System.Threading.NativeOverlapped *
  nativeOverlapped)+ 0x3d字节
  mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint
  errorCode,uint numBytes,
  System.Threading.NativeOverlapped *
  pOVERLAP)+ 0x54字节


我读了一些有关在WCF服务实现类中缓存静态数据的文章,并且GC调用在静态Dictionary中的对象上处置时遇到问题。我从IBM iSeries Access引用了一些activex对象,所以我实现了IDisposable接口来清理与iSeries的连接。我的问题是GC是在Service类的Static成员中处理对象。不确定是否需要所有代码,但是无论如何都在这里。问题是,从每个OperationContract方法返回时,GC会在添加到关联的Dictionary的ISeriesSystem或Queue对象上调用Dispose,但是ISeriesSystem Dictionary是静态的,因此我认为它持有对该对象的引用,因此只有将其从Dictionary中删除,GC才会完成。

服务接口:

[ServiceContract(Namespace="BossISeriesCwbxServices")]
public interface IDataQueueService
{
     [OperationContract]
     ISeriesSystem SystemInitialize(string sISeriesName);

     [OperationContract(Name="FinalizeSystemByName")]
     void SystemFinalize(string sISeriesName);

     [OperationContract]
     void SystemFinalize(ISeriesSystem oISeriesSystem);

     [OperationContract]
     Queue QueueInitialize(string sQueueName, string sLibrary, string sISeriesName);

     [OperationContract(Name="FinalizeQueueByName")]
     void QueueFinalize(string sQueueName, string sLibrary, string sISeriesName);

     [OperationContract]
     void QueueFinalize(Queue oDataQueue);

     [OperationContract (Name="QueueWriteByName")]
     void QueueWrite(string sQueueName, string sLibrary, string sISeriesName, string sMessage);

     [OperationContract]
     void QueueWrite(Queue oDataQueue, string sMessage);

     [OperationContract (Name="QueueReadByName")]
     string QueueRead(string sQueueName, string sLibrary, string sISeriesName);

     [OperationContract]
     string QueueRead(Queue oDataQueue);    
}


服务实施:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)] 
public class DataQueueService : IDataQueueService
{
    private static Dictionary<string, ISeriesSystem> mdictISeriesSystems = new Dictionary<string, ISeriesSystem>();
    public static IDictionary<string, ISeriesSystem> ISeriesDict
    {
       get { return mdictISeriesSystems; }
    } 

  public ISeriesSystem SystemInitialize(string sISeriesName)
  {
     ISeriesSystem oISeriesSystem = AddSystem(sISeriesName);
     return oISeriesSystem;
  }

  public void SystemFinalize(string sISeriesName)
  {
  }

  public void SystemFinalize(ISeriesSystem oISeriesSystem)
  {
     SystemFinalize(oISeriesSystem.Name);
  }


  public Queue QueueInitialize(string sQueueName, string sLibrary, string sISeriesName)
  {
     ISeriesSystem oISeriesSystem = null;
     Queue oDataQueue = null;

     try
     {
        oISeriesSystem = AddSystem(sISeriesName);
        oDataQueue = oISeriesSystem.AddQueue(sQueueName, sLibrary);
     }
     catch (Exception ex)
     {
        // ToDo: Log ex to WCF service log and remove from Console.
        Console.WriteLine(ex.ToString());
        oDataQueue = null;
     }

     return oDataQueue;
  }

  public Queue QueueInitialize(string sQueueName, string sLibrary, ISeriesSystem oISeriesSystem)
  {
     return QueueInitialize(sQueueName, sLibrary, oISeriesSystem.Name);
  }

  public void QueueFinalize(string sQueueName, string sLibrary, string sISeriesName)
  {
     string sISeriesKey = sISeriesName.Trim();
     string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();

     ISeriesSystem oISeriesSystem = null;
     Queue oDataQueue = null;


     if (DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
     {
        if (oISeriesSystem.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
        {
           oDataQueue.Dispose();
           oDataQueue = null;
           oISeriesSystem.DataQueueDict.Remove(sDataQueueKey);
        }

        if (oISeriesSystem.DataQueueDict.Count == 0)
        {
           oISeriesSystem.Dispose();
           oISeriesSystem = null;
        }
     }
  }

  public void QueueFinalize(Queue oDataQueue)
  {
     QueueFinalize(oDataQueue.Name, oDataQueue.Library, oDataQueue.ISeriesName);
  }

  public void QueueWrite(string sQueueName, string sLibrary, string sISeriesName, string sMessage)
  {
     string sISeriesKey = sISeriesName.Trim();
     string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();

     ISeriesSystem oISeriesSystem = null;
     Queue oDataQueue = null;


     if (DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
     {
        if (oISeriesSystem.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
        {
           oDataQueue.Write(sMessage);
        }
     }
  }

  public void QueueWrite(Queue oDataQueue, string sMessage)
  {
     QueueWrite(oDataQueue.Name, oDataQueue.Library, oDataQueue.ISeriesName, sMessage);
  }

  public string QueueRead(string sQueueName, string sLibrary, string sISeriesName)
  {
     string sISeriesKey = sISeriesName.Trim();
     string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();

     ISeriesSystem oISeriesSystem = null;
     Queue oDataQueue = null;


     if (DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
     {
        if (oISeriesSystem.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
        {
           return oDataQueue.Read();
        }
     }

     return "";
  }

  public string QueueRead(Queue oDataQueue)
  {
     return QueueRead(oDataQueue.Name, oDataQueue.Library, oDataQueue.ISeriesName);

  }

  ISeriesSystem AddSystem(string sISeriesName)
  {
     ISeriesSystem oISeriesSystem = null;
     string sISeriesKey = sISeriesName.Trim();

     if (!DataQueueService.ISeriesDict.TryGetValue(sISeriesKey, out oISeriesSystem))
     {
        oISeriesSystem = new ISeriesSystem(sISeriesName);
        DataQueueService.ISeriesDict[sISeriesKey] = oISeriesSystem;
     }

     return oISeriesSystem;
  }


ISeriesSystem数据合同:

using System;
using System.Collections.Generic;
using System.Text;

using System.ServiceModel;
using System.Runtime.Serialization;

using cwbx;

namespace BossISeriesCwbxServices.DataContracts
{
   public class ISeriesSystem : IDisposable
   {

      private string msName;
      [DataMember]
      public string Name
      {
         get { return msName; }
         set { msName = value; }
      }

      private Dictionary<string, Queue> mdictDataQueues = new Dictionary<string, Queue>();
      public IDictionary<string, Queue> DataQueueDict
      {
         get { return mdictDataQueues; }
      }

      private cwbx.AS400System mcwbxISeriesSystem = new AS400System();
      private cwbx.AS400System CwbxISeriesSystem
      {
         get { return mcwbxISeriesSystem; }
         set { mcwbxISeriesSystem = value; }
      }


      private bool bDisposed = false;


      public ISeriesSystem()
      {

      }

      public ISeriesSystem(string sISeriesName)
      {
         try
         {
            // Set DataContract properties.
            this.Name = sISeriesName;

            // Connect to iSeries, Logon and connect to iSeries services that may be used.
            this.CwbxISeriesSystem.Define(sISeriesName);
            this.CwbxISeriesSystem.UserID = "APP1DAK";
            this.CwbxISeriesSystem.Password = "DONNA99";
            this.CwbxISeriesSystem.Signon();
            this.CwbxISeriesSystem.Connect(cwbcoServiceEnum.cwbcoServiceDataQueues);
            this.CwbxISeriesSystem.Connect(cwbcoServiceEnum.cwbcoServiceSecurity);
            this.CwbxISeriesSystem.Connect(cwbcoServiceEnum.cwbcoServiceRemoteCmd);
         }
         catch (Exception ex)
         {
            // ToDo: Log ex to WCF service log and remove from Console.
            Console.WriteLine(ex.ToString());
            foreach (cwbx.Error cwbxError in this.CwbxISeriesSystem.Errors)
            {
               Console.WriteLine(cwbxError.Text);
               Console.WriteLine(cwbxError.ToString());
            }
         }
      }

      ~ISeriesSystem()
      {
         Dispose(false);
      }

      public void Dispose()
      {
         Dispose(true);
         GC.SuppressFinalize(this);
      }

      private void Dispose(bool bDisposing)
      {
         // Only Dispose of the object 1 time.
         if (!this.bDisposed)
         {
            // If disposing equals true, Dispose() was called by GC, so dispose all managed resources.
            if (bDisposing)
            {
               // Dispose managed resources, calling object Dispose method for objects
               // that implement IDisposable interface.
            }

            try
            {
               if (this.CwbxISeriesSystem.IsConnected(cwbcoServiceEnum.cwbcoServiceAny) == 1)
               {
                  this.CwbxISeriesSystem.Disconnect(cwbcoServiceEnum.cwbcoServiceAll);
               }
            }
            catch (Exception ex)
            {
               // ToDo: Log ex to WCF service log and remove from Console.
               Console.WriteLine(ex.ToString());
               foreach (cwbx.Error cwbxError in this.CwbxISeriesSystem.Errors)
               {
                  Console.WriteLine(cwbxError.Text);
                  Console.WriteLine(cwbxError.ToString());
               }
            }

            // Mark disposing as being done.
            bDisposed = true;
         }
      }

      public Queue AddQueue(string sQueueName, string sLibrary)
      {
         Queue oDataQueue = null;
         string sDataQueueKey = sLibrary.Trim() + sQueueName.Trim();

         if (!this.DataQueueDict.TryGetValue(sDataQueueKey, out oDataQueue))
         {
            oDataQueue = new Queue(sQueueName, sLibrary, this.CwbxISeriesSystem);
            this.DataQueueDict[sDataQueueKey] = oDataQueue;
         }

         return oDataQueue;
      }
   }
}


队列数据合同:

using System;
using System.Collections.Generic;
using System.Text;

using System.ServiceModel;
using System.Runtime.Serialization;

using cwbx;

namespace BossISeriesCwbxServices.DataContracts
{
   [DataContract]
   public class Queue : IDisposable
   {
      private string msName;
      [DataMember]
      public string Name
      {
         get { return msName; }
         set { msName = value; }
      }

      private string msLibrary;
      [DataMember]
      public string Library
      {
         get { return msLibrary; }
         set { msLibrary = value; }
      }

      private string msISeriesName;
      [DataMember]
      public string ISeriesName
      {
         get { return msISeriesName; }
         set { msISeriesName = value; }
      }

      private short miWaitTime = 10;
      [DataMember]
      public short WaitTime
      {
         get { return miWaitTime; }
         set { miWaitTime = value; }
      }

      private short miNumberOfAttempts = 1;
      [DataMember]
      public short NumberOfAttempts
      {
         get { return miNumberOfAttempts; }
         set { miNumberOfAttempts = value; }
      }

      private short miMaxQueueIndex = 1;
      public short MaxQueueIndex
      {
         get { return miMaxQueueIndex; }
         set { miMaxQueueIndex = value; }
      }

      private short miCurrentQueueIndex = 1;
      public short CurrentQueueIndex
      {
         get { return miCurrentQueueIndex; }
         set { miCurrentQueueIndex = value; }
      }



      private cwbx.DataQueue mcwbxDataQueue = new cwbx.DataQueue();
      private cwbx.DataQueue CwbxDataQueue
      {
         get { return mcwbxDataQueue; }
         set { mcwbxDataQueue = value; }
      }

      private bool bDisposed = false;


      public Queue()
      {
      }

      public Queue(string sQueueName, string sLibrary, cwbx.AS400System cwbxISeriesSystem)
      {
         this.Name = sQueueName;
         this.Library = sLibrary;
         this.ISeriesName = cwbxISeriesSystem.SystemName;

         this.CwbxDataQueue.QueueName = sQueueName;
         this.CwbxDataQueue.LibraryName = sLibrary;
         this.CwbxDataQueue.system = cwbxISeriesSystem;
      }

      ~Queue()
      {
         Dispose(false);
      }

      public void Dispose()
      {
         Dispose(true);
         GC.SuppressFinalize(this);
      }

      private void Dispose(bool bDisposing)
      {
         // Only Dispose of the object 1 time.
         if (!this.bDisposed)
         {
            // If disposing equals true, Dispose() was called by GC, so dispose all managed resources.
            if (bDisposing)
            {
               // Dispose managed resources, calling object Dispose method for objects
               // that implement IDisposable interface.
            }

            // Call the appropriate methods to clean up unmanaged resources here.
            try
            {
               this.CwbxDataQueue = null;
            }
            catch (Exception ex)
            {
               // ToDo: Log ex to WCF service log and remove from Console.
               Console.WriteLine(ex.ToString());
               foreach (cwbx.Error cwbxError in this.CwbxDataQueue.Errors)
               {
                  Console.WriteLine(cwbxError.Text);
                  Console.WriteLine(cwbxError.ToString());
               }
            }
            // Mark disposing as being done.
            bDisposed = true;
         }
      }


      public void Write(string sMessage)
      {
         try
         {
            cwbx.StringConverter cwbxStringConverter = new cwbx.StringConverter();
            Object oBytes = cwbxStringConverter.ToBytes(sMessage);
            this.CwbxDataQueue.Write(oBytes, false);
         }
         catch (Exception ex)
         {
            // ToDo: Log ex to WCF service log and remove from Console.
            Console.WriteLine(ex.ToString());
            foreach (cwbx.Error cwbxError in this.CwbxDataQueue.Errors)
            {
               Console.WriteLine(cwbxError.Text);
               Console.WriteLine(cwbxError.ToString());
            }
         }
      }

      public string Read()
      {
         try
         {
            Object oObject = null;
            return (new cwbx.StringConverter()).FromBytes(this.CwbxDataQueue.Read(this.WaitTime * this.NumberOfAttempts, out oObject));
         }
         catch (Exception ex)
         {
            Console.WriteLine(ex.ToString());
            foreach (cwbx.Error cwbxError in this.CwbxDataQueue.Errors)
            {
               Console.WriteLine(cwbxError.Text);
               Console.WriteLine(cwbxError.ToString());
            }

            return "";
         }
      }
   }
}


客户代码:

ISeriesSystem oISeriesSystem = null;
Queue oDataQueue = null;

oISeriesSystem = DQService.SystemInitialize("A2029D2.AS400.US.UPS.COM");
oDataQueue = DQService.QueueInitialize("SMTLST020", "IB5EXE", oISeriesSystem.Name);
oISeriesSystem.DataQueueDict.Add(oDataQueue.Library + oDataQueue.Name, oDataQueue);
ISeriesSystemDict.Add(oISeriesSystem.Name, oISeriesSystem);

DQService.QueueWrite(oDataQueue, "Testing cwbx.DataQueue WCF service");
string sMessage = DQService.QueueRead(oDataQueue);


Exe托管服务:

Uri baseAddress = new Uri("http://localhost:8080/BossISeriesCwbxServices");

//Instantiate new ServiceHost 
moServiceHost = new ServiceHost(typeof(BossISeriesCwbxServices.DataQueueService), baseAddress);

// Add Endpoint
moServiceHost.AddServiceEndpoint(typeof(BossISeriesCwbxServices.IDataQueueService), new WSHttpBinding(), "IDataQueueService");
// Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
moServiceHost.Description.Behaviors.Add(smb);

//Open moServiceHost
moServiceHost.Open();
Console.WriteLine("The IDataQueueService is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();

最佳答案

它是定期发生还是时不时发生?它是在开发中还是仅在生产服务器上(经过一段时间)发生?

当您将其托管在IIS下时,服务器可能会决定“回收”您的应用程序。基本建议:请勿在服务器应用中使用static。它不可靠且不可扩展。



编辑
好的,我读了更多(但不是全部)。


  问题是,从
  每个OperationContract方法,GC
  正在调用“处置”
  ISeriesSystem或Queue对象


您应该详细验证。真的是GC调用了终结器(也称为析构函数)吗?您应该使用日志记录或调试来验证是否正在调用重载Dispose(false)。如果调用了Dispose(true)(并且我看到执行此操作涉及的许多代码),则应跟踪到实际原因。

关于c# - 使用静态数据的WCF服务遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3143609/

相关文章:

c# - Windows 窗体绑定(bind) : is there an event similar to DataBindingComplete, 但在所有绑定(bind)完成时触发?

c# - 在 MVC 中实现验证

c# - 解决类型是否支持接口(interface)的最佳方法? (鸭子类型(duck typing))

javascript - 使用 JavaScript 或 jQuery 手动更新 UpdatePanel

c# - 在 WPF 中是否有使用后台 worker 的替代方法?

c# - WCF 客户端 - 最佳实践

wcf - 为 Excel PowerPivot 编写安全的 WCF 数据服务

c# - 从使用 configSource 文件的 Web.Config 读取值

.net - 在 .Net 中编写 RESTful 服务 "client"的最佳方法?

c# - Linq 问题不返回学生