WCF net.tcp only service giving TargetInvocationException/ServiceModel.CommunicationException - wcf

I'm writing a WCF (net.tcp) file transfer service which will eventually split files into several pieces and transfer said pieces to the client from the server/service. Currently the client and server are console apps.
While writing this service, I have at various times gotten the following exception;
System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '01:59:59.4220000'.
Actually, it starts as a Unhandled Exception: System.Reflection.TargetInvocationException: An exception occurred during the operation, making the result invalid. Check InnerException for exception details. ---> then the CommunicationException text above is right here.
In typical Microsoft fashion, that exception message is beyond unhelpful, so I'm finally calling out to the community to see if I can tackle this issue.
Also, in case this is important, the client is calling two of the service's methods Asynchronously ( InitGetFilePart() and GetFilePart() ). According to my logs, the first call to InitGetFilePartAsync(1, 1) gets handled all the way to the end; meaning that it's 'Completed' handler is called, which in turn calls vcClient.GetFilePartAsync(FileXferCargo, 1), and it's handler then spawns a BackgroundWorker thread (workers[chunkNum].
RunWorkerAsync(cargoHolder[chunkNum] where chunkNum=1) which itself completes. This is right about the time I get the TargetInvocationException mentioned above.
In the past I have made various tweaks (sadly, I can't remember what exactly) to the App.config to make this exception go away, but now nothing I do seems to make any difference and I just do NOT understand why this keeps happening.
I have read other advice on this matter, including "You have to catch the exception on the client, Abort current proxy and create and open new one." Well, I tried that but it doesn't appear I am able to catch this exception.
I also read that it could be due to sending too much data over the wire, but when I try to send my little 4k test file, I get the same exception. During my investigations, I also read that I can call 1 WCF service function/method, many times using the *Async() calls with the UserState parameter, which I am doing.
I will admit to being a relitive n00b when it comes to WCF, but I'm pretty sure I have my config files set correctly for what I'm trying to do.
Here are the client and server app.config files, the service interface definition and the top of the service's implementation class.
Client's App.config:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="MyTcpBinding_IFileXferService"
receiveTimeout="02:00:00"
sendTimeout="02:00:00"
transferMode="Streamed"
maxBufferSize="65536"
maxReceivedMessageSize="2147483647">
<readerQuotas maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="65536" />
<security mode="Transport">
<transport clientCredentialType="None">
<extendedProtectionPolicy policyEnforcement="Never" />
</transport>
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="ClientConfigBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="None" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint name="ClientConfig"
behaviorConfiguration="ClientConfigBehavior"
binding="netTcpBinding"
bindingConfiguration="MyTcpBinding_IFileXferService"
contract="ServiceRefs.IFileXferService" />
</client>
</system.serviceModel>
Server's App.config:
<bindings>
<netTcpBinding>
<!-- Under <netTcpBinding> setting the listenBacklog,
maxConnections, and maxBuffer* values high -->
<binding name="MyTcpBinding_IFileXferService"
receiveTimeout="02:00:00"
sendTimeout="02:00:00"
openTimeout="00:01:00"
transferMode="Streamed"
portSharingEnabled="true"
listenBacklog="32"
maxConnections="64"
maxBufferSize="65536"
maxReceivedMessageSize="2147483646">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="MediaServer.LNMediaServerSvc"
behaviorConfiguration="ServerConfigBehavior">
<host>
<baseAddresses>
<add baseAddress="net.tcp://lngsead148191a:9000/fileXferSvc"/>
</baseAddresses>
</host>
<endpoint name="mainEndPoint"
binding="netTcpBinding"
bindingConfiguration="MyTcpBinding_IFileXferService"
contract="ServiceInterfaces.IFileXferService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServerConfigBehavior">
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceThrottling maxConcurrentCalls="64" />
<dataContractSerializer maxItemsInObjectGraph="2147483646" />
<serviceCredentials>
<serviceCertificate findValue="tp_value"
x509FindType="FindByThumbprint" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
The Service interface is defined as follows;
[DataContract(IsReference = true)]
public class FileData
{
private long m_startPos;
[DataMember]
public long StartPosition
{
get { return m_startPos; }
set { m_startPos = value; }
}
private long m_endPos;
[DataMember]
public long EndPosition
{
get { return m_endPos; }
set { m_endPos = value; }
}
private byte m_chunkNumber;
[DataMember]
public byte ChunkNumber
{
get { return m_chunkNumber; }
set { m_chunkNumber = value; }
}
private long m_chunkSize;
[DataMember]
public long ChunkSize
{
get { return m_chunkSize; }
set { m_chunkSize = value; }
}
private string md5Hash;
[DataMember]
public string MD5Hash
{
get { return md5Hash; }
set { md5Hash = value; }
}
private string m_destFileSpec;
[DataMember]
public string DestinationFileSpec
{
get { return m_destFileSpec; }
set { m_destFileSpec = value; }
}
private string m_srcFileSpec;
[DataMember]
public string SourceFileSpec
{
get { return m_srcFileSpec; }
set { m_srcFileSpec = value; }
}
private Stream m_sourceStream;
[DataMember]
public Stream SourceStream
{
get { return m_sourceStream; }
set { m_sourceStream = value; }
}
private UInt32 m_JobNo;
[DataMember]
public UInt32 JobNumber
{
get { return m_JobNo; }
set { m_JobNo = value; }
}
private UInt32 m_fileNumber;
[DataMember]
public UInt32 FileNumber
{
get { return m_fileNumber; }
set { m_fileNumber = value; }
}
private long m_fileSize;
[DataMember]
public long FileSize
{
get { return m_fileSize; }
set { m_fileSize = value; }
}
}
[DataContract]
public partial class FileXferCargo
{
private FileData m_fileData;
[DataMember]
public FileData FileData
{
get { return m_fileData; }
set { m_fileData = value; }
}
private bool m_cancelled;
[DataMember]
public bool Cancelled
{
get { return m_cancelled; }
set { m_cancelled = value; }
}
private long m_errorCode;
[DataMember]
public long ErrorCode
{
get { return m_errorCode; }
set { m_errorCode = value; }
}
private Exception m_exceptionObj;
[DataMember]
public Exception Exception
{
get { return m_exceptionObj; }
set { m_exceptionObj = value; }
}
}
[ServiceContract]
public interface IFileXferService
{
[OperationContract]
bool InitFileRequest(ref FileXferCargo fileRequest);
[OperationContract]
bool InitGetFilePart(ref FileXferCargo fileCargo);
[OperationContract]
Stream GetFilePart(FileXferCargo fileCargo);
[OperationContract]
int CloseFile(FileData fileData);
}
The Service implementation class is defined as follows;
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple,
UseSynchronizationContext = false)
]
public class LNMediaServerSvc : IFileXferService
{
...
}

