I've been trying to find a way to override the default WVF REST 4.0 helppage with my own one, and came across this post:
http://code.msdn.microsoft.com/WCF-Custom-Help-Page-6f5a90f0
I've been trying to use the same approach in an IIS hosted service using the following code:
namespace WcfHelpRestService
{
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private void RegisterRoutes()
{
// Edit the base address of Service1 by replacing the "Service1" string below
var factory = new MyServiceHostFactory();
RouteTable.Routes.Add(new ServiceRoute("Service1", factory, typeof(Service1)));
}
}
}
namespace WcfHelpRestService
{
public class MyServiceHostFactory : WebServiceHostFactory
{
public MyServiceHostFactory ()
{
}
protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new MyServiceHost(serviceType, baseAddresses);
}
}
}
namespace WcfHelpRestService
{
public class MyServiceHost : WebServiceHost
{
public MyServiceHost(Type serviceType, Uri[] baseAddresses): base(serviceType, baseAddresses)
{
}
public override void AddServiceEndpoint(System.ServiceModel.Description.ServiceEndpoint endpoint)
{
endpoint.Behaviors.Add(new HelpPageEndpointBehavior("ACME LLC"));
base.AddServiceEndpoint(endpoint);
}
}
}
however I keep getting the error:
[AddressAlreadyInUseException: HTTP
could not register URL
http://+:51443/Service1/help/ because
TCP port 51443 is being used by
another application.]
System.ServiceModel.Channels.SharedHttpTransportManager.OnOpen()
+1106
not quite sure what I'm doing wrong, so any help would be greatly appreciated.
TIA
Søren
Looks like this address is exclusively locked by some other application.
This could be in case :
when you are using same address for some other application.
when you have started same app twice with same address
when IIS 5.1 is used that is locking exclusively port in your case 51443.
From above, look like third one.
Related
I am implementing IErrorHandler in order to centralize all of the error handling for my WCF service in one place. This works fairly well:
public class ServiceErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
// ..Log..
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// ..Provide fault..
}
}
Now, we're using Ninject to inject dependencies in the rest of the service, and I'd like to do the same here. Since WCF is constructing the objects based on my configuration, and I don't think I have any hooks into this process, I need to use property injection:
[Inject]
public ILoggingService Logger { get; set; }
However, this never seems to get injected. I tried using Ninject's MVC extensions to set ServiceErrorHandler to allow injection like a filter, but that didn't seem to do the trick. Is there a way to make this happen?
Late answer, but you can inject dependencies into IErrorHandler by creating your custom ServiceHost, let's say TestServiceHost.
In your TestServiceHost you need to do:
Implement constructor with IErrorHandler parameter.
Inside, create a private nested class named ErrorHandlerBehaviour*, which needs to implement both IServiceBehavior and IErrorHandler. It also must have constructor with IErrorHandler parameter.
Override OnStarting() method, where you will add ErrorHandlerBehaviour to service behaviours. All behaviours must be added before base.OnStarting().
*the idea came from Juval Lowy's example in book - "Programming WCF Services". More information about Faults and Error extensions you can find there.
Here is the working host console application. I don't use IoC there, just Pure DI, but you can easily resolve logger with any IoC you want:
using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace ConsoleHost
{
class Program
{
static void Main(string[] args)
{
var logger = new DummyLogger();
var errorHandler = new TestErrorHandler(logger);
ServiceHost host = new TestServiceHost(errorHandler, typeof(TestService), new Uri("net.tcp://localhost:8002"));
host.Open();
Console.WriteLine("Press enter to exit");
Console.ReadKey();
}
}
[ServiceContract]
public interface ITestService
{
[OperationContract]
string Test(int input);
}
public class TestService : ITestService
{
public string Test(int input)
{
throw new Exception("Test exception!");
}
}
public class TestErrorHandler : IErrorHandler
{
private ILogger Logger { get; }
public TestErrorHandler(ILogger logger)
{
Logger = logger;
}
public bool HandleError(Exception error)
{
Logger.Log(error.Message);
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
FaultException fe = new FaultException();
MessageFault message = fe.CreateMessageFault();
fault = Message.CreateMessage(version, message, null);
}
}
public class TestServiceHost : ServiceHost
{
private readonly IErrorHandler errorHandler;
public TestServiceHost(IErrorHandler errorHandler, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
this.errorHandler = errorHandler;
}
protected override void OnOpening()
{
Description.Behaviors.Add(new ErrorHandlerBehaviour(errorHandler));
base.OnOpening();
}
class ErrorHandlerBehaviour : IServiceBehavior, IErrorHandler
{
private readonly IErrorHandler errorHandler;
public ErrorHandlerBehaviour(IErrorHandler errorHandler)
{
this.errorHandler = errorHandler;
}
bool IErrorHandler.HandleError(Exception error)
{
return errorHandler.HandleError(error);
}
void IErrorHandler.ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
errorHandler.ProvideFault(error, version, ref fault);
}
void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
channelDispatcher.ErrorHandlers.Add(this);
}
}
void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
}
// Dummy logger
public interface ILogger
{
void Log(string input);
}
public class DummyLogger : ILogger
{
public void Log(string input) => Console.WriteLine(input);
}
}
And configuration:
<system.serviceModel>
<services>
<service name="ConsoleHost.TestService">
<endpoint address="net.tcp://localhost:8002/TestService"
binding="netTcpBinding"
contract="ConsoleHost.ITestService" />
</service>
</services>
</system.serviceModel>
Btw. Make sure you added System.Runtime.Serialization to your references
I need to host a webservice on IIS. I did this before where I will have an .svc(WCF) file or amsx file. But never did it with just a dll. How should I set it up?
Create a class like so using asp.net compatability mode...
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class MyService
{
//Some methods
}
Register the class in your global.asax file as a web service
public class Global : System.Web.HttpApplication
{
public void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private static void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("myServiceUrl", new WebServiceHostFactory(), typeof(MyService)));
}
}
if your app was running on port 8080 you could then hit the service at
http://localhost:8080/myServiceUrl
I have created a custom ServiceHostFactory, a ServiceHost, a ServiceBehavior and a ServiceInstanceProvider for DI using unity in my IIS hosted WCF service.
After this UDP discovery has stopped working. How is the ServiceDiscovery behavior created by WCF? Where is the UDPDiscoveryEndpoint created? Am I overriding some behavior in the code below?
public class ServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
{
protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new ServiceHost(serviceType, baseAddresses);
}
}
public class ServiceHost : System.ServiceModel.ServiceHost
{ ....
protected override void OnOpen(TimeSpan timeout)
{
Description.Behaviors.Add(new ServiceBehavior());
base.OnOpen(timeout);
}
}
public class ServiceBehavior : IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
var cd = cdb as ChannelDispatcher;
if (cd != null)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.InstanceProvider = new ServiceInstanceProvider(serviceDescription.ServiceType);
}
}
}
}
}
public class ServiceInstanceProvider : IInstanceProvider
{...
public object GetInstance(InstanceContext instanceContext, Message message)
{
return Container.Instance.Resolve(_serviceType);
}
}
I have the following after doing some research on other questions:
MyServiceHost:
public class MyServiceHost : ServiceHost
{
public MyServiceHost(IUnityContainer container, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
foreach (var cd in this.ImplementedContracts.Values)
{
cd.Behaviors.Add(new DependencyInjectionInstanceProvider(container));
}
}
}
DependencyInjectionInstanceProvider:
public class DependencyInjectionInstanceProvider : IInstanceProvider, IContractBehavior
{
private readonly IUnityContainer container;
public DependencyInjectionInstanceProvider(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException("container");
}
this.container = container;
}
#region IInstanceProvider Members
public object GetInstance(InstanceContext instanceContext, Message message)
{
return this.GetInstance(instanceContext);
}
public object GetInstance(InstanceContext instanceContext)
{
var serviceType = instanceContext.Host.Description.ServiceType;
return this.container.Resolve(serviceType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
this.container.Teardown(instance);
}
#endregion
#region IContractBehavior Members
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.InstanceProvider = this;
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
#endregion
}
MyServiceHostFactory:
public class MyServiceHostFactory : ServiceHostFactory
{
private readonly IUnityContainer container;
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new MyServiceHost(this.container, serviceType, baseAddresses);
}
}
Email Service with an attempted Constructor Injection:
public class EmailValidator : IEmailValidator
{
private IFakeDAL fakeDAL;
public EmailValidator(IFakeDAL fakeDAL)
{
this.fakeDAL = fakeDAL;
}
public bool ValidateAddress(string emailAddress)
{
Console.WriteLine("Validating: {0}", emailAddress);
string pattern = #"^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*#(([0-9a-zA-Z])+([-\w]*[0-9a-zA-Z])*\.)+[a-zA-Z]{2,9})$";
return Regex.IsMatch(emailAddress, pattern);
}
}
My Console Host to start the Service:
static void Main(string[] args)
{
Type serviceType = typeof(EmailValidator);
Uri serviceUri = new Uri("http://localhost:8080/");
MyServiceHostFactory shf = new MyServiceHostFactory();
ServiceHost host = shf.CreateServiceHost(serviceType, serviceUri);
//ServiceHost host = new ServiceHost(serviceType, serviceUri);
host.Open();
My problem resides in the console host logic. The CreateServiceHost call has a syntax error due to the first argument expecting a Constructor string and not a Type. Which I don't understand since it does accept a Type parameter. In addition to that I don't understand where I should be mapping IFakeDAL to a concrete class. Can I do that in an app.config file or should I register that somewhere else?
ServiceHostFactory is for hosting in IIS. In self hosting you should use your derived ServiceHost directly. Here you have whole example including Unity configuration.
I´m using the following classes in my windows service to create WCF services and inject dependencies to it using unity.
UnityInstanceProvider:
internal class UnityInstanceProvider : IInstanceProvider {
private readonly IUnityContainer container;
private readonly Type contractType;
public UnityInstanceProvider(IUnityContainer container, Type contractType) {
this.container = container;
this.contractType = contractType;
}
public object GetInstance(InstanceContext instanceContext) {
return GetInstance(instanceContext, null);
}
public object GetInstance(InstanceContext instanceContext, Message message) {
return container.Resolve(contractType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance) {
container.Teardown(instance);
}
}
UnityServiceBehavior:
public class UnityServiceBehavior : IServiceBehavior {
private readonly IUnityContainer container;
public UnityServiceBehavior(IUnityContainer container) {
this.container = container;
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) {
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers) {
foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) {
if (endpointDispatcher.ContractName != "IMetadataExchange") {
string contractName = endpointDispatcher.ContractName;
ServiceEndpoint serviceEndpoint = serviceDescription.Endpoints.FirstOrDefault(e => e.Contract.Name == contractName);
endpointDispatcher.DispatchRuntime.InstanceProvider = new UnityInstanceProvider(this.container, serviceEndpoint.Contract.ContractType);
}
}
}
}
}
UnityServiceHost:
public class UnityServiceHost : ServiceHost {
private IUnityContainer unityContainer;
public UnityServiceHost(IUnityContainer unityContainer, Type serviceType)
: base(serviceType) {
this.unityContainer = unityContainer;
}
protected override void OnOpening() {
base.OnOpening();
if (this.Description.Behaviors.Find<UnityServiceBehavior>() == null) {
this.Description.Behaviors.Add(new UnityServiceBehavior(this.unityContainer));
}
}
}
With this classes you can do the following (The configuration of services is done in .config):
UnityContainer container = new UnityContainer();
UnityServiceHost serviceHost = new UnityServiceHost(container, typeof("Type of Service to host"));
serviceHost.Open();
The CreateServiceHost method expects an array of Uri instances, so try this instead:
ServiceHost host = shf.CreateServiceHost(serviceType, new[] { serviceUri });
You can map interfaces to types in either XML or code, but I'd recommend code, since XML has too high a maintenance overhead.
The Main method is an excellent Composition Root, but if you want to configure the container at that level, you'll need to pass it from the Main method to MyServiceHostFactory - which is perfectly fine when you host the service in a console application, but will not work if you want to host it in IIS, where MyServiceHostFactory should be the Composition Root, since IIS requires a default constructor.
I'm managing a shared auth cookie when making WCF service calls via this methodology outlined under the header "Centralized cookie management" located here: http://megakemp.com/2009/02/06/managing-shared-cookies-in-wcf/
I've set up a custom IClientMessageInspector, IEndpointBehavior, BehaviorExtensionElement, the works. My endpoint behavior adds a message inspector as follows:
public class MyEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
// yuck.. Wish I had an instance of MyClientMessageInspector
// (which has the auth cookie already) so I could just inject that
// instance here instead of creating a new instance
clientRuntime.MessageInspectors.Add(new MyClientMessageInspector());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
It all works flawlessly, but this solution breaks down when you want to share cookies over multiple clients. Because the ApplyDispatchBehavior() method creates a new instance, any other client wouldn't get that message inspector instance, and thus, the auth ticket.
So then I thought of trying to create a custom constructor where I could inject the instance like so:
MyEndpointBehavior(MyClientMessageInspector msgInspector) { ... }
But, WCF needs parameter-less constructors. Weeding through the internets, WCF has hooks to allow for dependency injection, creating an IInstanceProvider, IServiceBehavior, etc. But I don't think that's what I'm looking for here.
Can anyone help guide me in the right direction?
You need only extend the concept so that you store the cookie outside of the message inspector itself so that all instances of the message inspector share the same storage.
The poor man's way, just to get started, would be to just use a static field instead of an instance field. Obviously if you have multiple threads you'll need to provide concurrency while updating the field. From there you can get even fancier if you extrapolate it out to a cookie container concept and then just make sure you share the same container with all clients. Sharing the container can be done by getting the ChannelParameterCollection for the client channel and adding property to it and then your behavior looks for that property while it's inspecting the mssage and pulling the cookies out of that. That would look a little something like this:
App logic
// Hold onto a static cookie container
public static CookieContainer MyCookieContainer;
// When instantiating the client add the cookie container to the channel parameters
MyClient client = new MyClient();
client.InnerChannel.GetProperty<ChannelParameterCollection>().Add(MyCookieContainer);
Message inspector logic
public void BeforeSendMessage(ref Message, IClientChannel clientChannel)
{
// Find the cookie container for the current channel
CookieContainer cookieContainer = clientChannel.GetProperty<ChannelParameterCollection>().Select(p => p as CookieContainer).Where(cc => cc != null).First();
// ... use the cookie container to set header on outgoing context ...
}
You're correct, IInstanceProvider won't help in your case - it's used for providing service instances only. You don't need a parameterless constructor for your behavior. You need a paramterless constructor for the config element, and this class can use some dependency injection class (see below) to create the appropriate inspector class needed for the behavior.
namespace ConsoleApplication4
{
public class MyEndpointBehavior : IEndpointBehavior
{
IClientMessageInspector inspector;
public MyEndpointBehavior(IClientMessageInspector inspector)
{
this.inspector = inspector;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(this.inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
public class MyEndpointBehaviorElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(MyEndpointBehavior); }
}
protected override object CreateBehavior()
{
return new MyEndpointBehavior(ClientInspectorFactory.GetClientInspector());
}
}
public class MyClientInspector : IClientMessageInspector
{
public MyClientInspector()
{
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
Console.WriteLine("AfterReceiveReply");
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
Console.WriteLine("BeforeSendRequest");
return null;
}
}
public static class ClientInspectorFactory
{
static IClientMessageInspector instance;
public static IClientMessageInspector GetClientInspector()
{
if (instance == null)
{
instance = new MyClientInspector();
}
return instance;
}
}
[ServiceContract]
public interface ITest
{
[OperationContract]
int Add(int x, int y);
}
public class Service : ITest
{
public int Add(int x, int y) { return x + y; }
}
class Program
{
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(Service));
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest> factory = new ChannelFactory<ITest>("client1");
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.Add(3, 4));
((IClientChannel)proxy).Close();
factory.Close();
factory = new ChannelFactory<ITest>("client2");
proxy = factory.CreateChannel();
Console.WriteLine(proxy.Add(5, 8));
((IClientChannel)proxy).Close();
factory.Close();
host.Close();
}
}
}
I liked the answers provided by #carlosfigueira and #drew, but I ultimately came up with a slightly different approach. I opted to configure my IEndpointBehavior PROGRAMMATICALLY, vs via config. Made things much simpler. I changed my endpoint behavior to store my client message inspector as follows:
public class MyEndpointBehavior : IEndpointBehavior
{
private MyClientMessageInspector_myClientMessageInspector;
public MyClientMessageInspector MyClientMessageInspector
{
get
{
if (_myClientMessageInspector == null)
{
_myClientMessageInspector = new MyClientMessageInspector();
}
return _myClientMessageInspector;
}
}
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(MyClientMessageInspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
Then I simply shared this behavior between clients, as follows:
var behavior = new MyEndpointBehavior();
client1.Endpoint.Behaviors.Add(behavior);
client2.Endpoint.Behaviors.Add(behavior);
Now both clients will share the same auth cookie.