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\'.
Related
I have WCF service class 'MusicServiceManager' with the constructor that takes the parameter of IAppAmbientState. I need to pass parameter value to MusicServiceManager constructor at time initializing Service Host. I don't want singleton implementation for IAppAmbientState.
Service Contract
[ServiceContract]
public interface IAlbumService
{
[OperationContract]
string TestMessage();
[OperationContract]
IEnumerable<AlbumData> GetAlbums();
[OperationContract(Name = "GetAlbumByID")]
AlbumData GetAlbum(Guid ID);
[OperationContract(Name = "GetAlbumByName")]
AlbumData GetAlbum(string name);
}
AppAmbientState
public class AppAmbientState : IAppAmbientState
{
public IContainer ServiceContainer { get; }
public AppAmbientState(
IContainer container // autofac DIC container
)
{
ServiceContainer = container;
}
}
MusicServiceManager
public class MusicServicesManager : IAlbumService
{
private readonly IAppAmbientState _appAmbientState;
public MusicServicesManager(IAppAmbientState appAmbientState)
{
this._appAmbientState= appAmbientState;
}
public AlbumData GetAlbum(Guid ID)
{
throw new NotImplementedException();
}
Service Host Configuration Class
public class ServiceHostConfiguration : IServiceHostConfiguration
{
private readonly ServiceHost hostMusicServiceManager;
public ServiceHostConfiguration()
{
var container = ContainerConfiguration.Configure();
AppAmbientState AppAmbientStateInstance = new AppAmbientState(container);
// need help here... MusicServiceManager(AppAmbientStateInstance)????
// ideally I don't want to define my base address in class
this.hostMusicServiceManager = new ServiceHost(typeof(MusicServicesManager));
}
public void InitializeService()
{
try
{
this.hostMusicServiceManager.Open();
Console.WriteLine("App Web Services Started. Press [Enter] To Exit ");
}
catch(Exception exp)
{
Console.WriteLine("App Web Services Could Not Start:: ", exp);
}
}
public void CloseService()
{
this.hostMusicServiceManager.Close();
}
}
Autofac DI Container
public static class ContainerConfiguration
{
public static IContainer Configure()
{
var builder = new ContainerBuilder();
//Application Configuration
builder.RegisterType<Application>().As<IApplication>();
builder.RegisterType<ServiceHostConfiguration>().As<IServiceHostConfiguration>();
builder.RegisterType<AppAmbientState>().As<IAppAmbientState>();
builder.RegisterType<MusicServicesManager>().As<IAlbumService>();
Here is my Demo:
This is the project directory:
Program.cs:
using Autofac;
using Autofac.Integration.Wcf;
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
namespace AutofacTest
{
class Program
{
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Logger>().As<ILogger>();
builder.RegisterType<Service>();
using (IContainer container = builder.Build())
{
ServiceHost host = new ServiceHost(typeof(Service));
host.AddDependencyInjectionBehavior<Service>(container);
host.Open();
Console.WriteLine("The host has been opened.");
Console.ReadLine();
host.Close();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
public interface ILogger
{
}
public class Service : IService
{
private readonly ILogger logger;
public Service(ILogger logger)
{
this.logger = logger;
}
public string Test()
{
return "TEst1";
}
}
public class Logger : ILogger
{
}
}
App.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<system.serviceModel>
<services>
<service name="AutofacTest.Service">
<endpoint address="" binding="basicHttpBinding" contract="AutofacTest.IService" name="TestServiceEndPoint" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" name="TestServiceMexEndPoint" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/TestService" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.0.0" newVersion="5.2.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Feel free to let me know if the problem persists.
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.
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.
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
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.