service code executed in wrong order - wcf

I have a problem with WCF service. It seems that code is executed in unexpected order. My service takes one argument which is class that have array with file attachments (arrays of bytes) and other text fields. My service is responsible for save those files on disk. Also every request to service and response is logged in database (in xml format). For this reason in my service I want to:
Save files on disk;
Clear field with file binary data (that binary data won’t be saved in log in database);
Update field with attachement name with path where file was saved (that this info would be in log record);
Everything works fine when I’m testing service locally in visual studio (not using service client proxy, but directly service).
Problem is when I’m testing service hosted on IIS (v 7.5). In my service, after saved file on disk I’m changing field with file name and clear field with binary data of file. The problem is that system update those fields before saved file on disk (result is that saved file is empty or error is thorwn /wrong save path/ ) . Of course in my service first task in code is to save file on disk.
This is how it looks in my example:
I’m using VS2013, .NET Framework 4.5, WCF
Attachment Class
public class Attachment
{
public string FilePath { get; set; }
public byte[] FileBinary { get; set; }
}
Service code (with some comments about the problem):
public bool AddAttachments(List<Attachment> attachments)
{
bool result = false;
string PathToSaveFile = String.Empty;
string FileNameGuid = String.Empty;
foreach (var a in attachments)
{
FileNameGuid = Guid.NewGuid().ToString();
// step 1. Set file path and name
PathToSaveFile = "C:\\FilesFromWCFService\\" + FileNameGuid + a.FilePath;
try
{
// step 2. saving file
// problem is here. Variable PathToSaveFile have value from step 1 and 3b (which i thing has not yet been executed)
// so value is i.e.: "C:\FilesFromWCFService\{Guid}{FilePath}File localization: {PathToSaveFile}"
// and a.FileBinary is equal to byte[0] step 3a.
// it seems like steps 3a and 3b is executed before step 2
File.WriteAllBytes(PathToSaveFile, a.FileBinary);
result = true;
}
catch (Exception ex)
{
throw new Exception("Error while saving file on disk");
}
// step 3a. clear file binary data (that data won't be saved in db log)
a.FileBinary = new byte[0];
// step 3b. update file localization on server
a.FilePath = String.Format("File localization: {0}", PathToSaveFile);
// reset variables
PathToSaveFile = String.Empty;
FileNameGuid = String.Empty;
}
Return result;
}
When I’m debugging service on iis (by attach iis process in VS) and set breakpoint on the first line of service code i can see that byte file content (a.FileBinary) and file path (a.FilePath) is set from step 3a and 3b (not original from parameter List attachments.
If I comment changing file path and reseting file content (step 3a and step 3b) then everything works fine (but of course save file in binary format in logs - which i want avoid).
Service configuration:
<services>
<service name="AddAttachments">
<endpoint address="AddAttachmentsService" behaviorConfiguration="ServiceEndpointBehavior"
binding="basicHttpBinding" name="httpEndpoint" contract="PlayService.IAddAttachmentsService" />
<endpoint address="AddAttachmentsService" behaviorConfiguration="ServiceEndpointBehavior"
binding="netTcpBinding" name="tcpEndpoint" contract="PlayService.IAddAttachmentsService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:65045/" />
<add baseAddress="net.tcp://localhost:65055/" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding messageEncoding="Mtom"
closeTimeout="00:10:00"
sendTimeout="00:10:00"
maxBufferPoolSize="524288"
maxReceivedMessageSize="2147483647">
</binding>
</basicHttpBinding>
<netTcpBinding>
<binding transferMode="Streamed"
closeTimeout="00:10:00"
sendTimeout="00:10:00"
maxBufferPoolSize="524288"
maxReceivedMessageSize="2147483647"></binding>
</netTcpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="ServiceEndpointBehavior" >
<logMessage_2 />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="mexBehaviour">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
Client configuration:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="httpEndpoint" messageEncoding="Mtom"
closeTimeout="00:10:00"
sendTimeout="00:10:00"
maxBufferPoolSize="524288"
maxReceivedMessageSize="2147483647"></binding>
<!--<binding name="httpEndpoint" messageEncoding="Mtom" />-->
</basicHttpBinding>
<netTcpBinding>
<binding name="tcpEndpoint"
closeTimeout="00:10:00"
sendTimeout="00:10:00"
maxBufferPoolSize="524288"
maxReceivedMessageSize="2147483647"></binding>
<!--<binding name="tcpEndpoint" transferMode="Streamed" />-->
</netTcpBinding>
</bindings>
<client>
<endpoint address="http://localhost/MyService/MyService.svc/AddAttachmentsService"
binding="basicHttpBinding" bindingConfiguration="httpEndpoint"
contract="PService.IAddAttachmentsService" name="httpEndpoint" />
<endpoint address="net.tcp://myComp/MyService/PlayService.svc/AddAttachmentsService"
binding="netTcpBinding" bindingConfiguration="tcpEndpoint"
contract="PService.IAddAttachmentsService" name="tcpEndpoint">
<identity>
<servicePrincipalName value="host/MyComp" />
</identity>
</endpoint>
</client>
</system.serviceModel>
Problem is the same when I’m using netTcpBinding or basicHttpBinding.
I’m new in WCF and i’m don’t have idea what else I can search on web. I have read about streaming (there is no difference when I’m set transferMode on Streamed or else), about concurrency and instancing (I have setting different option – but I think that this have nothing to do with this problem).
If I’m gave to little information please let me know.

Ok, i found out what the problem is:
when i'm testing my service i'm passing parameter (object) to the first calling of service and then to the secound call of service (but object was changed in first call) - i forgot that object is passing by reference :)
i found out that i can't change message in service calling (for logging purposes) - i log message through message inspector (method AfterReceiveRequest) which is calling earlier than service execution.