For your netTcpBinding, set the security mode to none, on both the client and the server configuration files:
<security mode="None" />
this is a quick fix.
If you need to have security enable for netTcpBinding then you have to implement delegation and impersonation.
read more here: http://msdn.microsoft.com/en-us/library/ms730088.aspx
From what I have read and googled this is an issue of multi-hop impersonation. Impersonation can carry over to one hop only. By setting the security mode to none you prevent the authentication step when calling the WCF service.

Don't have a comment button anymore so I'll put this here. Yes, I have turned on tracing, but it seems like I'm going to need to read a BOOK to understand all the stuff the trace data is trying to tell me. That said, I looked over the trace info and the one thing I couldn't see was anything leading up to the socket being aborted. Just seems to happen for no reason. :(

For me it was because of not having net.tcp binding in web.config in the server which hosted the WCF. When I added the same binding for the service I did not get the exception. So you need to have the binding in both sides in Web and WCF if they are hosted in different servers.

Related

WCFCore and serviceAuthorizationManager not working

I'm trying to assemble a .Net 6 WCF Service with WCFCore, using a basicHttpBinding, and I'm strugling to add a service authorization manager.
My purpose is to enable WCF to read and validate bearer tokens and use OAuth. I can't move to REST because of legacy applications compatibility, so I need to keep WCF but use bearer tokens.
My service at this stage is quite simple:
[ServiceContract]
public interface IService
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
public class Service : IService
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
My Program.cs:
var builder = WebApplication.CreateBuilder();
builder.Services.AddServiceModelServices();
builder.Services.AddServiceModelConfigurationManagerFile("wcf.config");
builder.Services.AddServiceModelMetadata();
builder.Services.AddSingleton<IServiceBehavior, UseRequestHeadersForMetadataAddressBehavior>();
builder.Services.AddSingleton<OAuthAuthorizationManager>();
var app = builder.Build();
app.UseServiceModel(bld =>
{
bld.AddServiceEndpoint<Service, IService>(new BasicHttpBinding(BasicHttpSecurityMode.Transport), "/Service.svc");
var mb = app.Services.GetRequiredService<ServiceMetadataBehavior>();
mb.HttpsGetEnabled = true;
});
app.Run();
Then my wcf.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicBinding" receiveTimeout="00:10:00">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="CoreWCFService.Service" behaviorConfiguration="Default">
<endpoint address="basic" binding="basicHttpBinding" bindingConfiguration="basicBinding" contract="CoreWCFService.IService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
<serviceAuthorization serviceAuthorizationManagerType="CoreWCFService.OAuthAuthorizationManager,CoreWCFService" />
<dataContractSerializer maxItemsInObjectGraph="10000000" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
But when I call the service with tokens, nothing happens on the authorization manager, the operation runs simply ignoring this service behavior.
Is there anyone out there that can help me with this?
You may refer to the Corewcf project template. There are a few things to note:
The interface and its implementation need to be separated to facilitate subsequent maintenance and invocation of the interface.
We need to look at the UseServiceModel part in Program.cs.

WCF Service receives null request

var dataToSend = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(mi));
var req = HttpWebRequest.Create("http://localhost/Service1.svc/json/MethodName");
req.ContentType = "application/json";
req.ContentLength = dataToSend.Length;
req.Method = "POST";
req.GetRequestStream().Write(dataToSend, 0, dataToSend.Length);
var response = req.GetResponse();
Here "/json" is my endpoint address and my service is configured with multiple endpoints. As per image here, request i sent is recieving null at server.
If my request format is not proper then suggest proper way to call this service.
// Service inter face
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method="POST")]
Response MethodName(Request request);
}
// Service1
public class Service1 : IService
{
public Response MethodName(Request request)
{
some logical operation....
}
}
// End point configuration (Web config)
<endpoint address="json" behaviorConfiguration="jsonBehavior"
binding="webHttpBinding" bindingConfiguration="webHttpBindingJson"
name="jsonn" contract="Service1.IService" />
<endpoint address="xml" behaviorConfiguration="poxBehavior" binding="webHttpBinding"
bindingConfiguration="webHttpBindingXml" name="xmll" contract="Service1.IService" />
<endpointBehaviors>
<behavior name="jsonBehavior">
<enableWebScript />
</behavior>
<behavior name="poxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
<webHttpBinding>
<binding name="webHttpBindingJson">
<security mode="None" />
</binding>
<binding name="webHttpBindingXml">
<security mode="None" />
</binding>
</webHttpBinding>
// Request class
[DataContract]
public class Request
{
string userMobile;
string otp;
[DataMember]
public string UserMobile
{
get { return userMobile; }
set { userMobile = value; }
}
[DataMember]
public string OTP
{
get { return otp; }
set { otp = value; }
}
}
Finally i found for this.
I modified endpoint of json behaviour configuration to this,
<behavior name="jsonBehavior">
<webHttp defaultBodyStyle ="Bare"/>
<!--<enableWebScript />-->
</behavior>
and removed enableWebScript. Finally my code working.

