WCF call causing memory leak - wcf

We are having problems with a WCF services call that is leaking memory. In our research we think we are calling the service correctly and handling errors that come back from it appropriately. We have even confirmed this with MSDN articles and sample WCF code obtained from a MS web site.
Here is a sample of the code that causes the leak. On the SaveAssociation call, the code on the other side throws an exception. This causes the channel to enter the faulted state. The first catch statement catches the exception (SystemException) and calls Abort() on the client which should immediately abort the session and close the client connection. However, watching this process run this code over and over in a loop we just see the memory used by the process climb and climb.
var client = new FrameworkServiceReference.MAServiceClient();
// Get User domain name
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
OperationContextScope a1 = new OperationContextScope(client.InnerChannel);
MessageHeader<string> customHeaderAppID = new MessageHeader<string>("Account Management");
System.ServiceModel.Channels.MessageHeader a2 = customHeaderAppID.GetUntypedHeader("application", "http://www.ma.com");
OperationContext.Current.OutgoingMessageHeaders.Add(a2);
try
{
client.SaveAssociation(association);
client.Close();
}
catch (SystemException se)
{
client.Abort();
}
catch (Exception ex)
{
client.Abort();
}
Here is the error we receiveā€¦
System.ServiceModel.FaultException`1[System.ServiceModel.ExceptionDetail]: Object reference not set to an instance of an object. (Fault Detail is equal to An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is:
System.NullReferenceException: Object reference not set to an instance of an object.
at Multiview.Business.Core.CoreObject.ValidateItem(String Item, String Value)
at Multiview.Business.Core.User.UpdateUser()
at Multiview.Business.Core.User.Save()
at Multiview.Core.ServiceLibrary.MultiviewService.SaveCRMUser(User user, Guid CRMGuid)
at SyncInvokeSaveCRMUser(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)
at System.ServiceMode...).
What can be done to stop this leak?

If memory on the server goes up, check the server log. Probably the object you return has a circular reference. This means that when it is returned, the server will throw a stack overflow when serializing the object.
Try to configure your service with:
IncludeExceptionDetailInFaults=true
And create some logs (server and client side):
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Error, ActivityTracing" propagateActivity="true">
<listeners>
<add name="xml" />
</listeners>
</source>
<source name="CardSpace">
<listeners>
<add name="xml" />
</listeners>
</source>
<source name="System.IO.Log">
<listeners>
<add name="xml" />
</listeners>
</source>
<source name="System.Runtime.Serialization">
<listeners>
<add name="xml" />
</listeners>
</source>
<source name="System.IdentityModel">
<listeners>
<add name="xml" />
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xml" type="System.Diagnostics.XmlWriterTraceListener" initializeData="C:\t\mylog.svclog" />
</sharedListeners>
</system.diagnostics>

If you have a problem with memory leaks you should start with some memory profiler and find which objects are living in your memory. That will lead you to the source of leakage.
You can start with the fact that OperationContextScope is disposable.

Related

WCF Rest ERR_CONNECTION_RESET not large response

The error code is absolutely terrible, ERR_CONNECTION_RESET has a host of causes and the causes that I found on other questions were related to having too small of a MaxRequestLength for large web service calls. The data I was returning was only a couple of kB though, so this couldn't be the issue.
Here is my interface code
[WebGet(RequestFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.WrappedRequest,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "GetReportByID?ReportID={ReportID}")]
[OperationContract]
UsageReport GetReportByID(int ReportID);
This was the implementation
public UsageReport GetReportByID(int ReportID)
{
return new UsageReport(ReportID);
}
And this was the class code for UsageReport
[DataContract]
public class UsageReport
{
[DataMember]
List<UsageItem> RL;
public UsageReport(int reportID)
{
RL = new List<UsageItem>();
using (SqlDataReader dr = DBUtility.ExecuteReader(cmd, "DBString"))
{
while (dr.Read())
{
ItemNumber = dr["ItemID"] as int? ?? 0;
RL.Add(new UsageItem(ItemNumber));
}
dr.Close();
}
}
public class UsageItem
{
int ItemNumber;
public UsageItem(int ItemNumber)
{
this.ItemNumber = ItemNumber;
}
}
The problem was my UsageItem class, I was missing the necessary DataContract and DataMember fields.
[DataContract]
public class UsageItem
{
[DataMember]
int ItemNumber;
public UsageItem(int ItemNumber)
{
this.ItemNumber = ItemNumber;
}
}
I'd like to add a solution related to a case where WCF is used on the server side:
Add diagnostics to web.config (taken from here):
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.diagnostics>
<sources>
<source name="System.Net" switchValue="Verbose">
<listeners>
<add name="SystemNetTrace"/>
</listeners>
</source>
<source name="System.ServiceModel" switchValue="Verbose, ActivityTracing" propagateActivity="true">
<listeners>
<add name="wcftrace" />
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging" switchValue="Verbose, ActivityTracing">
<listeners>
<add name="wcfmessages" />
</listeners>
</source>
<source name="System.Runtime.Serialization" switchValue="Verbose">
<listeners>
<add name="wcfmessages" />
</listeners>
</source>
</sources>
<sharedListeners>
<add name="SystemNetTrace" type="System.Diagnostics.TextWriterTraceListener" traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, Callstack" initializeData="C:\Traces\System_Net.txt" />
<add name="wcftrace" type="System.Diagnostics.XmlWriterTraceListener" traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, Callstack" initializeData="C:\Traces\WCFTrace.svclog" />
<add name="wcfmessages" type="System.Diagnostics.XmlWriterTraceListener" traceOutputOptions="LogicalOperationStack, DateTime, Timestamp, Callstack" initializeData="C:\Traces\WCFMessages.svclog" />
</sharedListeners>
<trace autoflush="true" />
</system.diagnostics>
</configuration>
Reproduce the error, then go to the trace folder (C:\Traces, in this example). There will be 2 svclog files there: WCFMessages.svclog and WCFTrace.svclog.
Open the file named WCFMessages.svclog. A "Microsoft Service Trace Viewer" window will open, showing errors with a red color.
If no errors are displayed, open WCFTrace.svclog, and the errors (in red) would be there.
In my case, it was a System.Runtime.Serialization error because of a lack of DataContract attribute.

WCF trace logs all exceptions except System.Exception itself

I've made a basic WCF Service Library and created one method called GetResult(). I've set up WCF trace logging to log Warnings to a file. This is correctly logging all exceptions thrown except, for some strange reason, System.Exception itself. For instance, this logs fine:
[OperationContract]
public string GetResult()
{
// exception is logged in file:
throw new DivideByZeroException();
// or
// throw new ApplicationException();
// or
// throw new FileNotFoundException();
// or anything else, it seems
}
But this does not log anything:
[OperationContract]
public string GetResult()
{
// exception is not logged!
throw new Exception();
// or
// throw new Exception("my message");
}
In the latter case, the log file doesn't even get created. If it already exists then it doesn't get written to.
What am I doing wrong?
Here is my app.config setup:
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging" switchValue="Warning">
<listeners>
<add name="ServiceModelTraceListener" />
</listeners>
</source>
<source name="System.ServiceModel" switchValue="Warning">
<listeners>
<add name="ServiceModelTraceListener" />
</listeners>
</source>
<source name="System.Runtime.Serialization" switchValue="Warning">
<listeners>
<add name="ServiceModelTraceListener" />
</listeners>
</source>
</sources>
<trace autoflush="true" />
<sharedListeners>
<add initializeData="c:\temp\logs\wcf.svclog"
type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
name="ServiceModelTraceListener"
traceOutputOptions="Timestamp" />
</sharedListeners>
</system.diagnostics>

WCF tracing & message logging - trace level warning

Assume I have a config file which looks like this:
...
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Warning,ActivityTracing" propagateActivity="true">
<listeners>
<add name="ServiceModelTraceListener" />
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="ServiceModelTraceListener" />
</listeners>
</source>
</sources>
<sharedListeners>
<add initializeData="LogServer.svclog" type="System.Diagnostics.XmlWriterTraceListener" name="ServiceModelTraceListener" />
</sharedListeners>
<trace autoflush="true" />
</system.diagnostics>
When using this config file every activity the caller performs against the service and each corresponding message that's sent to the service will be logged in the svclog file. Everything fine so far.
If I modify the 3rd line from the above listing to <source name="System.ServiceModel" switchValue="Warning" propagateActivity="true"> (the ActivityTracing is removed) then only those activities are logged that are at least labeled level warning. But it's still every message logged...
So is there a way to only log those message that correspond to those activities that are at least warnings? Those messages that succeeded aren't very interesting in that moment, but those messages that belong to the unsuccessful activities are!
Edit
To filter messages beyond the options below you may want to look into writing your own TraceSource.
Below is one I am using for a project. You could easily customize it to filter out the messages you want or perhaps hide activity if it is not in DEBUG, etc.
class DB : TraceSource
{
public DB(string name) : base(name)
{
}
public DB(string name, SourceLevels sourceLevels) : base (name, sourceLevels)
{
}
public void Log(object value)
{
WriteLine(value);
}
public void Error(object value)
{
WriteLine(value, TraceEventType.Error);
}
public void Error(RecordingResponseData errorResponse)
{
string errorMessage = "[Error] Code: "+errorResponse.ErrorCode +" Message: "+errorResponse.ErrorMessage;
WriteLine(errorMessage, TraceEventType.Error);
}
public void Warn(object value)
{
WriteLine(value, TraceEventType.Warning);
}
public void WriteLine(object value, TraceEventType type = TraceEventType.Information)
{
TraceEvent(type, 0, value.ToString());
}
}
Original
Your options are:
Critical
Error
Warning
Information
ActivityTracing
Verbose
All
Or a combination there of. If you have it set to Warning but are still getting too many messages then you may want to try Error or Critical.
ref: https://msdn.microsoft.com/en-us/library/ms733025%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
There's a switchValue available for the System.ServiceModel.MessageLogging trace switch as well. Just add that attribe to that source element and set it to Warning also and you will only see messages logged that are related to warnings.
Get rid of System.ServiceModel.MessageLogging source to get rid of logging messages to resolve "still every message logged".

SOAP message is empty when catching MessageLoggingTraceRecords with CustomTraceListener

I want to write CustomTraceListener which writes all data to SQL Server DB.
Here's the stub for it:
public class SqlTraceListener : TraceListener
{
public SqlTraceListener()
: base()
{ }
public SqlTraceListener(String name)
: base(name)
{ }
protected override string[] GetSupportedAttributes()
{
List<string> attributes = new List<string>();
attributes.Add("connectionString");
attributes.Add("actionFilter");
attributes.Add("hostFilter");
return base.GetSupportedAttributes();
}
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
{ }//Other empty methods...
}
In overridden method TraceData I want to catch SOAP messages sent to my WCF service. But when I check what is in "data" parameter I get this: (sorry for posting xml as pictures - it seems SO editor doesn't allow some xml keywords in posts):
But according to standard XmlWriterTraceListener I should get this:
How to configure TraceListener not to eliminate SOAP messages?
My config is here:
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="xml"/>
<add name="sql"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add initializeData="C:\logs\StockPriceService.svclog" type="System.Diagnostics.XmlWriterTraceListener" name="xml"/>
<add type="SqlTraceListener.SqlTraceListener, SqlTraceListener" name="sql"/>
</sharedListeners>
<trace autoflush="true"/>
Is there any reason you're not using the out of the box database trace listener? See: Enterprise Library Database Trace Listener?.

WCF tracing in code does not follow MessageLogging settings

I need to use WCF tracing in my application but it needs to be controlled from code as much as possible.
IT was suggested that I install the following sections in my app.config file:
<configuration>
<system.serviceModel>
<diagnostics>
<messageLogging
maxMessagesToLog="100"
logEntireMessage="true"
logMessagesAtServiceLevel="true"
logMalformedMessages="true"
logMessagesAtTransportLevel="true">
</messageLogging>
</diagnostics>
</system.serviceModel>
<system.diagnostics>
<sources>
<source name="System.ServiceModel" >
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="dummy"/>
</listeners>
</source>
</sources>
</system.diagnostics>
</configuration>
Then the following code could be used to get the trace running as needed:
BindingFlags privateMember = BindingFlags.NonPublic | BindingFlags.Instance;
BindingFlags privateStaticMember = privateMember | BindingFlags.Static;
Type type = Type.GetType("System.ServiceModel.DiagnosticUtility, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
MethodInfo[] mi = type.GetMethods(privateStaticMember);
// invoke InitializeTracing
object diagnosticTrace = mi.FirstOrDefault(e => e.Name == "InitializeTracing").Invoke(null, null);
if (diagnosticTrace != null)
{
// get TraceSource
Type type2 = Type.GetType("System.ServiceModel.Diagnostics.DiagnosticTrace, SMDiagnostics, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
PropertyInfo pi = type2.GetProperty("TraceSource", privateMember);
TraceSource traceSource = pi.GetValue(diagnosticTrace, null) as TraceSource;
// clear all listeners in the trace source
traceSource.Listeners.Clear();
// add listener to trace source
XmlWriterTraceListener listener = new XmlWriterTraceListener("mylogfile".svclog");
listener.TraceOutputOptions = TraceOptions.Timestamp | TraceOptions.Callstack;
traceSource.Attributes["propagateActivity"] = "true";
traceSource.Switch.ShouldTrace(TraceEventType.Verbose | TraceEventType.Start);
traceSource.Listeners.Add(listener);
// enable tracing
type.GetProperty("Level", privateStaticMember).SetValue(null, SourceLevels.All, null);
Trace.AutoFlush = true;
This works fine up to a point, the main problem being that the messagelogging settings in the system.servicemodel section of the app.config file are being ignored.
Is there anything that can be done to solve this problem?
I can't comment on all of your code because I have not used System.Diagnostics in this way before (programmatically configuring the WCF communication tracing), but if your intent on this line:
traceSource.Switch.ShouldTrace(TraceEventType.Verbose | TraceEventType.Start);
Is to set the level of tracing that you want, I think that you should be using the Switch.Level property instead. ShouldTrace is for asking if a given TraceSource would trace, given the input flags.
traceSource.Switch.Level = SourceLevels.Verbose | SourceLevels.ActivityTracing;
Note that according to this link, it is possible to configure apparently reasonable settings and yet the activity id might not be propogated correctly. Read it carefully. It may or not apply to your situation.
You need to enable MessageLogging by defining a trace source as indicated in this MSDN Library page. So, you need this extra bit in your app.config in the sources section:
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="dummy"/>
<remove name="Default" />
</listeners> </source>
The message logging settings don't apply to the System.ServiceModel trace source.