Entlib Custom exception handler missing mappings - wcf

I implemented custom exception handler which works, except mappings from xml configuration policy. Those mapping works with standard Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler
My implementation
[ConfigurationElementType(typeof(CustomHandlerData))]
public class IdentityFaultContractExceptionHandler : IExceptionHandler
{
public IdentityFaultContractExceptionHandler(NameValueCollection attributes)
{
}
public IdentityFaultContractExceptionHandler(Type faultContractType, NameValueCollection attributes)
{
}
public IdentityFaultContractExceptionHandler(Type faultContractType, string exceptionMessage, NameValueCollection attributes)
{
}
public IdentityFaultContractExceptionHandler(IStringResolver exceptionMessageResolver, Type faultContractType, NameValueCollection attributes)
{
}
public Exception HandleException(Exception exception, Guid handlingInstanceId)
{
return new Exception();
}
and part of the configuration
<add name="All Exceptions" type="System.Exception, mscorlib" postHandlingAction="ThrowNewException">
<exceptionHandlers>
<add type="MyClass.IdentityFaultContractExceptionHandler, MyClass" exceptionMessage="An error occurred in the service." faultContractType="MyClass.UnexpectedServerFault, MyClass" name="Fault Contract Exception Handler" >
<mappings>
<add source="{Message}" name="Message" />
</mappings>
</add>
</exceptionHandlers>
</add>
When I remove mappping node service works, when I add, then I got error : unrecognize element mappings.

If you are using a CustomHandlerData attribute then your configuration needs to use XML Attributes which then get passed in as a NameValueCollection to the custom handler constructor. If you want to have custom XML then you will have to use Full Design-time Integration. If you want to go down that road then you should look at the FaultContractExceptionHandlerData source code since your code would probably be quite similar.

Related

WCF Custom trace listener to write logs in msmq without EnterpriseLibrary

How to write custom trace listener to write message logs in msmq?
I have added below custom MSMQTraceListener :
public class MSMQTraceListener : TraceListener
{
string _queueName;
public MSMQTraceListener(string queueName)
: base("MSMQListener")
{
_queueName = queueName;
if (!MessageQueue.Exists(_queueName))
MessageQueue.Create(_queueName);
}
public override void Write(string message)
{
SendMessageToQueue(message);
}
public override void WriteLine(string message)
{
SendMessageToQueue(message);
}
/// <summary>
/// Send message to queue.
/// </summary>
/// <param name="message">string: message</param>
private void SendMessageToQueue(string message)
{
try
{
MessageQueue messageQueue = new MessageQueue(_queueName, QueueAccessMode.Send);
messageQueue.Label = DateTime.Now.ToString();
messageQueue.Send(message);
messageQueue.Close();
}
catch (Exception ex)
{
}
}
}
And updated below diagnostic setting in my web.config file:
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="messages" type="Proj.Common.Diagnostics.MSMQTraceListener,Proj.Common" initializeData=".\private$\PerformanceTesting" />
</listeners>
</source>
</sources>
</system.diagnostics>
If you are in code hosted by MSMQ and want to write a message to say a log file
All .NET applications are the same as far as System.Diagnostics is concerned. Configure the listener in app.config, and use Trace or TraceSource to write to the listener. MSDN explains this better than I can.
If you want a trace listener that sends message to MSMSQ
Get this utility library, Essential Diagnostics, that makes working with System.Diagnostics less painful
Override the one TraceEvent() method on BaseTraceListener. Inside that method, you use the available parameters to send messages to whatever you'd like, for example an MSMQ destination.
Register your custom TraceListener in the usual way.

Is there a way to pass parameters to a custom Service Behavior through configuration

I have multiple WCF services hosted in IIS to which I'm applying the same custom service behavior. I'm looking for a way to pass several parameters to the behavior through configuration, such as in behaviorExtensions or behavior elements.
If it helps, I'm also adding custom message inspector in ApplyDispatchBehavior, so I will need to pass parameters to the inspector:
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(
new ValidatingMessageInspector(<custom parameters>));
}
}
}
Would just creating a configuration section in web.config with the parameters be valid? If so you can just read the config there and apply it, or even just use appSettings if the parameters are basic.
For example, if you have few parameters of basic types, maybe a simple approach could be:
<appSettings>
<add key="Shared.CommonParameter" value="A value" />
<add key="Service1.OneParameter" value="False" />
<add key="Service1.AnotherParameter" value="Some Value" />
<add key="Service2.ADifferentParameter" value="42" />
</appSettings>
That way it would be easy to differentiate what setting belongs to which service (notice the service name prefix in the key), and also have some shared parameters if needed.
If you need something more complex in structure you might want to look into defining custom configuration sections for each service, as is shown here: http://msdn.microsoft.com/en-us/library/2tw134k3%28v=vs.140%29.aspx
But that might be overkill, depending on your needs and expected flexibility.

