We have a difficulty to use Ninject wcf extensions. We host our services in IIS and using svc files. We have read a lot of posts describing usage of NinjectServiceHostFactory(in svc files) with NinjectHttpApplication (in global.asax file) and it seems very simple.
Our problem is that we already have our custom ServiceHostFactory and ServiceHost.
Can someone point us what is the right way combining our ServiceHostFactory with NinjectServiceHostFactory?
Our svc files point to the our custom ServiceHostFactory.
UPDATE
It seems that we managed to implement it.
Our ServiceHostFactory implementation:
public class CustomServiceHostFactory : NinjectServiceHostFactory
{
protected override Type ServiceHostType
{
get
{
return typeof(CustomServiceHost<>);
}
}
}
Our ServiceHost implementation:
public class CustomServiceHost<T> : NinjectAbstractServiceHost<T>
{
public CustomServiceHost(IServiceBehavior serviceBehavior, T serviceType, params Uri[] baseAddresses) : base(serviceBehavior, serviceType, baseAddresses)
{
...
}
protected override void OnOpening()
{
...
base.OnOpening();
}
}
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 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.
I'm wanting to unit test my custom ServiceHostFactory. Unfortunately, I get this InvalidOperationException when calling CreateServiceHost:
'ServiceHostFactory.CreateServiceHost' cannot be invoked within the current hosting environment. This API requires that the calling application be hosted in IIS or WAS.
I can work around this by refactoring my class such that it exposes a public method that can be directly invoked by the unit test instead of using its inherited public interface; I hate to change my interface just for the sake of a unit test. I also see another SO answer that recommends spawning a Cassini host, but I'd hate to complicate my unit tests in this manner.
Is there a way to work around this ServiceHostFactory limitation without resorting to these measures?
I figured out the issue. In my custom ServiceHostFactory, I had only overridden the protected method, CreateServiceHost(Type serviceType, Uri[] baseAddresses). By overriding the public CreateServiceHost(string constructorString, Uri[] baseAddresses) method, I was able to construct the service host factory with no issues.
Before:
public class MyServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
// ...
}
}
After:
public class MyServiceHostFactory : ServiceHostFactory
{
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
return this.CreateServiceHost(typeof(MyService), baseAddresses);
}
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
// ...
}
}
Here is my solution to this problem (I am not 100% sure whether we should be doing this).
In the set up of the unit test fixture, set up a hosting environment (NUnit example):
[TestFixtureSetUp]
public void TestFixtureSetup()
{
if(!HostingEnvironment.IsHosted)
{
// The instance constructor hooks up the singleton hosting environment, ewww...
new HostingEnvironment();
// Check the hosting environment is fully initialized
ServiceHostingEnvironment.EnsureInitialized();
}
}
Then you should be free to use your custom ServiceHostFactory from inside of your unit tests:
[Test]
public void ServiceHostIsCorrect()
{
// Arrange
var serviceType = typeof (string);
var factory = new UnityServiceHostFactory();
// Act
var serviceHost = factory.CreateServiceHost(serviceType.AssemblyQualifiedName, new Uri[] {});
// Assert
Expect(serviceHost, Is.TypeOf<UnityServiceHost>());
var unityServiceHost = (UnityServiceHost)serviceHost;
Expect(unityServiceHost.Description.ServiceType, Is.EqualTo(serviceType));
}
I have a WCF/REST Web Service that I'm trying to add a global exception handler to. I'm looking for something similar to the Application_Error event in a standard .NET website.
I've found lots of info about using IErrorHandler and IServiceBehavior like what's detailed here: http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler.aspx#Y1479
That seems like what I need, but every example I've found assumes that the service is defined in the web.config. I'm not doing that - I'm using RouteTables, configured in the global.asax, like so:
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
RouteTable.Routes.Add(new ServiceRoute("", new WebServiceHost2Factory(), typeof(myService)));
}
So, given that, how do I configure my custom IErrorHandler and IServiceBehavior? Am I even on the right track, given that I'm using a RouteTable rather than configuring it via the web.config? I'm very new to WCF....
The wiring up of your IServiceBehaviour can be achieved by creating a custom WebServiceHostFactory that overrides CreateServiceHost.
For example if you have a class GlobalErrorHandlerBehaviour which implements IServiceBehavior, then you could wire it up as follows:
public class CustomWebServiceHostFactory : WebServiceHostFactory
{
protected override ServiceHost CreateServiceHost(System.Type serviceType, System.Uri[] baseAddresses)
{
return ApplyGlobalErrorHandler(base.CreateServiceHost(serviceType, baseAddresses));
}
private ServiceHost ApplyGlobalErrorHandler(ServiceHost serviceHost)
{
serviceHost.Description.Behaviors.Add(new GlobalErrorHandlerBehaviour());
return serviceHost;
}
}
You would then update your call to the ServiceRoute constructor to pass in this custom factory.