Related

Problem increasing WCF service endpoint size configuration

I have an asp.net project that was hosting a WCF service (via the wcf file exposure technique for IIS) that was put together around 10 years ago. There is an operation on the service that retrieves content that is greater than the default 75K limit. The service configuration (in web.config) property on the binding was adjusted to be larger than the default and was working ok. The ASP.NET project was upgraded in VS2017/2019 - and (I assume) the project was adjusted (somehow). The service operation described above no longer works. I did a lot of investigation and all solutions were handled (which makes sense as the original version of the project was working). I even went through the process of building a new web.config with minimal configuration and still did not work. I also adjusted the service to include the Configure operation that could be used to configure the endpoint configuration in code. It also did not work. For this I have the following:
public static void Configure(ServiceConfiguration config)
{
BasicHttpBinding binding = new BasicHttpBinding();
binding.MaxReceivedMessageSize = int.MaxValue;
binding.MaxBufferSize = int.MaxValue;
binding.MaxBufferPoolSize = int.MaxValue;
binding.BypassProxyOnLocal = true;
binding.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxStringContentLength = int.MaxValue,
MaxDepth = int.MaxValue,
MaxArrayLength = int.MaxValue,
MaxBytesPerRead = int.MaxValue,
MaxNameTableCharCount = int.MaxValue
};
var endpoint = new ServiceEndpoint(
ContractDescription.GetContract(
typeof(IAuthenticateServicePack)),binding,new
EndpointAddress("http://localhost:49437/WCFSevices/AuthenticateServicePack.svc"));
//MyEndpointBehavior Behavior = new MyEndpointBehavior();
//endpoint.Behaviors.Add(Behavior);
config.AddServiceEndpoint(endpoint);
}
The web.config definitions is the following
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true"
/>
<behaviors>
<serviceBehaviors>
<behavior name="ServicePack.ServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="500" maxConcurrentSessions="500"
/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<clear />
<binding name="myBindingForBigArrays" maxReceivedMessageSize="2147483647"
transferMode="Buffered">
<readerQuotas
maxDepth="32"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="4096"
maxNameTableCharCount="10000"
/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service name="Lapp.WebAdmin.Mvc.WCFSevices.AuthenticateServicePack"
behaviorConfiguration="ServicePack.ServiceBehavior">
<endpoint address="" binding="basicHttpBinding"
bindingConfiguration="myBindingForBigArrays"
contract="Lapp.WebAdmin.Mvc.WCFSevices.IAuthenticateServicePack" />
<host>
<timeouts closeTimeout="00:01:00" />
</host>
</service>
</services>
</system.serviceModel>
For some reason - the updated asp.net project does not seem to be handling the WCF service endpoint configuration. Has anyone run into this issue?
Note: I can test the older version of the service and is working properly -- and reviewing the old config with the new -- cannot identify any modification that would appear to be causing the new not to use the size limit settings.