WCF - IErrorHandler vs Authentication

I'm writing a WCF service with some authentication and a custom error handler. However, I'm coming up against this problem: my implementation of IErrorHandler is not getting hit when the authentication throws an exception, but runs just fine with other exeptions.
Does authentication run before IErrorHandler gets built? Am I barking up the wrong tree trying to get it to catch those errors?
Yes, I have tried (and am) throwing a FaultException in my authentication, not SecurityTokenException.
So first thing is to make sure that your custom Error Handler is also implementing IServiceBehavior. IServiceBehavior requires that you implement a couple other methods but the important one is "ApplyDispatchBehavior", in which you must add the ErrorHandler to the channel dispatchers.
C#
public class CustomErrorHandler: IServiceBehavior, IErrorHandler
{
public bool HandleError(Exception error)
{
//Return True here if you want the service to continue on as if
// the error was handled
return true;
}
public void ProvideFault(Exception error,
MessageVersion version,
ref Message fault)
{
FaultException fe = new FaultException(
new FaultReason(error.Message),
new FaultCode("Service Error"));
MessageFault mf = fe.CreateMessageFault();
fault = Message.CreateMessage(version, mf, fe.Action);
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
IErrorHandler eh = new CustomErrorHandler();
foreach (ChannelDsipatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
cd.ErrorHandlers.Add(eh);
}
}
public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
//Add binding parameters if you want, I am not
}
public void Validate(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
//Add custom fault validation here if you want
}
}
Then you need to add the CustomErrorHandler as a service behavior and add the behavior
web.config
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="ErrorHandler"
type="ServiceNamespace.CustomErrorHandler, ServiceNamespace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior name="MyBehavior1">
<!--Put other behaviors for your service here then add the next line-->
<ErrorHandler />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
This way all your thrown exceptions will be converted to faults to return back to the client.
In the case of SecurityTokenExceptions, you do not want to convert those to Fault Exceptions right away. You actually do want to throw these as SecurityTokenExceptions in the custom validation in order for the service/server to recognize that the security authorization failed, and automatically returns as a fault equiv of a "403 : Access Denied". I am not 100% but I think that the custom auth and validation pieces happen before custom service behaviors, like the error handler, are loaded. Unfortunately, if you need to troubleshoot something in your auth, you will need to turn on WCF tracing on the service, see this article titled "How to turn on WCF Tracing".
If you need to log failed auth attempts, you will probably need to put it directly in your custom validator.

Catches communication exception instead of custom fault exception - WCF

