How to use Autofac with a CustomServiceHostFactory in an IIS hosted WCF service? - wcf

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.

Related

Castle Windsor - Resolving duplex wcf service

I'm hosting a duplex wcf service using windows service with castle windsor wcffacility using TCP binding.
There is no problem with hosting, I think, when I add a service reference to a console application.I'm able to access the duplex service without any issues.
Problem arises when I use castle windsor at the client side while resolving. Below is the code am using for adding the wcf services through code based on config file.
public static IWindsorContainer RegisterWcfClients(IocBuildSettings iocBuildSettings,
IWindsorContainer container)
{
//Register callback methods for duplex service first.
container.Register(Component.For<INotificationCallback>()
.ImplementedBy<NotificationCallbackCastle>()
.LifestyleTransient());
// get dictionary with key = service class, value = service interface
var servicesWithWcfInterfaces = Assembly.GetAssembly(typeof (IApplicationService))
.GetTypes()
.Where(x => (x.IsInterface || x.IsClass) && HasServiceContract(x))
.ToList();
var registrations = new List<IRegistration>();
//get the client section in System.ServiceModel from web.config file
var clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;
//get the endpointsCollection from childSection
var endpointCollection =
clientSection.ElementInformation.Properties[string.Empty].Value as ChannelEndpointElementCollection;
foreach (var serviceInterface in servicesWithWcfInterfaces)
{
//get the childEndpoint name from web.config file
var endpointName = GetClientEndpointName(endpointCollection, serviceInterface);
//register services which are declared in web.config file only.
if (string.IsNullOrEmpty(endpointName)) continue;
// attribute is either on the service class or the interface
var attribute =
(ServiceContractAttribute)
(Attribute.GetCustomAttribute(serviceInterface, typeof (ServiceContractAttribute)));
if (attribute != null)
{
WcfClientModelBase model = null;
// handle duplex differently
if (attribute.CallbackContract != null)
{
model = new DuplexClientModel
{
Endpoint =
WcfEndpoint.ForContract(serviceInterface).FromConfiguration(endpointName)
}.Callback(container.Resolve(attribute.CallbackContract));
registrations.Add(WcfClient.ForChannels(model).Configure(c => c.LifestyleSingleton()));
}
else
{
//regular attributes
model = new DefaultClientModel
{
Endpoint = WcfEndpoint.ForContract(serviceInterface).FromConfiguration(endpointName)
};
registrations.Add(WcfClient.ForChannels(model).Configure(c => c.LifestyleTransient()));
}
}
}
return container.Register(registrations.ToArray());
}
Am hosting only one duplex service and the below are the servicecontracts -
[ServiceContract(CallbackContract = typeof(INotificationCallback))]
public interface INotificationService
{
[OperationContract(IsOneWay = false)]
void Subscribe(Guid subscriptionId, string userName, string[] eventNames);
[OperationContract(IsOneWay = true)]
void EndSubscribe(Guid subscriptionId);
}
[ServiceContract]
public interface INotificationCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveNotification(NotificationResultDto notificationResult);
}
[DataContract]
public class NotificationResultDto
{
[DataMember]
public string UserName { get; set; }
[DataMember]
public string NotificationMessage { get; set; }
}
When I try to resolve the duplex service using the below statement.
var temp = _container.Resolve();
I get error -
WcfClientActivator: could not proxy component c2a216c2-af61-4cb2-83ba-e4d9a5cc4e68
with inner exception - The Address property on ChannelFactory.Endpoint was null. The ChannelFactory's Endpoint must have a valid Address specified.
in the web.config file under client section -
<endpoint address="net.tcp://localhost:9877/NotificationService" binding="netTcpBinding"
bindingConfiguration="netTcpBindingConfiguration" contract="ServiceContracts.INotificationService"
name="INotificationService_Endpoint" />
After few hours of struggling, I found a work around for this problem.
I think this could a bug in Castle Windsor, while creating DuplexClientModel, endpoint cannot be created using "FromConfiguration". It fails while resolving during runtime. However samething works fine with "DefaultClientModel".
My workaround was to read the config file and get the address, binding and contract details and use them to create Endpoint in code.
model = new DuplexClientModel
{
//Endpoint = WcfEndpoint.ForContract(serviceInterface).FromConfiguration(endpointName)
//FromConfiguration method is failing for some reason,could be b.u.g in castle,
//so had to do this workaround by reading the web.config file and creating the Endpoint
//from there manually.
Endpoint = WcfEndpoint.ForContract(serviceInterface)
.BoundTo(CreateBindings(clientEndpoint.Binding))
.At(clientEndpoint.Address)
}.Callback(container.Resolve(attribute.CallbackContract));