WCF This could be due to the service endpoint binding not using the HTTP protocol

Default.aspx.cs
WCFService.Service1Client client = new WCFService.Service1Client();
string stream = client.JsonSerializeFromDatabase();
client.Close();
WCFService.Service1Client client2 = new WCFService.Service1Client();
foreach (WCFService.Person in client2.JsonDeserializeFromDatabase(stream))
Service1.svc.cs
public IList<Person> JsonDeserializeFromDatabase(string value)
{
MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(value));
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(List<Person>));
IList<Person> tableData = (IList<Person>)ser.ReadObject(ms);
ms.Close();
ms.Dispose();
return tableData;
}
IService1.cs
[OperationContract]
IList<Person> JsonDeserializeFromDatabase(string value);
Server Web.config
<httpRuntime maxRequestLength="8192"/>
</system.web>
...
<system.serviceModel>
<services>
<service name="TestWCF.Service1" behaviorConfiguration="TestWCF.Service1Behavior">
<endpoint address="" binding="wsHttpBinding" contract="TestWCF.IService1">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="TestWCF.Service1Behavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
</behavior>
</serviceBehaviors>
Client Web.config
<httpRuntime maxRequestLength="8192"/>
</system.web>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="debuggingBehaviour">
<dataContractSerializer maxItemsInObjectGraph="2147483646" />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1" closeTimeout="00:50:00" openTimeout="00:50:00" receiveTimeout="00:50:00" sendTimeout="00:50:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="64" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
<reliableSession ordered="true" inactivityTimeout="00:50:00" enabled="false"/>
<security mode="Message">
<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="~~~~~/Service1.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1" contract="WCFService.IService1" name="WSHttpBinding_IService1" behaviorConfiguration="debuggingBehaviour">
Exception Information
- Type: System.ServiceModel.CommunicationException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
- Message: An error occurred while receiving the HTTP response to ~~~~~/Service1.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.
I got this exception information from Server trace viewer, so please do not advise me to put <-system.diagnostics-> tag.
As you can see, I increased all the size thing.
Like.. i don't know why I am getting an error when I call JsonDeserializeFromDatabase(stream).
"An error occurred while receiving the HTTP response to ~~~~~/Service1.svc. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."
I too have experienced this error message when returning records from a database in a WCF service. As well as increasing maxReceivedMessageSize in the binding in the client configuration (App.config), a separate problem seems to be that WCF has problems serializing Entity Framework objects if they have relationships that lead to circularity in their object graphs.
I solved this by returning buddy class objects (which are copies of the raw database records, but without any relationship links) rather than the raw database classes themselves.
Hope this helps -
And WHY doesn't Microsoft produce better error messages?? Here, as in many other cases, the error message gives no clue to the real problem (the serialization of the return value from the WCF call)!
re: WCF & problems serializing Entity Framework objects if they have relationships that lead to circularity in their object graphs. I was getting the same error and the answer provided by user1956642 and it did point me in the right direction, but later realized I could serialize these entities by configuring the DbContext
context.Configuration.ProxyCreationEnabled = false;
Lazy loading is still enabled, but I believe the dynamic proxies are used for change tracking and lazy loading. So yea ... just my 5c