On Server I'm throwing the exception like this.
catch(SqlException exception)
{
if (exception.Message.Contains("Custom error from stored proc"))
{
//Exception to be thrown when authentication fails.
throw new FaultException<MyServiceFault>(new MyServiceFault { MessageText = exception.Message });
}
}
And on client end I'm catching the exception
catch(FaultException<MyServiceFault> faultException)
{
}
Here is my MyServiceFault
[DataContract]
public class MyServiceFault
{
[DataMember]
public string MessageText { get; set; }
[DataMember]
public Guid Id { get; set; }
}
The problem is that on client, it doesn't go to MyServiceFault catch block instead it goes to communication exception catch block and throws this error
System.ServiceModel.CommunicationException: The underlying connection was closed: The connection was closed unexpectedly. ---> System.Net.WebException
I've also decorated my service method [FaultContract(typeof(MyServiceFault))] in the interface which is implemented by my service.
In my web.config servicebehaviour tag consist
<serviceDebug includeExceptionDetailInFaults="true" />
Any idea where I'm going wrong.
This problem is occurring on windows 7. Can there be a reason related to it?
IMPORTANT UPDATE
According to answerers, any unhandled exception on the server can cause throwing of the Communication exception on the client side and it may not have anything to do with the custom fault exception you have thrown on the server. So the solution is log the errors on the server and find what error is causing this behavior. Here is a very useful open source logging functionality I found and implemented which can even be more useful after your project goes into production environment. Many thanks to the answerers.
A better way of logging exceptions in WCF
Add logging to your WCF calls by including this in your app.config.
<system.diagnostics>
<trace autoflush="true" />
<sources>
<source name="System.ServiceModel" switchValue="Information, ActivityTracing">
<listeners>
<add name="sdt" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\LogPath\LogFile.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
(You can do this for both server and client, obviously specifying different log files)
Once you've generated some logs, look through for exceptions or warnings. I often find this produces some very useful information that helps me solve WCF problems.
To read the log file, you'll need to use SvcTraceViewer.exe. Unfortunately the only way to get this is with the windows SDK which is a bit of a big download for one tiny little tool.
It's also worth bearing in mind that WCF can through the CommunctionException when it is being closed, and this is expected behaviour. You shouldn't just do using with client WCF comm channels. Instead, you should follow a pattern like this:
try{
x.Close()
}
catch(Comms ex){
x.Abort()
}
One thing i learned from working with wcf is that the error CommunicationException is thrown alot of times. It is even possible that the error you get has nothing to do with the Exception you throw, but with something else causing it. At the moment something is being sent between a client and server, it's hard to track down what is causing the exception.
I could solve some of my issues by adjusting some settings in the app.config. Setting timeout, etc...
Maybe that can help?
As a follow-up, one reason for getting CommunicationException is that the FaultException could not be serialized correctly. In the log (see Simon's answer on how to set up logging), this would show up as "Handling exception" followed by "Replying to an operation threw an exception".
In my case, this was caused by not initializing an enumeration value:
[DataContract]
public class MyCustomWebServiceFault
{
public MyCustomWebServiceFault()
{
}
[DataMember]
public EMyCustomWebServiceFaultReason Reason { get; set; }
[...]
The log then revealed it:
Enum value '0' is invalid for type 'EMyCustomWebServiceFaultReason' and cannot be serialized. Ensure that the necessary enum values are present and are marked with EnumMemberAttribute attribute if the type has DataContractAttribute attribute.
Long story short, use logging or unit test exception serialization.

WCF: Configuring Known Types

I want to know as to how to configure known types in WCF. For example, I have a Person class and an Employee class. The Employee class is a sublass of the Person class. Both class are marked with a [DataContract] attribute.
I dont want to hardcode the known type of a class, like putting a [ServiceKnownType(typeof(Employee))] at the Person class so that WCF will know that Employee is a subclass of Person.
Now, I added to the host's App.config the following XML configuration:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="Person, WCFWithNoLibrary, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null">
<knownType type="Employee, WCFWithNoLibrary, Version=1.0.0.0,Culture=neutral, PublicKeyToken=null" />
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
<system.serviceModel>
.......
</system.serviceModel>
</configuration>
I compiled it, run the host, added a service reference at the client and added some code and run the client. But an error occured:
The formatter threw an exception while
trying to deserialize the message:
There was an error while trying to
deserialize parameter
http://www.herbertsabanal.net:person.
The InnerException message was 'Error
in line 1 position 247. Element
'http://www.herbertsabanal.net:person'
contains data of the
'http://www.herbertsabanal.net/Data:Employee'
data contract. The deserializer has no
knowledge of any type that maps to
this contract. Add the type
corresponding to 'Employee' to the
list of known types - for example, by
using the KnownTypeAttribute attribute
or by adding it to the list of known
types passed to
DataContractSerializer.'. Please see
InnerException for more details.
Below are the data contracts:
[DataContract(Namespace="http://www.herbertsabanal.net/Data", Name="Person")]
class Person
{
string _name;
int _age;
[DataMember(Name="Name", Order=0)]
public string Name
{
get { return _name; }
set { _name = value; }
}
[DataMember(Name="Age", Order=1)]
public int Age
{
get { return _age; }
set { _age = value; }
}
}
[DataContract(Namespace="http://www.herbertsabanal.net/Data", Name="Employee")]
class Employee : Person
{
string _id;
[DataMember]
public string ID
{
get { return _id; }
set { _id = value; }
}
}
Btw, I didn't use class libraries (WCF class libraries or non-WCF class libraries) for my service. I just plain coded it in the host project.
I guess there must be a problem at the config file (please see config file above). Or I must be missing something. Any help would be pretty much appreciated.
I guess I have found the answer now.
The configuration file I posted above looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="Person, WCFWithNoLibrary, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null">
<knownType type="Employee, WCFWithNoLibrary, Version=1.0.0.0,Culture=neutral, PublicKeyToken=null" />
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
<system.serviceModel>
.......
</system.serviceModel>
</configuration>
What I just added was, the Namespace of the Person class and the Employee class. And no need for the longer Version and Culture values.... The correct configuration should be:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="WCFWithNoLibrary.Person, WCFWithNoLibrary">
<knownType type="WCFWithNoLibrary.Employee, WCFWithNoLibrary" />
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
<system.serviceModel>
.......
</system.serviceModel>
</configuration>
Now it is shorter and makes more sense. But if 3rd party libraries are used, then adding version, culture, publickeytokens would be required.
I know this was answered a long time ago, but, another (maybe more obvious for future programmers) solution:
[KnownType(typeof(SubClass))]
public class BaseClass
Scott
I got this lengthy error message also in another case. I did use the KnownTypeAttribute and had successfully deployed an application which uses WCF.RIA to production. In the second release I added a new subtype, and added the necessary corresponding KnownTypeAttribute (the compiler did not accept it without this attribute - great). What the compiler did accept and what ran on my machine, did not run in production, however. Only in production I got the error mentioned above. Comparing all the uses of the existing subtypes and the new one revealed I had forgotten that WCF.RIA requires the name of the subtype to be used in a name of a method, like GetMySubTypes. So if you get this error after having added the attributes, see whether it's because of WCF.RIAs conventions.