How to stop self-hosted WCF service upon unhandled errors? - wcf

I have a self-hosted WCF service that is running as a Windows Service. For the tests, I'm running it as a console application.
I'd like to catch all unhandled exceptions that happen in the service and shutdown the host. I'd like to catch all non FaultExceptions that happen when producing a response, but also all exceptions that are thrown in "idle mode" - i.e. thrown from some worker threads.
Unfortunatelly, I can't handle the exceptions neither by IErrorHandler (not called), nor by AppDomain.CurrentDomain.UnhandledException (not raised).
Any clues?
App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="TestServiceBahavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="TestServiceBahavior" name="WcfErrorHandling.TestService">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8001/TestService" />
</baseAddresses>
</host>
<endpoint address="" binding="basicHttpBinding"
bindingConfiguration="" contract="WcfErrorHandling.ITestService" />
<endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
</configuration>
The code:
using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
namespace WcfErrorHandling
{
[ServiceContract]
interface ITestService
{
[OperationContract]
string SayHello(string param);
}
public class TestService : ITestService
{
public string SayHello(string param)
{
if (param == "ae")
throw new ArgumentException("argument exception");
if (param == "fe")
throw new FaultException("fault exception");
return "hello";
}
}
public class TestHost : ServiceHost, IErrorHandler
{
public TestHost()
: base(typeof(TestService))
{
}
public void Start()
{
AppDomain.CurrentDomain.UnhandledException +=
(sender, ea) => UnhandledExceptionHandler(ea.ExceptionObject as Exception);
Open();
foreach (var channelDispatcher in ChannelDispatchers.OfType<ChannelDispatcher>())
channelDispatcher.ErrorHandlers.Add(this);
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// do nothing
}
public bool HandleError(Exception error)
{
if (!(error is FaultException))
UnhandledExceptionHandler(error);
return true;
}
private void UnhandledExceptionHandler(Exception ex)
{
Close();
Environment.Exit(1);
}
}
public class Program
{
public static void Main(string[] args)
{
var testHost = new TestHost();
testHost.Start();
Console.Out.WriteLine("Press any key to exit...");
Console.ReadKey();
testHost.Close();
}
}
}

Had the same problem, this solved it for me:
Make your service implement this interface:
public class MyService : System.ServiceModel.Description.IServiceBehavior
Implement it like this:
public void AddBindingParameters(System.ServiceModel.Description.ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<System.ServiceModel.Description.ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyDispatchBehavior(System.ServiceModel.Description.ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channel in serviceHostBase.ChannelDispatchers) { channel.ErrorHandlers.Add(new ErrorHandler()); }
}
public void Validate(System.ServiceModel.Description.ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
return;
}
And add this class:
public class ErrorHandler : IErrorHandler
{
bool IErrorHandler.HandleError(Exception error)
{ return true; }
void IErrorHandler.ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
{ return; }
}
Now set a breakpoint on HandleError.. it will show you the exception

Related

CORS on Self Hosted WCF Service