How to set up WorkflowService authentication?

I just need to secure my WF services. Can't find any resources on this. How to do it?
Already tried:
class Program
{
static void Main(string[] args)
{
using (WorkflowServiceHost host = new WorkflowServiceHost(new Workflow1(), new Uri("http://localhost/Test")))
{
host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new Test();
host.Open();
Console.Write("ready");
Console.ReadLine();
}
}
}
public class Test : UserNamePasswordValidator
{
public Test()
{
Console.Write("hit");
}
public override void Validate(string userName, string password)
{
Console.Write("never hit");
}
}
And a config
<bindings>
<wsHttpBinding>
<binding>
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<!--<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="myAssembly.Test, myAssembly" />
</serviceCredentials>-->
</behavior>
</serviceBehaviors>
Can't create a fixed name endpoint because they are dynamically created
UPDATE - I tried the configuration bellow and worked, but I want a more granular way to set what binding each service use
<protocolMapping>
<add scheme="http" binding="wsHttpBinding"/>
</protocolMapping>
We have an episode of Workflow TV that should help. Workflow TV - Workflow Services Security
As far as the messaging part this is just WCF so anything you can do with WCF should work here.
That said with workflow you typically need more fine grained control on all but the first request. For example all employees can start en expense report but only the employee who started a specific expense report can add expenses to it and submit it. You can do these kind of security checks using the WF Security Pack.
A little hackish, but works. Overrided WorkflowServiceHost in order to grab unknown contract names and added service endpoints for each one.
const string DEFAULT_WORKFLOW_SERVICE_BINDING_NAME = "WorkflowDefaultBinding";
static void Main(string[] args)
{
MyWorkflowServiceHost host = new MyWorkflowServiceHost(new CountingWorkflow2(), new Uri(hostBaseAddress));
foreach (var contractName in host.ImplementedContractsNames)
{
// now I'm able to choose which binding to use depending on a condition
var binding = new WSHttpBinding(DEFAULT_WORKFLOW_SERVICE_BINDING_NAME);
host.AddServiceEndpoint(contractName, binding, string.Empty);
}
}
And MyWorkflowServiceHost
public class MyWorkflowServiceHost : WorkflowServiceHost
{
public MyWorkflowServiceHost(Activity activity, params Uri[] baseAddresses)
: base(activity, baseAddresses)
{
}
private IDictionary<string, System.ServiceModel.Description.ContractDescription> _implementedContracts;
public IEnumerable<string> ImplementedContractsNames
{
get
{
foreach (var contract in _implementedContracts)
yield return contract.Key;
}
}
protected override System.ServiceModel.Description.ServiceDescription CreateDescription(out System.Collections.Generic.IDictionary<string, System.ServiceModel.Description.ContractDescription> implementedContracts)
{
System.ServiceModel.Description.ServiceDescription description = base.CreateDescription(out implementedContracts);
_implementedContracts = implementedContracts;
return description;
}
}
Adding a unamed WSHttpBinding and the following section on service model should work too, but for default configuration
<protocolMapping>
<add scheme="http" binding="wsHttpBinding"/>
</protocolMapping>

