Обновление: при помощи Henk определено, что вызывается public Dispose(), который в свою очередь вызывает private Dispose (true). Это моя первая реализация интерфейса IDisposable, поэтому не уверен, что это правильно. Я не называю Dispose явно нигде. Кажется, что архитектура WCF вызывает ее при выходе из каждого члена OperationContract.Проблема с WCF Service со статическими данными
Взял неуправляемый код очистки из Dispose на данный момент, и несколько вызовов могут получить доступ к статическим данным. Кажется, что Dispose() вызывается для всех локально выделенных объектов при возврате из вызовов, даже если есть ссылка на объект в статическом хранилище. Не уверен в мире .net, как обойти это, чтобы правильно обработать IDisposable-интерфейс. Я предполагаю, что эти объекты также получат сбор мусора.
Вот стек вызовов по возвращении из 1-го вызова при Dispose вызывается:
BossISeriesCwbxService.dll BossISeriesCwbxServices.DataContracts.ISeriesSystem.Dispose (BOOL bDisposing = истина) Линия 119 C#
BossISeriesCwbxService.dll! BossISeriesCwbxServices.DataContracts.ISeriesSystem.Dispose() линия 107 + 0xD байт C#
System.ServiceModel.Dispatcher.MessageRpc.DisposeParametersCore() System.ServiceModel.dll! + 0x56 байт Syste m.ServiceModel.dll! System.ServiceModel.Dispatcher.MessageRpc.DisposeParameters() + 0xf байт System.ServiceModel.dll! System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessageCleanup (ссылка System.ServiceModel.Dispatcher.MessageRpc = RPC {} System.ServiceModel.Dispatcher.MessageRpc) + 0x135 байт System.ServiceModel.dll! System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5 (ссылка System.ServiceModel.Dispatcher.MessageRpc = RPC {System.ServiceModel.Dispatcher. MessageRpc}) + 0x1bf bytes System.ServiceModel.dll! System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4 (ref System.ServiceModel.Dispatcher.MessageRpc RPC) + 0x80 байт
System.ServiceModel.dll! System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3 (ссылка System.ServiceModel.Dispatcher.MessageRpc RPC) + 0x36 байт
System.ServiceModel.dll! System.ServiceModel .Dispatcher.ImmutableDispatchRuntime.ProcessMessage2 (ссылка System.ServiceModel.Dispatcher.MessageRpc RPC) + 0x43 байт
System.ServiceModel.dll! System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1 (ссылка System.ServiceModel.Dispatcher.MessageRpc rpc) + 0xd7 bytes
System.ServiceModel.dll! System.Serv iceModel.Dispatcher.MessageRpc.Process (BOOL isOperationContextSet = ложь) + 0x9b байт
System.ServiceModel.dll! System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Dispatch (ссылка 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}, BOOL cleanThread, System.ServiceModel.OperationContext currentOperationContext) + 0x20c байт
System.ServiceModel.dll! System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest (System.ServiceModel.Channels.RequestContext запрос, система .ServiceModel.OperationContext currentOperationContext) + 0xDF байтSystem.ServiceModel.dll! System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump (System.IAsyncResult результат) + 0x43 байт
System.ServiceModel.dll! System.Ser viceModel.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 (объект о) + 0x28 байт
mscorlib.dll! System.Security.SecurityContext.Run (System.Security.SecurityContext SecurityContext, System.Threading.ContextCallback обратного вызова, состояние объекта) + 0x55 байтSystem.ServiceModel.dll! System.ServiceModel.Cha nnels.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 bytes
SMDiagnostics.dll! System.Serv iceModel.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 вызывающему выбрасывайте на объекты в статический словарь. Я ссылаюсь на некоторые объекты activex из IBM iSeries Access, поэтому я реализовал интерфейс IDisposable для очистки соединения с iSeries. Моя проблема - это GC is Disposing объектов в элементах Static класса Service. Не уверен, что весь код нужен, но здесь все равно. Проблема в том, что при возврате из каждого метода OperationContract GC вызывает Dispose в объекте ISeriesSystem или Queue, который был добавлен в соответствующий словарь, но словарь ISeriesSystem является статическим, поэтому я думал, что он содержит ссылку на объект, поэтому 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 DataContract:
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;
}
}
}
Очередь DataContract:
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();
такое же поведение с SessionMode.Необходимые. Я не привязан к привязке Http. На самом деле будет использовать NetTcp, если это произойдет в производстве. –