I'm trying to implement CORS suppor into my WCF service.
I got some codes from
https://enable-cors.org/server_wcf.html
public class CustomHeaderMessageInspector : IDispatchMessageInspector
{
Dictionary<string, string> requiredHeaders;
public CustomHeaderMessageInspector (Dictionary<string, string> headers)
{
requiredHeaders = headers ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
}
}
But I'm getting error message on this line
public class CustomHeaderMessageInspector : IDispatchMessageInspector
ERROR: Classes can inherit only from other classes
How can i inherit IDispatchMessageInspector
Thanks
I have made a demo, wish it is useful to you.
Reference.
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
Server.
class Program
{
static void Main(string[] args)
{
using (ServiceHost sh = new ServiceHost(typeof(MyService)))
{
sh.Open();
Console.WriteLine("service is ready...");
Console.ReadLine();
sh.Close();
}
}
}
[ServiceContract(Namespace = "mydomain")]
public interface IService
{
[OperationContract]
[WebGet(ResponseFormat =WebMessageFormat.Json)]
string SayHello();
}
public class MyService : IService
{
public string SayHello()
{
return $"Hello, busy World,{DateTime.Now.ToShortTimeString()}";
}
}
public class CustomHeaderMessageInspector : IDispatchMessageInspector
{
Dictionary<string, string> requiredHeaders;
public CustomHeaderMessageInspector(Dictionary<string, string> headers)
{
requiredHeaders = headers ?? new Dictionary<string, string>();
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
string displayText = $"Server has received the following message:\n{request}\n";
Console.WriteLine(displayText);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in requiredHeaders)
{
httpHeader.Headers.Add(item.Key, item.Value);
}
string displayText = $"Server has replied the following message:\n{reply}\n";
Console.WriteLine(displayText);
}
}
public class CustomContractBehaviorAttribute : BehaviorExtensionElement, IEndpointBehavior
{
public override Type BehaviorType => typeof(CustomContractBehaviorAttribute);
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
var requiredHeaders = new Dictionary<string, string>();
requiredHeaders.Add("Access-Control-Allow-Origin", "*");
requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
requiredHeaders.Add("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomHeaderMessageInspector(requiredHeaders));
}
public void Validate(ServiceEndpoint endpoint)
{
}
protected override object CreateBehavior()
{
return new CustomContractBehaviorAttribute();
}
}
App.config
<system.serviceModel>
<services>
<service name="Server3.MyService" behaviorConfiguration="mybahavior">
<endpoint address="" binding="webHttpBinding" contract="Server3.IService" behaviorConfiguration="rest"></endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost:5638"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="mybahavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="rest">
<webHttp />
<CorsBehavior />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="CorsBehavior" type="Server3.CustomContractBehaviorAttribute, Server3" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
Client.
<script>
$(function(){
$.ajax({
method:"Get",
url: "http://10.157.13.69:5638/sayhello",
dataType:"json",
success: function(data){
$("#main").html(data);
}
})
})
</script>
Result
Feel free to let me know if there is anything I can help with.
You should add
Dim method = httpRequest.Method
If method.ToLower() = "options" Then httpResponse.StatusCode = System.Net.HttpStatusCode.NoContent
to your example at the end of BeforeSendReply, otherwise at least POST requests won't work.

WCF custom service host factory not being called

