If I have a WCF application hosted on IIS, how can I handle
UnknownMessageReceived events?
I know how to do it when I build a console host.
You can use a service host factory to get access to the service host instance which is used under IIS. You can find more information about service host factories at http://blogs.msdn.com/b/carlosfigueira/archive/2011/06/14/wcf-extensibility-servicehostfactory.aspx, and the code below shows an example of a factory which listens to the UnknownMessageReceived events.
public class MyFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
ServiceHost host = base.CreateServiceHost(serviceType, baseAddresses);
host.UnknownMessageReceived += new EventHandler<UnknownMessageReceivedEventArgs>(host_UnknownMessageReceived);
return host;
}
void host_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e)
{
// do something with the message
}
}
Related
Lets say I have a simple service contract:
[ServiceContract(Namespace = Constants.MyNamespace)]
public interface IAccountService
{
[OperationContract]
Account GetByAccountNumber(string accountNumber);
}
Here is the service:
[ServiceBehavior(Namespace = Constants.MyNamespace)]
public class AccountService : IAccountService
{
private readonly IUnitOfWorkAsync _uow;
private readonly IRepositoryAsync<Account> _repository;
public AccountService(IDataContextAsync dataContext)
{
_uow = new UnitOfWork(dataContext);
_repository = new Repository<Account>(dataContext, _uow);
}
public Account GetByAccountNumber(string accountNumber)
{
return _repository.GetByAccountNumber(accountNumber);
}
}
Here is the CustomServiceHostFactory:
public class CustomServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDbContext>().As<IDataContextAsync>();
builder.Register(c => new AccountService(c.Resolve<IDataContextAsync>())).As<IAccountService>();
using (var container = builder.Build())
{
var host = new CustomServiceHost(serviceType, baseAddresses);
host.AddDependencyInjectionBehavior<IAccountService>(container);
return host;
}
}
}
..where CustomServiceHost creates all of the bindings/behaviors programmatically. I am using file-less activation so my .config file just has section like this:
<serviceHostingEnvironment>
<serviceActivations>
<add service="Company.Project.Business.Services.AccountService"
relativeAddress="Account/AccountService.svc"
factory="Company.Project.WebHost.CustomServiceHostFactory"/>
</serviceActivations>
</serviceHostingEnvironment>
I publish to IIS and can view the site in a browser. It says "you have created a service". However, any call I try to make to the service from my client application gives the following error:
Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.
How do you use Autofac with WCF and a CustomServiceHostFactory?
I am able to use poor man's DI as a workaround for now but was hoping to get this working. I can't seem to find any good examples on the web. Thanks.
Don't dispose of the container. Instead of a using statement, keep the container alive. It needs to live as long as the host.
You'll notice in the default Autofac WCF stuff the container is a global static that lives for the app lifetime - that's why.
I have created WCF service project.
It has following content in SVC file.
<%# ServiceHost Service="Deepak.BusinessServices.Implementation.ApiImplementation"
Factory="Deepak.BusinessServices.Implementation.CustomServiceHostFactory"%>
SVC reference
http://localhost/DeepakGateway/Service.svc
Service is UP and WSDL generated. Now I want to host this service as Windows Service.
How can I do it?
I have created "Windows Service" Project ans have following code.
protected override void OnStart(string[] args)
{
if (m_Host != null)
{
m_Host.Close();
}
Uri httpUrl = new Uri("http://localhost/DeepakGateway/Service.svc");
m_Host = new ServiceHost
(typeof(?????? WHAT TO FILL HERE?), httpUrl);
//Add a service endpoint
m_Host.AddServiceEndpoint
(typeof(?????? WHAT TO FILL HERE?), ), new WSHttpBinding(), "");
//Enable metadata exchange
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
m_Host.Description.Behaviors.Add(smb);
//Start the Service
m_Host.Open();
}
You need to add the type of the class that implements your service contract in the ServiceHost constructor, and type of the service contract in your AddServiceEndpoint
Assuming your service implementation class looks something like this:
namespace Deepak.BusinessServices.Implementation
{
public class ApiImplementation : IApiImplementation
{
....
}
}
then you need:
m_Host = new ServiceHost(typeof(ApiImplementation), httpUrl);
m_Host.AddServiceEndpoint(typeof(IApiImplementation), new WSHttpBinding(), "");
the service host needs to know what (concrete) type of service class to host
the endpoint needs to know what service contract (interface) it exposes
I have a custom OperationBehavior. I would like to apply it for all operations at once.
Unfortunately, OperationBehaviors cannot be configured per entire service or in web.config.
When hosting WCF service in a test application, I can do the following:
foreach (var ep in _serviceHost.Description.Endpoints)
{
foreach (OperationDescription od in ep.Contract.Operations)
{
od.Behaviors.Add(new MyOperationBehavior());
}
}
_serviceHost.Open();
But how do I do it in a IIS hosted web application?
I tried to get OperationContext.Current.Host.Description.Endpoints in Application_Start but of course OperationContext.Current is not available before any operation has started, so my approach fails.
You can use a ServiceHostFactory to do that. With it, you can get access to the OM prior to the service being opened.
This is an example:
public class MyFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
ServiceHost host = base.CreateServiceHost(serviceType, baseAddresses);
foreach (var ep in host.Description.Endpoints)
{
foreach (OperationDescription od in ep.Contract.Operations)
{
od.Behaviors.Add(new MyOperationBehavior());
}
}
return host;
}
}
And you can get more information about service host factories at http://blogs.msdn.com/b/carlosfigueira/archive/2011/06/14/wcf-extensibility-servicehostfactory.aspx
At the end I found an alternative solution: use a contract behavior which injects any other behvaior as needed. Like this:
public class InjectAllOperationsBehavior : Attribute, IContractBehavior
{
private IOperationBehavior _operationBehavior = null;
public InjectAllOperationsBehavior(Type operationBehaviorType)
{
_operationBehavior =
(IOperationBehavior)Activator.CreateInstance(operationBehaviorType);
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
foreach (OperationDescription opDescription in contractDescription.Operations)
{
opDescription.Behaviors.Add(_operationBehavior);
}
}
... other IContractBehavior methods can be left empty. You might want also to use the ApplyClientBehavior method with the same code from the ApplyDispatchBehavior method.
I'm implementing a WCF service hosted by IIS, that impersonates the caller. When I have the service endpoint configuration in the Web.config-file everything works as intended.
I want to set the service endpoint programmatically but I'm missing something because the caller is not impersonated (the endpoint works fine except for that little detail). Is there any way I can capture the service endpoint created from web.config in code so that when debugging I can find what the difference is between this one and the one I create programmatically?
Thanks,
Christian
You can use the default service host factory to access the endpoint from web.config in your code (and possibly attach a debugger to the IIS process to see what it contains).
public class MyServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new MyServiceHost(serviceType, baseAddresses);
}
}
public class MyServiceHost : ServiceHost
{
public MyServiceHost(Type serviceType, Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
protected override void OnOpening()
{
// At this point you have access to the endpoint descriptions
foreach (var endpoint in this.Description.Endpoints)
{
Console.WriteLine("Endpoint at {0}", endpoint.Address.Uri);
Binding binding = endpoint.Binding;
BindingElementCollection elements = binding.CreateBindingElements();
foreach (var element in elements)
{
Console.WriteLine(" {0}", element);
}
}
base.OnOpening();
}
}
And in the .svc file, specify the Factory="YourNamespace.MyServiceHostFactory" attribute.
How to configure a WCF service for a load balancer and specific the end points
You could try writing a custom service host factory which will use the load balancer's url as base address:
public class CustomServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(
Type serviceType, Uri[] baseAddresses)
{
Uri uri = null;
if (baseAddresses.Length < 2)
{
uri = baseAddresses[0];
}
else
{
// TODO: You need to choose the load balancer's url here:
uri = baseAddresses[????];
}
return base.CreateServiceHost(serviceType, new Uri[] { uri });
}
}