WCF problem with uploading large file, hosted in IIS

I am trying to upload large files to a WCF Service hosted in IIS.
I am using Restful and Streaming method.
But I am not able to upload files which is more than 64KB.
I tried lot by changing all the size-related elements in web.config file, but failed to fix the issue.
Here is my code and config, please let me know if anyone find any issues in the code and how can I fix.
Operation Contract
[OperationContract]
[WebInvoke(UriTemplate = "/UploadImage/{filename}")]
bool UploadImage(string filename, Stream image);
Implementation of Operation Contract
public bool UploadImage(string filename, Stream image)
{
try
{
string strFileName = ConfigurationManager.AppSettings["UploadDrectory"].ToString() + filename;
FileStream fileStream = null;
using (fileStream = new FileStream(strFileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
const int bufferLen = 1024;
byte[] buffer = new byte[bufferLen];
int count = 0;
while ((count = image.Read(buffer, 0, bufferLen)) > 0)
{
fileStream.Write(buffer, 0, count);
}
fileStream.Close();
image.Close();
}
return true;
}
catch (Exception ex)
{
return false;
}
}
web.config
<system.serviceModel>
<services>
<service name="Service" behaviorConfiguration="ServiceBehavior">
<endpoint address="http://localhost/WCFService1" behaviorConfiguration="web"
binding="webHttpBinding" bindingConfiguration="webBinding"
contract="IService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="webBinding"
transferMode="Streamed"
maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"
openTimeout="00:25:00" closeTimeout="00:25:00" sendTimeout="00:25:00"
receiveTimeout="00:25:00" >
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
and
<httpRuntime maxRequestLength="2097151"/>
Service is hosted in hosted in IIS
Client side Code (console application)
private static void UploadImage()
{
string filePath = #"F:\Sharath\TestImage\TextFiles\SampleText2.txt";
string filename = Path.GetFileName(filePath);
string url = "http://localhost/WCFService1/Service.svc/";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + "UploadImage/" + filename);
request.Accept = "text/xml";
request.Method = "POST";
request.ContentType = "txt/plain";
FileStream fst = File.Open(filePath, FileMode.Open);
long imgLn = fst.Length;
fst.Close();
request.ContentLength = imgLn;
using (Stream fileStream = File.OpenRead(filePath))
using (Stream requestStream = request.GetRequestStream())
{
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int byteCount = 0;
while ((byteCount = fileStream.Read(buffer, 0, bufferSize)) > 0)
{
requestStream.Write(buffer, 0, byteCount);
}
}
string result;
using (WebResponse response = request.GetResponse())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
}
Console.WriteLine(result);
}
With this much of code I am able to upload 64KB of file, but when I try to upload a file of more than 64KB in size, I am getting error like,
The remote server returned an error: (400) Bad Request
i did what you told but still I am getting same problem, this is how my config looks like now, can you please tell me what is still missing here
<services>
<service name="Service" behaviorConfiguration="ServiceBehavior">
<endpoint address="http://localhost/WCFService1" behaviorConfiguration="web"
binding="webHttpBinding" bindingConfiguration="webBinding"
contract="IService">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<webHttpBinding>
<binding transferMode="Streamed"
maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"
openTimeout="00:25:00" closeTimeout="00:25:00" sendTimeout="00:25:00" receiveTimeout="00:25:00"
name="webBinding">
<readerQuotas maxDepth="64"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647"/>
</binding>
</webHttpBinding>
</bindings>
The large data transfer issue with wcf:
I have wcf 4.0 service hosted on IIS 7, windows 2008 server. When I call my service with small data say 4K or 5K bytes then request get processed easily but while trying to upload large size it gave me following errors
Bad Request 400
File not found 404 13 as seen in IIS 7 logs
There is no end point listening to the service "myservice url"
In all caseses I was able to transfer small data request with my client application to server but for large size message. Request failed.
I have tried all methods available to solve this issues but nothing worked for me.
After a scratching my head for a week and reading all articles and blogs finally I figured following
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="myBinding" closeTimeout="00:10:00" maxBufferPoolSize="250000000" maxReceivedMessageSize="250000000" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" messageEncoding="Text">
<readerQuotas maxDepth="4500000" maxStringContentLength="4500000" maxBytesPerRead="40960000" maxNameTableCharCount="250000000" maxArrayLength="4500000"/>
</basicHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
<system.web>
<httpRuntime executionTimeout="4800" maxRequestLength="500000000"/>
</system.web>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="500000000"></requestLimits>
</requestFiltering>
</security>
</system.webServer>
<!-- other useful setting -->
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
So I think It may help someone days....
Based on the size mentioned by you, it seems error due to limit hit at the binding level i.e. readerQuota values. You can confirm if denial of server is due to exceeding the limit at binding level by capturing WCF traces. We can't see the configuration you've posted so, providing our best guess besed on information which is visible.
I'd capture WCF traces at verbose level to troubleshoot the issue.
Btw, have you tried increasing maxRequestLength?
http://msdn.microsoft.com/en-us/library/system.web.configuration.httpruntimesection.maxrequestlength.aspx
HTH,
Amit Bhatia
what fixed 404 size of the message too big problem for me was a part of WaseemM's example:
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="500000000"></requestLimits>
</requestFiltering>
</security>
See...all of us who are facing this problem of 64KB upload thingy are missing the very basic point. We are setting high values for maxAllowedContentLength, maxReceivedMessageSize...blah blah but still nothing works :). We all miss the very basic point(...well at least I was) is no matter whatever binding configuration we set, are we anywhere mentioning that our ENDPOINT SHOULD FOLLOW THE BINDING CONFIG????...no where...So basically we must let the ENDPOINT know that it must follow our BINDING CONFIG.
<endpoint address=""
binding="webHttpBinding"
behaviorConfiguration="Your behaviour config name"
bindingConfiguration="YOUR BINDING CONFIG NAME"
contract="Your contract service" />
If transferring large data is if your task you should use MTOM. Just search for "MTOM WCF".
Chandrachur is correct, whatever you specify in your <binding/> or <readerQuotas/>, you need to add "bindingConfiguration="my binding config name" in your <endpoint/>. Otherwise it won't work even if your binding config is correct. you need to make sure your configs are applied to your endpoint. to do so, you need "bindingConfiguration" set correctly.