I've implemented a WCF Service Library (.Net 4.5) which works fine.
Then I tried to implement Unity DI using a custom Servcie Host Factory. Debugging application I noticed that CreateServiceHost method in my factory not being called so that my service constructor which uses a dependency as parameter not being called and I get this error:
"System.InvalidOperationException: The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host."
my custom Service Host Factory:
namespace Amir.CodingChallenge.WcfService.Unity
{
public class UnityServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
UnityServiceHost serviceHost = new UnityServiceHost(serviceType,baseAddresses);
// I register types in RegisterComponents method by coding.
serviceHost.Container = UnityConfig.RegisterComponents();
return serviceHost;
}
}
}
my service:
namespace Amir.CodingChallenge.WcfService
{
public class MovieService : IMovieService
{
IShowService showService;
public MovieService(IShowService showService)
{
this.showService = showService;
}
...
}
}
and App.Config:
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true">
<serviceActivations>
<add service="Amir.CodingChallenge.WcfService.MovieService"
relativeAddress="./MovieService.svc"
factory="Amir.CodingChallenge.WcfService.Unity.UnityServiceHostFactory"/>
</serviceActivations>
</serviceHostingEnvironment>
<services>
<service name="Amir.CodingChallenge.WcfService.MovieService">
<endpoint address="" binding="wsHttpBinding" contract="Amir.CodingChallenge.WcfService.IMovieService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:49875/Amir.CodingChallenge.WcfService/MovieService/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information,
set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
<!-- 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>
</system.serviceModel>
"Amir.CodingChallenge.WcfService" is my app Assembly name.
Am I doing something wrong in config? Any help would be appreciated.
And the rest of my codes to give further info in below.
UnityConfig class:
namespace Amir.CodingChallenge.WcfService.Unity
{
public static class UnityConfig
{
static UnityContainer container;
public static UnityContainer RegisterComponents()
{
if (container == null)
container = new UnityContainer();
RegisterTypes();
return container;
}
private static void RegisterTypes()
{
container.RegisterType<IShowService, ShowService>(new HierarchicalLifetimeManager());
}
}
}
UnityServiceHost class:
namespace Amir.CodingChallenge.WcfService.Unity
{
public class UnityServiceHost : ServiceHost
{
public UnityContainer Container { set; get; }
public UnityServiceHost()
: base()
{
Container = new UnityContainer();
}
public UnityServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
Container = new UnityContainer();
}
protected override void OnOpening()
{
if (this.Description.Behaviors.Find<UnityServiceBehavior>() == null)
this.Description.Behaviors.Add(new UnityServiceBehavior(Container));
base.OnOpening();
}
}
}
UnityServiceBehavior class:
namespace Amir.CodingChallenge.WcfService.Unity
{
public class UnityServiceBehavior : IServiceBehavior
{
public UnityInstanceProvider InstanceProvider { get; set; }
private ServiceHost serviceHost = null;
public UnityServiceBehavior()
{
InstanceProvider = new UnityInstanceProvider();
}
public UnityServiceBehavior(UnityContainer unity)
{
InstanceProvider = new UnityInstanceProvider();
InstanceProvider.Container = unity;
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
if (cd != null)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
InstanceProvider.ServiceType = serviceDescription.ServiceType;
ed.DispatchRuntime.InstanceProvider = InstanceProvider;
}
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
public void AddBindingParameters(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
}
}
}
UnityInstanceProvider class:
namespace Amir.CodingChallenge.WcfService.Unity
{
public class UnityInstanceProvider : IInstanceProvider
{
public UnityContainer Container { set; get; }
public Type ServiceType { set; get; }
public UnityInstanceProvider()
: this(null)
{
}
public UnityInstanceProvider(Type type)
{
ServiceType = type;
Container = new UnityContainer();
}
#region IInstanceProvider Members
public object GetInstance(InstanceContext instanceContext, Message message)
{
return Container.Resolve(ServiceType);
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
#endregion
}
}
Make sure that you are linking to the UnityServiceHostFactory in your Service Markup
<%# ServiceHost
Language="C#" Debug="true"
Service="WcfServiceApplication.Implementation.ProductService"
Factory="WcfServiceApplication.DependencyInjection.WCF.UnityServiceHostFactory"
%>
Finding the Markup code was the issue for me. Just right click on the Svc and click "View Markup"
Then you can add in the UnityServiceHostFactory as the Factory value
Link to Source of Answer
Ensure that the build is copying the project dll to the correct location.
In my case, I was copying for 'bin\debug' and the correct would be 'bin\'.

Unable to Catch the FaultException thrown by WCF Service in SilverLight client Application

I am Calling a WCF Service Method from my Silverlight Application. Wcf Service is returning a Fault Exception on Failure. I am able to throw the fault exception from my WCF Service. But it is not receiving to my Silverlight Application(.xaml.cs). Instead I am getting an exception "Communication Exception was unhandled by User, the remote server returned an Error:NotFound"
in References.cs file(AutoGenerated File)
I am calling the WCF Service Method in my .Xaml.cs file like below
private void btnExecuteQuery_Click(object sender, RoutedEventArgs e)
{
try
{
objService.GetDataTableDataAsync(_DATABASENAME, strQuery);
objService.GetDataTableDataCompleted += new EventHandler<GetDataTableDataCompletedEventArgs>(objService_GetDataTableDataCompleted);
}
catch (FaultException<MyFaultException> ex)
{
lblErrorMessage.Content = "Please Enter a Valid SQL Query";
}
}
And I wrote my GetDataTableDataCompleted event as below
void objService_GetDataTableDataCompleted(object sender, GetDataTableDataCompletedEventArgse)
{
//code
}
Here is my Service Method
public IEnumerable<Dictionary<string, object>> GetDataTableData(string dataBaseName, string query)
{
try
{
IEnumerable<Dictionary<string, object>> objDictionary;
objDictionary = objOptimityDAL.GetDataForSelectQuery(dataBaseName, query);
return objDictionary;
}
catch (Exception ex)
{
MyFaultException fault = new MyFaultException();
fault.Reason = ex.Message.ToString();
throw new FaultException<MyFaultException>(fault, new FaultReason("Incorrect SQL Query"));
}
}
Here My WCf Service is interacting with Data Access Layer and throwing the Fault Exception Successfully but it is not receiving to my client Method, Instead I am getting an unhandled Exception like
"Communication Exception was unhandled by User, the remote server returned an Error:NotFound" in References.cs code shown below
public System.Collections.ObjectModel.ObservableCollection<System.Collections.Generic.Dictionary<string, object>> EndGetDataTableData(System.IAsyncResult result) {
object[] _args = new object[0];
System.Collections.ObjectModel.ObservableCollection<System.Collections.Generic.Dictionary<string, object>> _result = ((System.Collections.ObjectModel.ObservableCollection<System.Collections.Generic.Dictionary<string, object>>)(base.EndInvoke("GetDataTableData", _args, result)));
return _result;
}
Here is the Web.config of Wcf Service
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Below is my ServiceReferences.ClientConfig file
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1"
maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647"
closeTimeout="01:00:00"
receiveTimeout="01:00:00"
sendTimeout="01:00:00">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:3132/Service1.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService1" contract="ServiceReference1.IService1"
name="BasicHttpBinding_IService1" />
</client>
</system.serviceModel>
</configuration>
Please Suggest me someway to catch the faultException in my SilverlightClient
Thanks in Advance
You should have chosen Silverlight Enabled WCF Service when you created your service first time. It would have created all the infrastructure for you.
But you can still add the necessary code manually to the WCF Service project.
SilverlightFaultBehavior.cs
/// <summary>
/// The behavior which enables FaultExceptions for Silverlight clients
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class SilverlightFaultBehaviorAttribute : Attribute, IServiceBehavior
{
private class SilverlightFaultEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new SilverlightFaultMessageInspector());
}
public void Validate(ServiceEndpoint endpoint)
{
}
private class SilverlightFaultMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
if ((reply != null) && reply.IsFault)
{
HttpResponseMessageProperty property = new HttpResponseMessageProperty();
property.StatusCode = HttpStatusCode.OK;
reply.Properties[HttpResponseMessageProperty.Name] = property;
}
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
{
endpoint.Behaviors.Add(new SilverlightFaultEndpointBehavior());
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
Service1.cs
[SilverlightFaultBehaviorAttribute]
public class Service1 : IService1
{
...
}
And on the client you should check the e.Error property inside the callback function. Try/catch from your example will not work.
The Silverlight Client
objService.GetDataTableDataCompleted += (s, e) =>
{
if(e.Error != null) {
if (e.Error is FaultException) {
lblErrorMessage.Content = "Please Enter a Valid SQL Query";
}
// do something with other errors
}
else {
// success
}
};
objService.GetDataTableDataAsync(_DATABASEName, strQuery);

How can I change the WCF Router Backup List Behavior with Custom Filter?

I'm trying to find a way to handle exception in WCF router, it means when for any reason router can not send message to primary endpoint (here, there is EndpointNotFoundException, ServerTooBusyException, or CommunicationObjectFaultedException) and goes and select an endpoint from backup list and send this message to backup endpoint. how can I get this internal exception in router service? because at this time I want to change the router configuration dynamically in memory and change the backup endpoint with primary endpoint. is it possible to do this with IErrorHandler? or is it possible to do this Custom Filter ? How I can change the backup behavior with Custom Filter?
this is a full working example to implement IErrorHandler fo wcf service. we can implement this for WCF Router and get the internal exception in router level and then make decision how we can change the configuration in the runtime.
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContract(typeof(MyFault))]
string GetData(int value);
}
[DataContract]
public class MyFault
{
}
public class Service1 : IService1
{
public string GetData(int value)
{
throw new Exception("error");
}
}
public class MyErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message msg)
{
var vfc = new MyFault();
var fe = new FaultException<MyFault>(vfc);
var fault = fe.CreateMessageFault();
msg = Message.CreateMessage(version, fault, "http://ns");
}
}
public class ErrorHandlerExtension : BehaviorExtensionElement, IServiceBehavior
{
public override Type BehaviorType
{
get { return GetType(); }
}
protected override object CreateBehavior()
{
return this;
}
private IErrorHandler GetInstance()
{
return new MyErrorHandler();
}
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandlerInstance = GetInstance();
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(errorHandlerInstance);
}
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
{
if (endpoint.Contract.Name.Equals("IMetadataExchange") && endpoint.Contract.Namespace.Equals("http://schemas.microsoft.com/2006/04/mex"))
continue;
foreach (OperationDescription description in endpoint.Contract.Operations)
{
if (description.Faults.Count == 0)
{
throw new InvalidOperationException("FaultContractAttribute not found on this method");
}
}
}
}
}
web.config:
<system.serviceModel>
<services>
<service name="ToDD.Service1">
<endpoint address="" binding="basicHttpBinding" contract="ToDD.IService1" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<errorHandler />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="errorHandler" type="ToDD.ErrorHandlerExtension, ToDD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
so in HandleError method we can handle this exception in proper way.