WCF - Streaming file upload over http

I am trying to build a WCF service that will allow my WPF desktop clients to upload files to a server.
I adapted a code sample from The Code Project (WCF Streaming: Upload/Download Files Over HTTP) and I've looked at several SO posts as well, but can't seem to get this working.
When I execute the code, it fails with a null reference exception at the point that the server tries to read the stream that has been passed through the interface.
At this point, I am rather lost and don't know how to fix this up. Any suggestions are appreciated.
Code samples follow:
CustomerDocumentModel is the data element that I pass through the WCF interface with the stream to read the client side file:
[DataContract]
[KnownType(typeof(System.IO.FileStream))]
public class CustomerDocumentModel : IDisposable
{
public CustomerDocumentModel()
{
}
public CustomerDocumentModel(string documentName, string path)
{
DocumentName = documentName;
Path = path;
}
[DataMember]
public string DocumentName;
[DataMember]
public string Path;
[DataMember]
public System.IO.Stream FileByteStream;
public void Dispose()
{
if (FileByteStream != null)
{
FileByteStream.Close();
FileByteStream = null;
}
}
}
IBillingService is the interface definition for my WCF service:
[ServiceContract]
public interface IBillingService
{
// other methods redacted...
[OperationContract]
void UploadCustomerDocument(CustomerDocumentModel model);
}
The class BillingService implements the WCF service:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class BillingService : IBillingService
{
// Other methods redacted ...
public void UploadCustomerDocument(CustomerDocumentModel model)
{
string path = HttpContext.Current.Server.MapPath(
String.Format("/Documents/{1}",
model.DocumentName));
using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None))
{
const int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int size = 0;
try
{
// The following Read() fails with a NullReferenceException
while ((size = model.FileByteStream.Read(buffer, 0, bufferSize)) > 0)
{
stream.Write(buffer, 0, size);
}
}
catch
{
throw;
}
finally
{
stream.Close();
model.FileByteStream.Close();
}
}
}
}
A few relevant bits from the web.config on my WCF web server:
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpRuntime maxRequestLength="2097151" useFullyQualifiedRedirectUrl="true" executionTimeout="360"/>
</system.web>
<system.serviceModel>
<serviceHostingEnvironment
aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
<bindings>
<basicHttpBinding>
<binding name="userHttps" transferMode="Streamed" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="">
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
The client is a WPF/MVVM app that creates a CustomerDocumentModel model, uses an OpenFileDialog to Open() the file stream and then passes the model to the UploadCustomerDocument method on WCF Service.
If I am missing any relevant details, please ask.
I know this rather very late reply for your question and I'm sure you must have resolved your problem as well. This could be helpful to someone else :-)
Use Messagecontract over Datacontract and only one MessageBodyMember with datatype Stream and rest all parameter are MessageHeader.
Here is the example:
[MessageContract]
public class CustomerDocumentModel : IDisposable
{
public CustomerDocumentModel(string documentName, string path)
{
DocumentName = documentName;
Path = path;
}
[MessageHeader]
public string DocumentName{get;set;}
[MessageHeader]
public string Path{get;set;}
[MessageBodyMember]
public System.IO.Stream FileByteStream{get;set;}
public void Dispose()
{
if (FileByteStream != null)
{
FileByteStream.Close();
FileByteStream = null;
}
}
}
Note: Make sure your in your configuration transfer mode is StreamedResponse, also you may want to change the MessageEncoding to MTOM for better performance.
public void UploadCustomerDocument(CustomerDocumentModel model)
{
var filename = //your file name and path;
using (var fs = new FileStream(filename, FileMode.Create))
{
model.FileByteStream.CopyTo(fs);
}
}
Your data type is what is making the streaming fail. This is documented on MSDN here: http://msdn.microsoft.com/en-us/library/ms731913.aspx
The relevant passage is:
Restrictions on Streamed Transfers
Using the streamed transfer mode causes the run time to enforce
additional restrictions.
Operations that occur across a streamed transport can have a contract
with at most one input or output parameter. That parameter corresponds
to the entire body of the message and must be a Message, a derived
type of Stream, or an IXmlSerializable implementation. Having a return
value for an operation is equivalent to having an output parameter.
Some WCF features, such as reliable messaging, transactions, and SOAP
message-level security, rely on buffering messages for transmissions.
Using these features may reduce or eliminate the performance benefits
gained by using streaming. To secure a streamed transport, use
transport-level security only or use transport-level security plus
authentication-only message security.
SOAP headers are always buffered, even when the transfer mode is set
to streamed. The headers for a message must not exceed the size of the
MaxBufferSize transport quota. For more information about this
setting, see Transport Quotas.