WCF Service TcpNetBinding StreamRequest not receiving stream

My first question so be gentle =)
The following is all .Net 4, VB.Net, in VS2010. The end goal is to receive a (complete) stream from the client over Tcp binding, to an IIS Hosted WCF Service. The problem I am facing is that the service is not able to read any bytes from the provided stream. Now with the nitty gritty... I've removed a fair amount for brevity but, let me know if I've omitted something important.
The service contract is as follows:
<ServiceContract(Namespace:="ImageSystem")> _
Public Interface IUploadService
<OperationContract()> _
Function UploadFile(ByVal file As ImageUpload) As ImageUpload
End Interface
The data contract ImageUpload is as follows:
<MessageContract()> _
Public Class ImageUpload
#Region " Message Header "
Private _ImageID As Nullable(Of Long)
<MessageHeader()> _
Public Property ImageID() As Nullable(Of Long)
Get
Return _ImageID
End Get
Set(ByVal value As Nullable(Of Long))
_ImageID = value
End Set
End Property
'... a few other value type properties
#End Region
#Region " Message Body"
' Do not add any more members to the message body or streaming support will be disabled!
<MessageBodyMember()> _
Public Data As System.IO.Stream
#End Region
End Class
The relevant server config/bindings are as follows (these are obviously dev environment settings only):
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTcpStreamBinding" transferMode="Streamed" maxBufferSize="20971520" maxReceivedMessageSize="20971520"/>
</netTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="UploadServiceBehaviour"
name="ImageSystem.SVC.UploadService">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="netTcpStreamBinding"
contract="ImageSystem.SVC.IUploadService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:809/UploadService" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="UploadServiceBehaviour">
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
The WCF Service is a service Library, which is hosted by a Web Application. The Web Application project runs in my local IIS 7.5. IIS has been configured to enable TCP connections, and the application pool identity is configured with relevant permissions to the contract implementation. VS2010 is run as admin to enable debugging in IIS.
To test the contract implementation I have a Windows Console Application set up as a (test) client. The client proxy classes were generated by adding a service reference to the service within the IIS host (http://localhost/ImageSystem/UploadService.svc). The service reference is configured to generate async methods.
The relevant auto-generated client config is as follows (note, I've tried increasing maxBufferPoolSize, maxBufferSize, and maxReceivedMessageSize to match the servers config of "20971520", but to no avail):
[EDIT: reliableSessions section commented out in light of Sixto Saez's suggestion but to no avail]
<system.serviceModel>
<bindings>
<binding name="NetTcpBinding_IUploadService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Streamed" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="20971520"
maxBufferSize="20971520" maxConnections="10" maxReceivedMessageSize="20971520">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<!--<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />-->
<security mode="None">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://mycomputername.mydomain/ImageSystem/UploadService.svc"
binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IUploadService"
contract="UploadService.Local.IUploadService" name="NetTcpBinding_IUploadService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
The client usage is as follows:
Public Sub Test()
Dim serviceClient As UploadService.Local.UploadServiceClient = New UploadService.Local.UploadServiceClient
AddHandler serviceClient.UploadFileCompleted, AddressOf LocalTestCallback
Dim ms As MemoryStream = New MemoryStream
My.Resources.Penguins.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg)
serviceClient.ClientCredentials.Windows.ClientCredential.Domain = "MYDOMAIN"
serviceClient.ClientCredentials.Windows.ClientCredential.UserName = "User"
serviceClient.ClientCredentials.Windows.ClientCredential.Domain = "Password123"
serviceClient.UploadFileAsync(Nothing, ..., ms, ms) '"..." is obviously not actually here, other values omitted. "ms" is passed as UserState object in addition to fulfilling the 'Data' parameter
End Sub
In case you wonder (or it matters), the penguins image is the one provided with Windows 7 in the sample pictures directory. The image is 777,835 bytes (should be within the relevant request/buffer max sizes).
I have tried two approaches to read the image on the server side.
Approach 1:
Public Function UploadFile(ByVal file As ImageUpload) As ImageUpload Implements IUploadService.UploadFile
Dim uploadBuffer(Helper.Settings.AppSettings(Of Integer)("UploadBufferSize", True) - 1) As Byte
Dim ms As MemoryStream = New MemoryStream()
Dim bytesRead As Integer
Do
bytesRead = file.Data.Read(uploadBuffer, 0, uploadBuffer.Length)
ms.Write(uploadBuffer, 0, bytesRead)
Loop Until bytesRead = 0
End Function
Approach 2:
Public Function UploadFile(ByVal file As ImageUpload) As ImageUpload Implements IUploadService.UploadFile
Dim reader As StreamReader = New StreamReader(file.Data)
Dim imageB64 As String = reader.ReadToEnd
ms = New MemoryStream(Convert.FromBase64String(imageB64))
End Function
In both cases, ms.Length = 0. More clearly, in the second approach, imageB64 = "" (empty string).
Why aren't I receiving anything from the stream? Also, as a sneaky sub-question, why does the generated proxy class not provide an overload that accepts an object of type ImageUpload?
Thank you in advance!!
it seemed strange to me that you would have the problems you mentioned, so I was curious and put together an implementation using your service contract. That one actually worked straight away. I don't actually know what went wrong in your case (it's not obvious), but let me just post a working solution here hoping that this will help you to solve your problem.
Unfortunately, as I abandoned VB many years ago, I can only provide C# code. Hope that's alright.
Server Web.config (tested in IIS, with net.tcp binding):
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding transferMode="Streamed" maxReceivedMessageSize="1000000">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="ImageSystem.SVC.UploadService">
<endpoint address="" binding="netTcpBinding" contract="ImageSystem.SVC.IUploadService">
</endpoint>
<endpoint address="mex" kind="mexEndpoint" binding="mexTcpBinding"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Client app.config (console test app):
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding transferMode="Streamed" maxReceivedMessageSize="1000000">
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint
address="net.tcp://localhost/WcfService1/UploadService.svc"
binding="netTcpBinding"
contract="ImageServices.IUploadService"
name="NetTcpBinding_IUploadService">
</endpoint>
</client>
</system.serviceModel>
Service contract & implementation:
[ServiceContract(Namespace="urn:ImageSystem")]
public interface IUploadService
{
[OperationContract]
ImageUpload UploadFile(ImageUpload file);
}
[MessageContract]
public class ImageUpload
{
[MessageHeader]
public long? ImageID { get; set; }
[MessageBodyMember]
public Stream Data;
}
public class UploadService : IUploadService
{
public ImageUpload UploadFile(ImageUpload file)
{
long length;
using (var ms = new MemoryStream())
{
file.Data.CopyTo(ms);
length = ms.Length;
}
return new ImageUpload { ImageID = length, Data = new MemoryStream() };
}
}
Test app:
private static readonly string imgPath = #"C:\Pictures\somepicture.jpg";
private static readonly EventWaitHandle waitHandle = new AutoResetEvent(false);
static void Main()
{
long? result;
using (var service = new ImageServices.UploadServiceClient("NetTcpBinding_IUploadService"))
{
var image = new ImageServices.ImageUpload();
using (var imgStream = File.OpenRead(imgPath))
{
image.Data = imgStream;
service.UploadFileCompleted += (sender, e) =>
{
result = e.Result;
if (e.Data != null) image.Data.Dispose();
waitHandle.Set();
};
service.UploadFileAsync(null, imgStream);
waitHandle.WaitOne();
}
}
}
First of all, as you can see, the config files can be a lot simpler. Especially the large BufferSize value is not necessary. Then, with regard to the service contract, it's not clear to me why the Upload operation would receive AND return an ImageUpload message. In my implementation, I'm returning the uploaded file size in the ImageID parameter just for demo purposes, of course. I don't know what your reasoning behind that contract was, and what you actually would want to return.
Actually, was just about to click "Send" when I had an idea why your code could have failed. In your test client, before you call serviceClient.UploadFileAsync(), add this line to your code: ms.Seek(0, SeekOrigin.Begin).
This resets the position of the MemoryStream back to its beginning. If you don't do that, the MemoryStream will be consumed only from its current position, which is its end - and which explains the Length = 0 of the stream received on the service side!
You may have already seen the information in this MSDN article but you should review the section called Streaming Data and the restrictions it lists. Your client config shows the reliableSession element having the ordered attribute set to "true" which is not supported for streaming.
I don't know if that is specifically the cause of your issue but it is a start. That article also layouts pretty well the basic config required for streaming so you should ensure your config complies with its recommendations.

WCF FaultException error

I'm new to WCF and I have issues throwing exceptions from my WCF Service to the client. I'm using code examples which I copied from the web. (I'm using VS2010 .NET Framework 4.0)
I created an ErrorHandler where the ProvideFault-method looks like this:
public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message msg)
{
FaultException<Exception> faultException = new FaultException<Exception>(error, error.Message, new FaultCode("Testing."));
MessageFault messageFault = faultException.CreateMessageFault();
msg = Message.CreateMessage(version, messageFault, Constants.FaultAction);
}
The fault contract looks like this:
[FaultContract(typeof(Exception), Action=Constants.FaultAction)]
The client side test code looks like this:
private void button1_Click(object sender, EventArgs e)
{
HistorianAccessServiceClient cli = new HistorianAccessServiceClient();
Tables.Batch bt = new Tables.Batch();
try
{
bt = cli.GetBatch(3241);
}
catch (FaultException<Exception> ex)
{
MessageBox.Show(ex.Message);
}
}
I noticed that if the error parameter to the ProvideFault method contains an inner exception then a System.ServiceModel.CommunicationException is thrown on the client side (!?), the inner exception is System.Net.WebException, the inner exception to that exception is System.IO.IOException and the inner exceptin to that exception is System.Net.Sockets.SocketException (Error Code 10054)?!?!
(Unfortunately I have a swedish operating system installed which means that the messages from the debugger is in swedish.)
The exception message (google translate) looks like this:
An error occurred while receiving the HTTP response to http://localhost:7070/Historian.WebAccess/HistorianAccessService. It may be that the service endpoint binding not using the http protocol. It may also be due to a context for the http request has been interrupted by the server (probably because the service is terminated). You can find more information in server logs.
If I throw an exception without an inner exception, the exception is handled by the client perfectly ok!?!?!
My configuration files looks like this (Service):
<system.serviceModel>
<bindings />
<client />
<services>
<service name="Historian.WebAccess.HistorianAccessService">
<host>
<baseAddresses>
<!--<add baseAddress="http://localhost:8732/Design_Time_Addresses/Historian.WebAccess/HistorianAccessService/"/>-->
<add baseAddress="http://localhost:7070/Historian.WebAccess/"/>
</baseAddresses>
</host>
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address supplied above -->
<!--<endpoint address="HistorianAccessService" binding="wsHttpBinding" contract="Historian.WebAccess.IHistorianAccessService">-->
<endpoint address="HistorianAccessService" binding="wsHttpBinding" contract="Historian.WebAccess.IHistorianAccessService">
<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
-->
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<!-- Metadata Endpoints -->
<!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. -->
<!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information,
set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="false"/>
<serviceThrottling maxConcurrentCalls="16" maxConcurrentInstances="2147483646" maxConcurrentSessions="10"/>
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
Configuration file (Client):
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IHistorianAccessService" closeTimeout="00:10:00"
openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="104857600" maxReceivedMessageSize="104857600"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="104857600" maxStringContentLength="104857600" maxArrayLength="104857600"
maxBytesPerRead="104857600" maxNameTableCharCount="104857600" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:7070/Historian.WebAccess/HistorianAccessService"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IHistorianAccessService"
contract="HistorianAccessHost.IHistorianAccessService"
name="WSHttpBinding_IHistorianAccessService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior>
<dataContractSerializer maxItemsInObjectGraph="2147483646"/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
Does anyone out there recognize this phenomenon and the solution to it?!
I'd be greatful for all the help I can get!
The solution is to not attempt to pass .NET Exception objects back to the client. This limits you to clients running .NET.
In fact, it limits you to running clients which know about all of the exceptions that you might throw. What if you add a new MyNewException on the server, and throw it back to the client? The client will need to have the assembly containing that exception in order for it to be deserialized at all.
I think you're being too fancy for what you're trying to do. If you're just trying to throw FaultException, just do new FaultException(error). You have to do a bit more work if you're throwing a custom fault type, but none of that message stuff is necessary. Here's a VB example I found:
Public Function DoSomething() As Data()
Try
DoSomething()
Catch ex As Exception
Throw New FaultException(ex.Message)
End Try
End Function
If you're throwing a custom type of fault (like say PermissionDenied or such), you need to create an object for that, which is a bit more work.
You also want to be careful what you're returning here. Sending back a lot of details like stack traces to the client can help an attacker trying to break into the system, and isn't a lot of use to your standard end user. You should log that on the server instead.