I have made a WCF publisher Subscriber service which handles 70 to 80 connections per day
If a connection breaks in between ( due to internet connection failure , System shutdown etc.)
the service itself will try to set up a connection itself when the connectivity resumes.
sometime back I am noticed my service is throwing exceptions (operation Timed out)
This request operation sent to net.tcp://localhost:2001/sub did not receive a reply within the configured timeout (00:00:59.9799877). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client
and then after some of these Exceptions RM destination time out exceptions are coming up which faults the line for further communication with the service
The request to create a reliable session has been refused by the RM Destination. Server
'net.tcp://localhost:2001/Sub' is too busy to process this request. Try again later. The channel could not be opened.
During this time the the clients already subscribed are responding successfully to the pub sub requests
No new Channels are subscribing to the service
I am using net Tcp binding in the Service
client code
public class Subscriber : IPublishing
{
static int Counter = 0;
ISubscription _proxy;
string _endpoint = string.Empty;
public Subscriber()
{
_endpoint = "net.tcp://localhost:2001/sub";
MakeProxy(_endpoint, this);
}
void MakeProxy(string EndpoindAddress, object callbackinstance)
{
try
{
NetTcpBinding netTcpbinding = new NetTcpBinding(SecurityMode.None);
EndpointAddress endpointAddress = new EndpointAddress(EndpoindAddress);
InstanceContext context = new InstanceContext(callbackinstance);
// Added for Reliable communication
netTcpbinding.ReliableSession.Enabled = true;
netTcpbinding.ReliableSession.Ordered = true;
netTcpbinding.MaxBufferSize = 2147483647;
netTcpbinding.MaxBufferPoolSize = 2147483647;
netTcpbinding.MaxReceivedMessageSize = 2147483647;
netTcpbinding.ReliableSession.InactivityTimeout = TimeSpan.MaxValue;
netTcpbinding.ReceiveTimeout = TimeSpan.MaxValue;
DuplexChannelFactory<ISubscription> channelFactory = new DuplexChannelFactory<ISubscription>(new InstanceContext(this), netTcpbinding, endpointAddress);
_proxy = channelFactory.CreateChannel();
Counter++;
}
catch (Exception ex)
{
//
}
}
public void Subscribe()
{
try
{
_proxy.Subscribe("500");
Counter = 0;
}
catch (Exception ex)
{
((IContextChannel)_proxy).Abort();
}
}
public void Publish(Message e, String ID)
{
}
public void _Subscribe()
{
try
{
//OnUnSubscribe();
Subscribe();
}
catch (Exception ex)
{
}
}
}
Server Code
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class Subscription : ISubscription
{
#region ISubscription Members
public void Subscribe(string ID)
{
String s = DateTime.Now.ToString(
IPublishing subscriber = OperationContext.Current.GetCallbackChannel<IPublishing>();
Filter.AddSubscriber(KioskID, subscriber);
}
public void UnSubscribe(string ID)
{
IPublishing subscriber = OperationContext.Current.GetCallbackChannel<IPublishing>();
Filter.RemoveSubscriber(KioskID, subscriber);
}
#endregion
}
class Filter
{
static Dictionary<string, List<IPublishing>> _subscribersList = new Dictionary<string, List<IPublishing>>();
static public Dictionary<string, List<IPublishing>> SubscribersList
{
get
{
lock (typeof(Filter))
{
return _subscribersList;
}
}
}
static public List<IPublishing> GetSubscribers(String topicName)
{
lock (typeof(Filter))
{
if (SubscribersList.ContainsKey(topicName))
{
return SubscribersList[topicName];
}
else
return null;
}
}
static public void AddSubscriber(String topicName, IPublishing subscriberCallbackReference)
{
DebugLog.WriteLog("C:\\DevPubSubServiceLogs", topicName + "came for Sub");
lock (typeof(Filter))
{
DebugLog.WriteLog("C:\\DevPubSubServiceLogs",topicName + "inside lock");
if (SubscribersList.ContainsKey(topicName))
{
// Removing any stray subscribers for same topic name
// because only 1 subscriber for 1 topic name should exist
SubscribersList.Remove(topicName);
}
List<IPublishing> newSubscribersList = new List<IPublishing>();
newSubscribersList.Add(subscriberCallbackReference);
SubscribersList.Add(topicName, newSubscribersList);
}
DebugLog.WriteLog("C:\\DevPubSubServiceLogs", topicName + "subscribed");
}
static public void RemoveSubscriber(String topicName, IPublishing subscriberCallbackReference)
{
lock (typeof(Filter))
{
if (SubscribersList.ContainsKey(topicName))
{
//if (SubscribersList[topicName].Contains(subscriberCallbackReference))
//{
// SubscribersList[topicName].Remove(subscriberCallbackReference);
//}
SubscribersList.Remove(topicName);
}
}
}
}
Contracts
[ServiceContract]
public interface IPublishing
{
[OperationContract(IsOneWay = true)]
void Publish(Message e, string ID);
}
[ServiceContract(CallbackContract = typeof(IPublishing))]
public interface ISubscription
{
[OperationContract]
void Subscribe(string topicName);
[OperationContract]
void UnSubscribe(string topicName);
}
Please assist..
Related
This topic resembles this thread and this thread. Also I have read this example
But, in any of this thread I can't find clear example how organize p2p connection across the internet.
All examples good working in local network.
I made a small program, you can see it below. It works good in local network.
Main question: After peer registration how to open Wcf host and wait calls from another peers? In this topic I have read that:
When a peer is resolved on the global cloud, it is typically resolved to an IPv6 address
So I need open my WCF on this IPv6 address? How I can receive it?
Please give me some coding tips.
My program:
namespace FileShare
{
internal class Program
{
private static void Main()
{
Console.WriteLine("Hello enter your username");
IPeerRegistrationRepository peerRegistrationRepository = new PeerRegistrationRepository();
IPeerNameResolverRepository peerNameResolverRepository = new PeerNameResolverRepository();
ServiceHost host = null;
try
{
string username = Console.ReadLine();
int port = PeerUtils.FindFreePort();
peerRegistrationRepository.StartRegistration("Max951Peer", port, PeerNameType.Secured);
string serviceUrl = $"net.tcp://{peerRegistrationRepository.PeerName.PeerHostName}:{port}/Max951P2PService";
IP2PService localService = new P2PService();
host = new ServiceHost(localService, new Uri(serviceUrl));
host.AddServiceEndpoint(typeof(IP2PService), new NetTcpBinding { Security = { Mode = SecurityMode.None } }, serviceUrl);
try
{
host.Open();
Console.WriteLine($"Host opened on { serviceUrl }");
}
catch (Exception e)
{
Console.WriteLine($"Error { e }");
return;
}
Console.WriteLine($"Registered peer. PeerHostName: { peerRegistrationRepository.PeerName.PeerHostName }, Classifier: { peerRegistrationRepository.PeerName.Classifier }");
PeerNameRecordCollection results = peerNameResolverRepository.ResolvePeerName(peerRegistrationRepository.PeerName, Cloud.Global);
Console.WriteLine("{0} Peers Found:", results.Count.ToString());
int i = 1;
foreach (PeerNameRecord peer in results)
{
Console.WriteLine("{0} Peer:{1}", i++, peer.PeerName);
foreach (IPEndPoint ip in peer.EndPointCollection)
{
Console.WriteLine("\t Endpoint: {0}", ip);
string endpointUrl = $"net.tcp://[{ip.Address}]:{ip.Port}/Max951P2PService";
NetTcpBinding binding = new NetTcpBinding { Security = { Mode = SecurityMode.None } };
try
{
IP2PService serviceProxy = ChannelFactory<IP2PService>.CreateChannel(binding, new EndpointAddress(endpointUrl));
serviceProxy.SendMessage("Hello!", username);
}
catch (Exception)
{
// ignored
}
}
}
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
peerRegistrationRepository.StopRegistration();
host?.Close();
}
//serviceModelClient.Stop();
}
}
[ServiceContract]
public interface IP2PService
{
[OperationContract(IsOneWay = true)]
void SendMessage(string message, string from);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class P2PService : IP2PService
{
/// <inheritdoc />
public void SendMessage(string message, string from)
{
Console.WriteLine($"{from} says: { message }");
}
}
}
namespace Utils.PeerToPeer
{
public class PeerNameResolverRepository : IPeerNameResolverRepository
{
private readonly PeerNameResolver peerNameResolver;
public PeerNameResolverRepository()
{
this.peerNameResolver = new PeerNameResolver();
}
/// <inheritdoc />
public PeerNameRecordCollection ResolvePeerName(string name, PeerNameType peerNameType, Cloud cloud)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException(nameof(name));
}
return this.peerNameResolver.Resolve(new PeerName(name, peerNameType), cloud);
}
/// <inheritdoc />
public PeerNameRecordCollection ResolvePeerName(PeerName peerName, Cloud cloud)
{
return this.peerNameResolver.Resolve(peerName, cloud);
}
}
}
namespace Utils.PeerToPeer
{
public class PeerRegistrationRepository : IPeerRegistrationRepository
{
private PeerNameRegistration peerNameRegistration;
/// <inheritdoc />
public bool IsRegistered => this.peerNameRegistration != null && this.peerNameRegistration.IsRegistered();
/// <inheritdoc />
public string PeerUri => GetPeerUri();
/// <inheritdoc />
public PeerName PeerName { get; set; }
/// <inheritdoc />
public void StartRegistration(string name, int port, PeerNameType peerNameType)
{
this.PeerName = new PeerName(name, peerNameType);
this.peerNameRegistration = new PeerNameRegistration(PeerName, port, Cloud.Global);
this.peerNameRegistration.Start();
}
/// <inheritdoc />
public void StopRegistration()
{
this.peerNameRegistration?.Stop();
this.peerNameRegistration = null;
}
private string GetPeerUri()
{
return this.peerNameRegistration?.PeerName.PeerHostName;
}
}
}
I am facing problem with client which cannot register callback channel on second start. I tried various things it seems I found a solution to remove Close() channel (ResetProxy() call in wrapper) call on shutdown of service. But this it turns out causing another problem: the server is crashing with error "The thread tried to read from or write to a virtual address for which it does not have the appropriate access".
Additionally, there are network related issues which also result in same behavior. The solution is always to restart the server which is not really right option to fix callback channel registration.
Could anyone suggest what could be the problem? I've done a lot of testing, and it seems this somehow relates to sudden stop of all clients (global stop of multiple services) and the number of services matters too. As result even new client cannot register with server until server is rebooted. This sounds like server is holding up closed channels.
Contract:
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IAgentCallback))]
public interface IAgentManager
{
[OperationContract]
string RegisterAgent(string hostName);
[OperationContract]
bool Ping();
}
Client create callback code (truncated):
public class AgentCallbackDaemon
{
private void CreateManagerProxy()
{
Reset();
var isUnbound = true;
while (isUnbound)
{
try
{
ManagerProxy = new ProxyWrapper<AgentManagerProxy, IAgentManager>(CreateProxy);
ManagerProxy.ChannelStateChanged += HandleProxyConnectionStateChanged;
isUnbound = false;
}
catch (AddressAlreadyInUseException)
{
sLog.ErrorFormat("Port is already reserved, binding failed.");
}
catch (Exception error)
{
sLog.ErrorFormat($"No proxy due to {error}");
throw;
}
}
}
private AgentManagerProxy CreateProxy()
=> mCallbackChannelPortRange.IsCallbackPortRageSet() ? GetProxyWithPortRange() : GetProxyDefault();
private AgentManagerProxy GetProxyDefault()
=> new AgentManagerProxy(mAgentCallback, mManagerUri, GetServiceName());
private AgentManagerProxy GetProxyWithPortRange()
{
var minPort = mCallbackChannelPortRange.MinimumCallbackPortNumber;
var maxPort = mCallbackChannelPortRange.MaximumCallbackPortNumber;
return new AgentManagerProxy(mAgentCallback, mManagerUri, GetServiceName(), minPort, maxPort);
}
}
Client callback code (truncated):
public class AgentManagerProxy : DuplexClientBase<IAgentManager>, IAgentManager
{
public const string SERVICE_NAME = "AgentManager";
public AgentManagerProxy(IAgentCallback callback, string serviceAddress, string connectionId,
ushort minPort, ushort maxPort)
: base(callback, BindingAbstractFactory.DuplexServiceClientBindingFactory(connectionId,
minPort, maxPort), BindingUtility.CreateEndpoint(serviceAddress, SERVICE_NAME))
{
}
public string RegisterAgent(string hostName)
=> Channel.RegisterAgent(hostName);
public bool Ping() => return Channel.Ping();
}
public static class BindingAbstractFactory
{
public static Binding DuplexServiceClientBindingFactory(string connectionId, ushort minPort, ushort maxPort)
=> CreateDuplexServiceClientBinding(connectionId, minPort, maxPort);
private static Binding CreateDuplexServiceClientBinding(string connectionId, ushort minPort, ushort maxPort)
{
var binding = CreateDuplexServiceHostBinding();
if (binding is WSDualHttpBinding)
{
lock (sLock)
{
try
{
((WSDualHttpBinding)binding).ClientBaseAddress =
CreateClientBaseAddress(connectionId, minPort, maxPort);
}
catch (Exception error)
{
sLog.ErrorFormat("Unexpected exception: {0}", error);
throw error;
}
finally
{
Monitor.PulseAll(sLock);
}
}
}
return binding;
}
private static Binding CreateDuplexServiceHostBinding()
{
var binding = new WSDualHttpBinding
{
ReceiveTimeout = TimeSpan.MaxValue,
MaxReceivedMessageSize = int.MaxValue,
ReaderQuotas =
{
// Set the maximum sizes to allow big message sizes.
MaxArrayLength = int.MaxValue,
MaxBytesPerRead = int.MaxValue,
MaxDepth = int.MaxValue,
MaxNameTableCharCount = int.MaxValue,
MaxStringContentLength = int.MaxValue
},
Security =
{
// Disable security on control services.
// TODO: Once security concept has been clarified, update this to reflect security rules.
Mode = WSDualHttpSecurityMode.None,
Message = { ClientCredentialType = MessageCredentialType.None }
//// binding.Security.Mode = SecurityMode.None;
//// binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
}
};
return binding;
}
private static Uri CreateClientBaseAddress(string connectionId, ushort minPort, ushort maxPort)
{
var fullDnsName = Dns.GetHostEntry(Dns.GetHostName()).HostName;
int portNumber;
if (maxPort == ushort.MaxValue)
portNumber = LocalPorts.GetRandomAvailablePort(connectionId, minPort, maxPort);
else
portNumber = LocalPorts.GetNextAvailablePort(connectionId, minPort, maxPort);
return new Uri($#"http://{fullDnsName}:{portNumber}");
}
}
All this wrapped into proxy wrapper with Reset function which is called on service stop or any error (including network):
public class ProxyWrapper<TPROXY, TSERVICE> where TPROXY : ClientBase<TSERVICE> where TSERVICE : class
{
public delegate TPROXY CreateProxy();
private readonly object mProxyLock = new object();</summary>
private readonly CreateProxy mCreateProxy;
private TPROXY mProxy;
public ProxyWrapper(CreateProxy createProxyCallback)
{
mLog.Info.Write($"Creating Proxy for '{typeof(TPROXY).FullName}'");
mCreateProxy = createProxyCallback;
BuildProxy();
}
public event EventHandler<CommunicationState> ChannelStateChanged;
public TPROXY Proxy
{
get
{
lock (mProxyLock)
{
if (mProxy == null)
BuildProxy();
return mProxy;
}
}
}
public void ResetProxy()
{
mLog.Info.Write("Call ResetProxy()");
lock (mProxyLock)
{
try
{
if (mProxy == null)
return;
UnSubscribeFromChannelEvents();
CloseProxy();
}
catch (Exception ex)
{
// Catch all exceptions, and ignore them.
}
finally
{
mProxy = null;
}
}
}
private void RaiseChannelStateChanged(object sender, EventArgs e)
=> ChannelStateChanged?.Invoke(this, mProxy.InnerChannel.State);
private void RaiseChannelEnteredFaultyState()
=> ChannelStateChanged?.Invoke(this, CommunicationState.Faulted);
private void BuildProxy()
{
lock (mProxyLock)
{
if (mProxy != null)
ResetProxy();
mProxy = mCreateProxy();
SubscribeToChannelEvents();
}
}
private void SubscribeToChannelEvents()
{
if (mProxy?.InnerChannel == null)
return;
mProxy.InnerChannel.Faulted += OnInnerChannelFaulted;
mProxy.InnerChannel.Closed += RaiseChannelStateChanged;
mProxy.InnerChannel.Closing += RaiseChannelStateChanged;
mProxy.InnerChannel.Opened += RaiseChannelStateChanged;
mProxy.InnerChannel.Opening += RaiseChannelStateChanged;
}
private void UnSubscribeFromChannelEvents()
{
if (mProxy?.InnerChannel == null)
return;
mProxy.InnerChannel.Faulted -= OnInnerChannelFaulted;
mProxy.InnerChannel.Closed -= RaiseChannelStateChanged;
mProxy.InnerChannel.Closing -= RaiseChannelStateChanged;
mProxy.InnerChannel.Opened -= RaiseChannelStateChanged;
mProxy.InnerChannel.Opening -= RaiseChannelStateChanged;
}
private void OnInnerChannelFaulted(object sender, EventArgs e)
{
ResetProxy();
RaiseChannelEnteredFaultyState();
}
private void CloseProxy()
{
try
{
mProxy.Close();
}
catch (Exception ex)
{
try
{
mProxy.Abort();
}
catch (Exception abortException) { // ignored }
}
try
{
mProxy.ChannelFactory.Close();
}
catch (Exception ex)
{
try
{
mProxy.ChannelFactory.Abort();
}
catch (Exception abortException) { // ignored }
}
}
}
The server only maintains the callback list.
Create Proxy for 'AgentManagerProxy'
Call BuildProxy()
mProxy.Open() [State=Created]
call SubscribeToChannelEvents()
...............
Service stopping...
call ResetProxy()
Call UnSubscribeFromChannelEvents()
call CloseProxy(): mProxy.Close()
call CloseProxy(): mProxy.ChannelFactory.Close()
Added logging for client shows this on first time start:
On second start service fails:
Create Proxy for 'AgentManagerProxy'
Call BuildProxy()
mProxy.Open() [State=Created]
Error:
No proxy due to System.TimeoutException: The request channel timed out attempting to send after 00:00:00. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. ---> System.TimeoutException: The HTTP request to 'http://xxxxx:9003/AgentManager' has exceeded the allotted timeout of 00:00:00. The time allotted to this operation may have been a portion of a longer timeout.
at System.ServiceModel.Channels.HttpChannelUtilities.SetRequestTimeout(HttpWebRequest request, TimeSpan timeout)
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
--- End of inner exception stack trace ---
The number of your clients may exceed the value of Maxconcurrentinstances.
Maxconcurrentinstances is a positive integer that limits the number of instancecontext objects that can be executed through ServiceHost at a time. When the number of slots is below the limit, requests to create other instances are queued and completed. The default value is the sum of maxconcurrentsessions and maxconcurrentcalls.You need to close the channel after each client-side completes.Otherwise, there will be an exception after the client has been queuing for a long time.
try
{
if (client.State != System.ServiceModel.CommunicationState.Faulted)
{
client.Close();
}
}
catch (Exception ex)
{
client.Abort();
}
The close method may fail. If the close method fails, the abort method is required.
I have a WCF service that creates a private message in MSMQ. The messages are being created fine, and I can see the messages that my client application is creating.
I also have a MSMQ listener service that seems to run fine. When I start the service up, it appears to successfully "process" the messages in the queue and they are removed. However, the implementation of my listener service doesn't seem to get executed.
I'm fairly new to MSMQ, and I have no idea why the messages are being removed from the queue, and why the code in my listener method is not getting executed.
Below are my service classes...
[ServiceContract(Namespace = ServiceConstants.NAMESPACE, Name = "IOrderService")]
public interface IOrderQueueProcessingService
{
[OperationContract(IsOneWay = true, Action = "*")]
void ProcessOrderQueue(MsmqMessage<string> message);
}
public abstract class OrderQueueProcessingServiceBase : ServiceBase, IOrderQueueProcessingService
{
#region CONSTRUCTORS
protected OrderQueueProcessingServiceBase() { }
protected OrderQueueProcessingServiceBase(List<EventRecord> existingList) : base(existingList) { }
#endregion //CONSTRUCTORS
#region IOrderQueueProcessingService Members
[OperationBehavior(TransactionScopeRequired = false, TransactionAutoComplete = true)]
public virtual void ProcessOrderQueue(MsmqMessage<string> message)
{
throw new NotImplementedException();
}
#endregion
}
public class OrderQueueProcessingService : OrderQueueProcessingServiceBase
{
#region Constructors
public OrderQueueProcessingService() {}
public OrderQueueProcessingService(List<EventRecord> existingList) : base(existingList) { }
#endregion
/// <summary>
/// Processes any Orders in the Orders Queue
/// </summary>
/// <param name="message"></param>
public override void ProcessOrderQueue(MsmqMessage<string> message)
{
var q = new MessageQueue(#".\private$\msmqdemo/submitorderservice.svc");
q.Send("hey");
/*
using (new Tracer("OrderQueueProcessingService"))
{
// add data context to work with.
using (var unitOfWork = new TLFDataContext())
{
var newOrderLines = new List<OrderLineDataContract>
{
new OrderLineDataContract
{
C = "test",
IC = "msw",
Qty = 1,
T = "volume" ,
ED = DateTime.UtcNow.AddDays(5)
}
};
var newOrder = new OrderDataContract
{
LIs = newOrderLines.AsEnumerable(),
PId = 9323,
POId = 8686,
S = "new"
};
var orderService = new OrderService();
var createdOrder = orderService.CreateOrder(null, null, newOrder);
//unitOfWork.SubmitUnitOfWork();
//return null;
}
}*/
}
}
I commented out the code that I am eventually trying to execute, and replaced it with a simple MSMQ message send, for testing. This seems like it should work fine. Any help would be greatly appreciated.
Config settings below...
<service name="ServiceImplementation.OrderQueueProcessingService">
<host>
<baseAddresses>
<add baseAddress="https://localhost:9000/OrderQueueProcessingService.svc" />
</baseAddresses>
</host>
<endpoint address="net.msmq://localhost/private/testingqueue/OrderQueueProcessingService.svc" binding="netMsmqBinding" bindingConfiguration="MsmqBindingNonTransactionalNoSecurity" contract="IOrderQueueProcessingService" />
</service>
I was able to figure out my issue. I didn't have any event handling for the QueueOnPeekComplted event. I added that to an Initialize method, and was able to successfully process my messages. I also added handling of messages that could not be processed. Below is my new implementation of my OrderQueueProcessingService.
public class OrderQueueProcessingService : OrderQueueProcessingServiceBase, IDisposable
{
#region Constructors
public OrderQueueProcessingService()
{
Initialize(ConfigurationManager.AppSettings["OrderQueueProcessingQueueName"]);
}
public OrderQueueProcessingService(List<EventRecord> existingList) : base(existingList) {}
#endregion
#region Properties
private MessageQueue Queue { get; set; }
#endregion
#region IDisposable Members
public new void Dispose()
{
if (Queue == null) return;
//unsubscribe and dispose
Queue.PeekCompleted -= QueueOnPeekCompleted;
Queue.Dispose();
}
#endregion
private void Initialize(string queueName)
{
Queue = new MessageQueue(queueName, false, true, QueueAccessMode.Receive)
{
Formatter = new XmlMessageFormatter(new[] {typeof (OrderQueueDataContract)})
};
//setup events and start.
Queue.PeekCompleted += QueueOnPeekCompleted;
Queue.BeginPeek();
}
private static void MoveMessageToDeadLetter(IDisposable message)
{
var q = new MessageQueue(ConfigurationManager.AppSettings["OrderProcessingDLQ"], QueueAccessMode.Send)
{
Formatter = new XmlMessageFormatter(new[] {typeof (OrderQueueDataContract)})
};
q.Send(message, MessageQueueTransactionType.Single);
q.Dispose();
}
/// <summary>
/// Processes the specified Order message
/// </summary>
/// <param name="orderMessage"></param>
public override void ProcessOrderQueue(OrderQueueDataContract orderMessage)
{
using (var unitOfWork = new MyDataContext())
{
switch (orderMessage.M.ToLower())
{
case "create":
DataAccessLayer.CreateOrder(unitOfWork, orderMessage.O.TranslateToBe());
break;
default:
break;
}
}
}
private void QueueOnPeekCompleted(object sender, PeekCompletedEventArgs peekCompletedEventArgs)
{
var asyncQueue = (MessageQueue) sender;
using (var transaction = new MessageQueueTransaction())
{
transaction.Begin();
try
{
using (var message = asyncQueue.ReceiveById(peekCompletedEventArgs.Message.Id, TimeSpan.FromSeconds(30), transaction))
{
if (message != null) ProcessOrderQueue((OrderQueueDataContract) message.Body);
}
}
catch (InvalidOperationException ex)
{
transaction.Abort();
}
catch (Exception ex)
{
transaction.Abort();
}
if (transaction.Status != MessageQueueTransactionStatus.Aborted) transaction.Commit();
else
{
using (var message = asyncQueue.ReceiveById(peekCompletedEventArgs.Message.Id, TimeSpan.FromSeconds(30), transaction))
{
if (message != null)
{
MoveMessageToDeadLetter(message);
message.Dispose();
}
}
EventLog.WriteEntry("OrderQueueProcessingService", "Could not process message: " + peekCompletedEventArgs.Message.Id);
}
transaction.Dispose();
}
asyncQueue.EndPeek(peekCompletedEventArgs.AsyncResult);
asyncQueue.BeginPeek();
}
}
Does anyone see any issues with this implementation? I had to fiddle with it quite a bit, but it has passed unit testing, and process testing.
One thing that I noticed is that when my service first starts up, it processes all the messages in the queue; which could potentially hold up the starting of my other services. This may only be an issue when I'm starting them in my console app while testing. To be safe I start this service last.
I have Winforms application that host WCF service.
this is my Button connect event:
private void btnConnect_Click(object sender, EventArgs e)
{
try
{
// Returns a list of ipaddress configuration
IPHostEntry ips = Dns.GetHostEntry(Dns.GetHostName());
// Get machine ipaddress
IPAddress _ipAddress = IPAddress.Parse(tbServerIp.Text);
// Create the url that is needed to specify where the service should be started
urlService = "net.tcp://" + _ipAddress.ToString() + ":8000/MyService";
// Instruct the ServiceHost that the type that is used is a ServiceLibrary.service1
host = new ServiceHost(typeof(ServiceLibrary.service1));
host.Opening += new EventHandler(host_Opening);
host.Opened += new EventHandler(host_Opened);
host.Closing += new EventHandler(host_Closing);
host.Closed += new EventHandler(host_Closed);
// The binding is where we can choose what transport layer we want to use. HTTP, TCP ect.
NetTcpBinding tcpBinding = new NetTcpBinding();
tcpBinding.TransactionFlow = false;
tcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
tcpBinding.Security.Mode = SecurityMode.None; // <- Very crucial
// Add a endpoint
host.AddServiceEndpoint(typeof(ServiceLibrary.IService1), tcpBinding, urlService);
// A channel to describe the service. Used with the proxy scvutil.exe tool
ServiceMetadataBehavior metadataBehavior;
metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
// This is how I create the proxy object that is generated via the svcutil.exe tool
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetUrl = new Uri("http://" + _ipAddress.ToString() + ":8001/MyService");
metadataBehavior.HttpGetEnabled = true;
metadataBehavior.ToString();
host.Description.Behaviors.Add(metadataBehavior);
urlMeta = metadataBehavior.HttpGetUrl.ToString();
//pbIndicator.Image = Resources.indicator_green;
btnConnect.BackColor = Color.Red;
btnConnect.Text = "Stop";
}
host.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace);
}
}
And this is my ServiceLibrary:
namespace ServiceLibrary
{
public delegate void StatusEventHandler(string srt);
[ServiceContract()]
public interface IService1
{
[OperationContract]
string MyOperation1(string myValue);
[OperationContract]
string MyOperation2(DataContract1 dataContractValue);
[OperationContract]
string HelloWorld(string str);
}
public class service1 : IService1
{
public event StatusEventHandler StartEvent;
public string MyOperation1(string myValue)
{
return "Hello: " + myValue;
}
public string MyOperation2(DataContract1 dataContractValue)
{
return "Hello: " + dataContractValue.FirstName;
}
public string HelloWorld(string str)
{
StartEvent(str);
//return "Helloworld from " + str;
}
}
[DataContract]
public class DataContract1
{
string firstName;
string lastName;
[DataMember]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
[DataMember]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
}
What i want to do is to pass the client message from HelloWorld function to my main form, so i try to create event inside class service1:
public delegate void StatusEventHandler(string srt);
public event StatusEventHandler StartEvent;
but it remained null even after registered from my main form (inside my button click event)
What is the simplest way to achieve that ?
When you created a service host, you gave a type, which means that with every call to your service, a new instance of that type will be created.
host = new ServiceHost(typeof(ServiceLibrary.service1));
You need to pass an instance of your service to the servicehost constructor, so every call will use that instance.
var yourServiceInstance = new ServiceLibrary.service1();
// attach event handlers here
host = new ServiceHost(yourServiceInstance);
When you do this, your service class needs to be configured for single instance mode:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class service1
{
// methods here
}
Update: with help from Henk, determined that public Dispose() is being called, which in turn calls private Dispose(true). This is my first implementation of IDisposable interface, so not sure if it is correct. I don't call Dispose explicitly anywhere. It seems that WCF architecture is calling it on exit from each OperationContract member.
Took unmanaged cleanup code out of Dispose for now, and multiple calls are able to access the static data. It seems that Dispose() is called on all locally allocated objects on return from calls, even if there is a reference to object in static storage. Not sure in .net world how to get around this so that IDisposable interface will get callled correctly. I'm guessing that these objects will get garbage collected at some point also.
Here is call stack on return from 1st call when Dispose is being called:
BossISeriesCwbxService.dll!BossISeriesCwbxServices.DataContracts.ISeriesSystem.Dispose(bool
bDisposing = true) Line 119 C#
BossISeriesCwbxService.dll!BossISeriesCwbxServices.DataContracts.ISeriesSystem.Dispose()
Line 107 + 0xd bytes C#
System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.DisposeParametersCore()
+ 0x56 bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.DisposeParameters()
+ 0xf bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessageCleanup(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc =
{System.ServiceModel.Dispatcher.MessageRpc})
+ 0x135 bytes System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(ref
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 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0x36 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0x43 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc) + 0xd7 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.MessageRpc.Process(bool
isOperationContextSet = false) + 0x9b
bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.Dispatch(ref
System.ServiceModel.Dispatcher.MessageRpc
rpc, bool isOperationContextSet) +
0x2d bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext
request =
{System.ServiceModel.Security.SecuritySessionServerSettings.SecuritySessionRequestContext},
bool cleanThread,
System.ServiceModel.OperationContext
currentOperationContext) + 0x20c
bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext
request,
System.ServiceModel.OperationContext
currentOperationContext) + 0xdf bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult
result) + 0x43 bytes
System.ServiceModel.dll!System.ServiceModel.Dispatcher.ChannelHandler.OnContinueAsyncReceive(object
state) + 0x45 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2()
+ 0x46 bytes System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.OnSecurityContextCallback(object
o) + 0x28 bytes
mscorlib.dll!System.Security.SecurityContext.Run(System.Security.SecurityContext
securityContext,
System.Threading.ContextCallback
callback, object state) + 0x55 bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke()
+ 0x4d bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks()
+ 0x180 bytes System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(object
state) + 0x7a bytes
System.ServiceModel.dll!System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(uint
errorCode, uint numBytes,
System.Threading.NativeOverlapped*
nativeOverlapped) + 0xf bytes
SMDiagnostics.dll!System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(uint
error, uint bytesRead,
System.Threading.NativeOverlapped*
nativeOverlapped) + 0x3d bytes
mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint
errorCode, uint numBytes,
System.Threading.NativeOverlapped*
pOVERLAP) + 0x54 bytes
I read some posts on caching static data in a WCF service implementation class, and was having a problem with the GC calling dispose on the objects in a static Dictionary. I am referencing some activex objects from IBM iSeries Access, so I implemented IDisposable interface to clean up connection to iSeries. My problem is the GC is Disposing of objects in the Static members of the Service class. Not sure all the code was required, but here it is anyway. The problem is that on return from each OperationContract method, the GC is calling Dispose on the ISeriesSystem or Queue object that was added to the associated Dictionary, but the ISeriesSystem Dictionary is static, so I thought that it held a reference to the object, so GC wouldn't be done until it is removed from Dictionary.
Service Interface:
[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);
}
Service Implementation:
[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;
}
}
}
Queue 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 "";
}
}
}
}
Client Code:
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 Hosted Service:
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();
Does it happen regularly or every now and then? Does it happen under development or only (after some time) on the production server?
When you host this under IIS, the server might decide to 'recycle' your app. Basic advice: don't use static in server apps. It's not reliable and not scalable.
Edit
OK, I've read a little more (but not all).
The problem is that on return from
each OperationContract method, the GC
is calling Dispose on the
ISeriesSystem or Queue object
You should verify that in great detail. Is it really the GC that calls your Finalizer(s) (aka destructors)? You should use logging or debugging to verify that the overload Dispose(false) is being called. If Dispose(true) is called (and I see a lot of code involved in doing that) you should stacktrace to the actual cause.
Can you put the following code :
[ServiceContract(SessionMode = SessionMode.Required)]
on your service contract just to make sure the transport session is being created? If it isn't this should throw an error.
WSHttpBinding does not create a transport session without security and reliable session.
EDIT :
Other than that, since you are creating a Single Instance service, why would you need static members? There is only going to be one instance alive, so, any instance members of that instance will live with it and will be kind of static. Have you thought about that or there is a specific reason for using static members?
I know it's a bit late but have you tried adding the [OperationBehavior(AutoDisposeParameters = false)] attribute?
Link to MSDN Forum