MSMQ WCF Throttling

I have a windows service that reads my message queue through WCF. I want the service to process one message before another message (intensive memory actions per msg). I set the throttling configuration to 1, but it does not seem to do anything. If i have 6 messages in my queue, it takes 4 right after the start.
Am i missing something?
My web.config :
<system.serviceModel>
<client>
<endpoint
address="net.tcp://spserv30:9999/services/SPInterface"
binding="netTcpBinding" bindingConfiguration="tcpspbinding"
contract="Itineris.OPM.WCFSP.ActionContracts.ISPActions" >
</endpoint>
</client>
<services>
<service name="Itineris.OPM.MSMQProcessorV2.MSMQProcessor" behaviorConfiguration="Throttled" >
<endpoint address="msmq.formatname:DIRECT=OS:localhost\private$\documents" binding="msmqIntegrationBinding"
bindingConfiguration="MSMQProcessorBinding" contract="Itineris.OPM.MSMQProcessorV2.IMSMQProcessor" />
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="tcpspbinding" transferMode="StreamedRequest" />
</netTcpBinding>
<msmqIntegrationBinding>
<binding name="MSMQProcessorBinding" maxReceivedMessageSize="2147483647"
receiveRetryCount="0" retryCycleDelay="00:10:00" maxRetryCycles="0"
receiveErrorHandling="Move">
<security mode="None" />
</binding>
</msmqIntegrationBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="Throttled">
<serviceThrottling
maxConcurrentCalls="1"
maxConcurrentSessions="1"
maxConcurrentInstances="1"
/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
My servicehost creation :
protected override void OnStart(string[] args)
{
if (_serviceHost != null)
{
if (_serviceHost.State != CommunicationState.Faulted)
_serviceHost.Close();
else
_serviceHost.Abort();
}
//create servicehost
_serviceHost = new ServiceHost(typeof(MSMQProcessor));
_serviceHost.Open();
_serviceHost.Faulted += serviceHost_Faulted;
// Already load configuration here so that service does not start if there is a configuration error.
new DocumentGeneratorV2.LoadGeneratorConfigurator().Load();
var startLog = new LogEntry {Message = "Itineris MSMQ Processor Service V2 has started"};
startLog.Categories.Add(CategoryGeneral);
startLog.Priority = PriorityNormal;
Logger.Write(startLog);
}
private void serviceHost_Faulted(object sender, EventArgs e)
{
if (!_isClosing)
{
_serviceHost.Abort();
_serviceHost = new ServiceHost(typeof(MSMQProcessor));
_serviceHost.Faulted += serviceHost_Faulted;
_serviceHost.Open();
}
}
Class with contract :
[ServiceContract(Namespace = "http://Itineris.DocxGenerator.MSMQProcessor")]
[ServiceKnownType(typeof(string))]
public interface IMSMQProcessor
{
[OperationContract(IsOneWay = true, Action = "*")]
void GenerateWordDocument(MsmqMessage<string> message);
}
public class MSMQProcessor : IMSMQProcessor
{
/// <summary>
/// Method that processed the message and generates a word document
/// </summary>
/// <param name="message">message from MSMQ to be processed</param>
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void GenerateWordDocument(MsmqMessage<string> message)
{
DocumentGeneration documentGenerator = null;
var state = new DocumentStatus();
var docGenerator = new DocumentGenerator(new LoadGeneratorConfigurator().Load());
var deserializer = new XmlSerializer(typeof(DocumentGeneration));
documentGenerator = deserializer.Deserialize(new StringReader(message.Body)) as DocumentGeneration;
if(documentGenerator == null)
throw new Exception("Deserializing of the message has failed");
docGenerator.MailQueue = appSettings["MAILQUEUE"];
docGenerator.GenerateDocument(documentGenerator);
var builder = new StringBuilder();
builder.Append("The documents have been saved to the following locations: \r\n");
}
}
Your service as configured in the question should only process message at a time. Although you are not using the ServiceBehavior attribute for the service implementation class, the default value for the ConcurrencyMode is Single not Multiple (which could cause the behavior you are seeing). The default value of InstanceContextMode is Per Session but the maxConcurrentInstances and maxConcurrentSessions values force support for a single session at a time.
The only other option that I see is to force the ServiceHost to use only one service instance by using a different constructor. Here is the code:
// ... snipped ...
//force single service instance to be used by servicehost
var singleton = new MSMQProcessor();
_serviceHost = new ServiceHost(singleton);
_serviceHost.Open();
// ... snipped ...