How to add OperationBehavior for all operations on IIS hosted WCF service?

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.

ASP.NET MVC Application with hosted WCF and Windsor: HttpContext.Current is null

I've built an ASP.NET MVC 3 Application hosting a WCF-Service. The service class doing the actual work resides within a class library. I'm trying to use the Windsor WCF facility to wire it up on service request.
Here's my Windsor container factory:
public class WindsorContainerFactory
{
private static IWindsorContainer container;
private static readonly object sync = new Object();
public static IWindsorContainer Current()
{
if(container == null) {
lock (sync)
{
if (container == null)
{
container = new WindsorContainer();
container.Install(new ControllerInstaller());
container.Install(new NHibernateSessionInstaller());
container.Install(new RepositoryInstaller());
container.Install(new ServiceInstaller());
}
}
}
return container;
}
}
In Global.asax, I call WindsorContainerFactory.Current() once to guarantee the factory is beeing built:
protected void Application_Start()
{
WindsorContainerFactory.Current();
ControllerBuilder.Current.SetControllerFactory(typeof(WindsorControllerFactory));
...
I install my service by the following class:
public class ServiceInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.Kernel.AddFacility<WcfFacility>();
container.Kernel.Register(
Component
.For<ICustomerService>()
.ImplementedBy<CustomerService>()
.Named("CustomerService"));
}
}
Then I added a new WCF service to the project, deleted the code behind file and modified the svc markup as follows:
<%# ServiceHost Language="C#" Debug="true"
Factory="Castle.Facilities.WcfIntegration.DefaultServiceHostFactory,
Castle.Facilities.WcfIntegration" Service="CustomerService" %>
As you can see, there are other components beeing installed within windsor (Repositories, Controllers,...), but this seems to work well.
My problem is, that the WCF-client (console app) gets the error:
HttpContext.Current is null. PerWebRequestLifestyle can only be used in ASP.Net
Client code:
CustomerServiceClient c = new Services.Customers.CustomerServiceClient();
CustomerResponse resp = c.GetCustomerBySurname("Lolman");
c.Close();
Can anyone point me to the right direction?
Thank you in advance!
Try enabling AspNetCompatibility.
Check this link for help

Inject dependency into IAuthorizationPolicy

I have a custom IAuthorizationPolicy which has a dependency on a repository
internal class CustomAuthorizationPolicy : IAuthorizationPolicy
{
private IBaseRepository _baseRepository;
public CustomAuthorizationPolicy(IBaseRepository baseRepository)
{
_baseRepository = baseRepository;
}
}
It is configured like this in web.config
<serviceAuthorization principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="CustomAuthorizationPolicy" />
</authorizationPolicies>
</serviceAuthorization>
This fails because WCF is not able to inject the required object when the policy is created.
It expects a parameterless constructor.
I am using StructureMap and has a custom IInstanceProvider that handles all other dependencies in my application. But I can't get it to handle this situation.
Is this possible to do??
I ended up solving this with the use of a custom ServiceHost and a ServiceHostFactory.
The factory sends the IoC container to the servicehost, which adds the new policy with a reference to the container. Now the policy can use the container to get the objects it needs.
public class CustomServiceHost : ServiceHost
{
public CustomServiceHost(IContainer container, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
// Keep existing policies
var policies = new List<IAuthorizationPolicy>();
if (Authorization.ExternalAuthorizationPolicies != null)
{
policies.AddRange(Authorization.ExternalAuthorizationPolicies);
}
// Add new policy
policies.Add(new PasswordAuthorizationPolicy(container));
Authorization.ExternalAuthorizationPolicies = policies.AsReadOnly();
// Set correct mode
this.Authorization.PrincipalPermissionMode = PrincipalPermissionMode.Custom;
}
}

WCF contract mismatch error using Autofac to register endpoint via ChannelFactory