IErrorHandler doesn't seem to be handling my errors in WCF .. any ideas?

Have been reading around on IErrorHandler and want to go the config route.
so, I have read the following in an attempt to implement it.
MSDN
Keyvan Nayyeri blog about the type defintion
Rory Primrose Blog
This is basically just the msdn example wrapped in a class that inherits IErrorHandler and IServiceBehaviour ... then this is wrapped in the Extension element that inherits from BehaviourExtensionElement to allegedly allow me to add the element into the web.config. What have i missed?
I have got it to compile and from the various errors i have fixed it seems like WCF is actually loading the error handler. My problem is that the exception that i am throwing to handle in the error handler doesn;t get the exception passed to it.
My service implementation simply calls a method on another class that throws ArgumentOutOfRangeException - however this exception never gets handled by the handler.
My web.config
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basic">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<extensions>
<behaviorExtensions>
<add name="customHttpBehavior"
type="ErrorHandlerTest.ErrorHandlerElement, ErrorHandlerTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior name="exceptionHandlerBehaviour">
<serviceMetadata httpGetEnabled="true"/>
<!-- 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="true"/>
<customHttpBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="exceptionHandlerBehaviour" name="ErrorHandlerTest.Service1">
<endpoint binding="basicHttpBinding" bindingConfiguration="basic" contract="ErrorHandlerTest.IService1" />
</service>
</services>
Service Contract
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContract(typeof(GeneralInternalFault))]
string GetData(int value);
}
The ErrorHandler class
public class ErrorHandler : IErrorHandler , IServiceBehavior
{
public bool HandleError(Exception error)
{
Console.WriteLine("caught exception {0}:",error.Message );
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (fault!=null )
{
if (error is ArgumentOutOfRangeException )
{
var fe = new FaultException<GeneralInternalFault>(new GeneralInternalFault("general internal fault."));
MessageFault mf = fe.CreateMessageFault();
fault = Message.CreateMessage(version, mf, fe.Action);
}
else
{
var fe = new FaultException<GeneralInternalFault>(new GeneralInternalFault(" the other general internal fault."));
MessageFault mf = fe.CreateMessageFault();
fault = Message.CreateMessage(version, mf, fe.Action);
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandler = new ErrorHandler();
foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
if (channelDispatcher != null)
{
channelDispatcher.ErrorHandlers.Add(errorHandler);
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
And the Behaviour Extension Element
public class ErrorHandlerElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new ErrorHandler();
}
public override Type BehaviorType
{
get { return typeof(ErrorHandler); }
}
}
Here's a full working example:
[ServiceContract]
public interface IService1
{
[OperationContract]
[FaultContract(typeof(MyFault))]
string GetData(int value);
}
[DataContract]
public class MyFault
{
}
public class Service1 : IService1
{
public string GetData(int value)
{
throw new Exception("error");
}
}
public class MyErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message msg)
{
var vfc = new MyFault();
var fe = new FaultException<MyFault>(vfc);
var fault = fe.CreateMessageFault();
msg = Message.CreateMessage(version, fault, "http://ns");
}
}
public class ErrorHandlerExtension : BehaviorExtensionElement, IServiceBehavior
{
public override Type BehaviorType
{
get { return GetType(); }
}
protected override object CreateBehavior()
{
return this;
}
private IErrorHandler GetInstance()
{
return new MyErrorHandler();
}
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandlerInstance = GetInstance();
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(errorHandlerInstance);
}
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint endpoint in serviceDescription.Endpoints)
{
if (endpoint.Contract.Name.Equals("IMetadataExchange") &&
endpoint.Contract.Namespace.Equals("http://schemas.microsoft.com/2006/04/mex"))
continue;
foreach (OperationDescription description in endpoint.Contract.Operations)
{
if (description.Faults.Count == 0)
{
throw new InvalidOperationException("FaultContractAttribute not found on this method");
}
}
}
}
}
and web.config:
<system.serviceModel>
<services>
<service name="ToDD.Service1">
<endpoint address=""
binding="basicHttpBinding"
contract="ToDD.IService1" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<errorHandler />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="errorHandler"
type="ToDD.ErrorHandlerExtension, ToDD, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
You can see if the web.config is working and loading by adding a print or a breakpoint to the ApplyDispatchBehavior, and see if that gets printed/hit when the service first opens. So is it being loaded?
I'd add a print/breakpoint at ProvideFault, as well.