I have a WCF service that works when accessed by a simple MVC application.
When I try to make call on the same endpoint from a different MVC app that's wired up with Autofac I get a binding/contract mismatch exception like this:
Content Type application/soap+xml;
charset=utf-8 was not supported by service http://localhost:6985/ProductService.svc.
The client and service bindings may be mismatched.
System.Net.WebException: The remote server returned an error: (415) Unsupported Media Type.
I'm reasonably confident I do not have a mismatch in the configuration settings on either end, I base this confidence on testing the exact same settings on a WCF + MVC combination where Autofac is not present. The config settings are on pastebin.com/t7wfR77h.
I therefore would like some help analysing if the way I have registered the dependency/endpoint with Autofac is the issue...
*Application_Start* code in MVC app for Autofac setup:
var builder = new ContainerBuilder();
//other registrations...
builder.Register(c =>
new ChannelFactory<IProductService>(
new WSHttpBinding("ProductService_wsHttpBinding"),
new EndpointAddress("http://localhost:6985/ProductService.svc")
)
).SingleInstance();
builder.Register(c =>
{
var factory = c.Resolve<ChannelFactory<IProductService>>();
return factory.CreateChannel();
}
).InstancePerHttpRequest();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
(For completeness) where I make use of this is in a ProductController that has only 1 dependency to be injected, very simple:
public class ProductController : AsyncController
{
private IProductService _service;
public ProductController(IProductService ps)
{
_service = ps;
}
//...
//later simply call
_service.SomeMethod();
}
As mentioned in the comment to #Nick Josevski, I was able to get something similar to work.
In my MVC3 application's Application_Start method, I have the following code:
protected void Application_Start()
{
var builder = new ContainerBuilder();
builder.Register(c => new ChannelFactory<ICartService>("CartService")).SingleInstance();
builder.Register(c => c.Resolve<ChannelFactory<ICartService>>().CreateChannel()).InstancePerHttpRequest();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// other MVC startup activities, like registering areas and routes
}
These registrations gather the WCF configuration data from Web.config. I've also gotten registrations to work with endpoints defined in code. For completeness, here's some of the pertinent client-side Web.config entries:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding" ... />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:50930/Purchasing/CartService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding"
contract="CartService.ICartService" name="CartService" />
</client>
</system.serviceModel>
Then, in my controller, I have code like the following:
using Autofac.Features.OwnedInstances;
public class BulkCartController : Controller
{
private readonly Owned<ICartService> cartService_;
public BulkCartController(Owned<ICartService> cartService)
{
cartService_ = cartService;
}
protected override void Dispose(bool disposing) // defined in Controller
{
cartService_.Dispose();
base.Dispose(disposing);
}
//
// GET: /BulkCart/Get/1
public ActionResult Get(int id)
{
var model = new ShoppingCart { ShoppingCartId = id };
using (var cartService = cartService_)
{
model.Items = cartService.Value.GetCartProductItems(id);
}
return View("Get", model);
}
}
Unit testing looks like this:
using Autofac.Features.OwnedInstances;
using Autofac.Util;
using Moq;
[TestMethod]
public void Get_ReturnsItemsInTheGivenCart()
{
var mock = new Mock<ICartService>(MockBehavior.Strict);
mock.Setup(x => x.GetCartProductItems(2)).Returns(new CartProductItemViewObject[0]);
var controller = new BulkCartController(new Owned<ICartService>(mock.Object, new Autofac.Util.Disposable()));
var result = controller.Get(2);
Assert.IsInstanceOfType(result, typeof(ViewResult));
var view = (ViewResult)result;
Assert.AreEqual("Get", view.ViewName);
Assert.IsInstanceOfType(view.ViewData.Model, typeof(ShoppingCart));
var model = (ShoppingCart)view.ViewData.Model;
Assert.AreEqual(2, model.ShoppingCartId);
Assert.AreEqual(0, model.Items.Length);
}
I validate disposal with a unit test defined in an abstract controller test base class:
[TestClass]
public abstract class ControllerWithServiceTestBase<TController, TService>
where TController : Controller
where TService : class
{
[TestMethod]
public virtual void Dispose_DisposesTheService()
{
var disposable = new Mock<IDisposable>(MockBehavior.Strict);
disposable.Setup(x => x.Dispose()).Verifiable();
var controller = (TController) Activator.CreateInstance(typeof(TController), new Owned<TService>(null, disposable.Object));
controller.Dispose();
disposable.Verify();
}
}
One thing I don't know yet is whether this use of Owned<T> and Dispose() gives me adequate disposal, or whether I'll need to use a LifetimeScope as per An Autofac Lifetime Primer.