WCF contract mismatch error using Autofac to register endpoint via ChannelFactory - wcf

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.

Related

Nservicebus 5 and later Web Api Depenedency Injection settings

How can I configure Web api dependency settings for NserviceBus 5 and later version.
Version 3 or 4 is like this:
public static class ConfigureWebApi
{
public static Configure ForWebApi(this Configure configure)
{
// Register our http controller activator with NSB
configure.Configurer.RegisterSingleton(typeof(IHttpControllerActivator),
new NSBHttpControllerActivator());
// Find every http controller class so that we can register it
var controllers = Configure.TypesToScan
.Where(t => typeof(IHttpController).IsAssignableFrom(t));
// Register each http controller class with the NServiceBus container
foreach (Type type in controllers)
configure.Configurer.ConfigureComponent(type, ComponentCallModelEnum.Singlecall);
// Set the WebApi dependency resolver to use our resolver
GlobalConfiguration.Configuration.ServiceResolver.SetResolver(new NServiceBusResolverAdapter(configure.Builder));
// Required by the fluent configuration semantics
return configure;
}
}
But Version 5 does not use Configure class, that use BusConfiguration
I try this but can not scan assemblies:
public static class ConfigureWebApi
{
public static BusConfiguration ForWebApi(this BusConfiguration configuration)
{
configuration.RegisterComponents(c => c.RegisterSingleton(typeof(IHttpControllerActivator),
new NServiceBusHttpControllerActivator()));
????
}
}
I'm not sure which way you're thinking. I'm asking, because I might be wrong with my answer. If so, let me know and I'll try to update it.
The way I go about this issue is setting up the container first and then have NServiceBus use that container. I'm using AutoFac and create a special class to set it up.
Disclaimer : I'm copying this from an existing app and didn't try nor compile it. I'm 100% sure this is working though, although I might've forgotten a line or added one too much! :)
public class DependenciesConfig
{
public static IContainer RegisterDependencies()
{
ContainerBuilder builder = new ContainerBuilder();
// MVC Controllers
builder.RegisterModule(new AutofacWebTypesModule());
builder.RegisterControllers(Assembly.GetExecutingAssembly())
// WebAPI controllers
var config = GlobalConfiguration.Configuration;
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterWebApiFilterProvider(config);
// Way more registrations
// Next line is AutoFac specific for WebAPI
builder.RegisterFilterProvider();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
return container;
}
}
Then I have an additional class for registering NServiceBus. I don't have handlers in my web app, nor do I publish messages, so I use a SendOnly endpoint.
public class ServiceBus
{
public static ISendOnlyBus Bus { get; private set; }
private static readonly object padlock = new object();
public static void Init(ILifetimeScope container)
{
if (Bus != null) return;
NServiceBus.Logging.LogManager.Use<CommonLoggingFactory>();
lock (padlock)
{
if (Bus != null) return;
var configuration = new BusConfiguration();
configuration.UseSerialization<JsonSerializer>();
configuration.UseContainer<AutofacBuilder>(x => x.ExistingLifetimeScope(container));
configuration.UseTransport<AzureStorageQueueTransport>();
ConventionsBuilder conventions = configuration.Conventions();
conventions.DefiningCommandsAs(t => t.Namespace != null && t.Namespace.StartsWith("Messages") && t.Namespace.EndsWith("Commands"));
Bus = NServiceBus.Bus.CreateSendOnly(configuration);
}
}
}
Is this what you're looking for?

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

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.

Autofac instance management?

Lets say I manually instantiate my objects like this:
var context = new MyDbContext();
_uow = new UnitOfWork(context);
_repository = new Repository<Account>(context, _uow);
Instead, I want to inject them and register them like so:
private readonly IUnitOfWorkAsync _uow;
private readonly IRepositoryAsync<Account> _repository;
public AccountService(IUnitOfWorkAsync uow, IRepositoryAsync<Account> repository)
{
_uow = uow;
_repository = repository;
}
Here is the registration.
builder.RegisterType<MyDbContext>().As<IDataContextAsync>().InstancePerDependency();
builder.RegisterType<UnitOfWork>().As<IUnitOfWorkAsync>();
builder.RegisterGeneric(typeof (Repository<>)).As(typeof (IRepositoryAsync<>));
builder.RegisterAssemblyTypes(typeof(AccountService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.As(t => t.GetInterfaces().FirstOrDefault(
i => i.Name == "I" + t.Name));
My service gets published to IIS and looks fine, but doesn't function correctly. For example, I go to insert a new Account and it doesn't save to the DB and doesn't error. It seems the injected UnitOfWork is not the same UnitOfWork used to instantiate the repository. I confirmed this with some code in the
constructor like so:
if(_uow.GetHashCode() != _repository.MyUow.GetHashCode())
throw new ArgumentException("Bad UOW");
If I inject the UnitOfWork and repository I ge the exception. If I manually instantiate my objects I do NOT get the exception. I've tried changing my registration to .InstancePerOwned() for the service and other various registration changes to no avail. How do I use Autofac registration to properly instantiate my repository BASED ON the UnitOfWork it already instantiated? I thought the default .InstancePerDependency() would suffice but doens't. Thanks.
Edit 1: I am using WCF and here is my CustomHostFactory. I don't see an option to specify .InstancePerRequest() like stated in the documentation. Also, interestingly, this line doesn't matter in the below code. Everything works exactly the same if I just take it out. AutofacServiceHostFactory.Container = container;
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
Type contractType = GetContractType(serviceType);
var builder = new ContainerBuilder();
builder.RegisterType<MyDbContext>().As<IDataContextAsync>();
builder.RegisterType<UnitOfWork>().As<IUnitOfWorkAsync>();
builder.RegisterGeneric(typeof (Repository<>)).As(typeof (IRepositoryAsync<>));
builder.RegisterAssemblyTypes(typeof(AccountService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.As(t => t.GetInterfaces().FirstOrDefault(
i => i.Name == "I" + t.Name));
var container = builder.Build();
AutofacServiceHostFactory.Container = container;
var host = new CustomServiceHost(serviceType, baseAddresses);
host.AddDependencyInjectionBehavior(contractType, container);
return host;
}
private static Type GetContractType(Type serviceType)
{
return serviceType.GetInterfaces()
.FirstOrDefault(i => Attribute.IsDefined(i, typeof(ServiceContractAttribute), false));
}
InstancePerRequest is only available for WebForms and MVC integration packages.
Try using Autofac integration with WCF in svc.less mode properly, as described below.
From Autofac docs:
Svc-Less Services
If you want to use services without an .svc file, Autofac will work with that.
As shown above, register your service with the container.
var builder = new ContainerBuilder();
builder.RegisterType<Service1>();
AutofacHostFactory.Container = builder.Build();
To use svc-less services, add a factory entry under the serviceActivation element in the web.config file. This ensures that the AutofacServiceHostFactory is used to activate the service.
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
<serviceActivations>
<add factory="Autofac.Integration.Wcf.AutofacServiceHostFactory"
relativeAddress="~/Service1.svc"
service="TestService.Service1" />
</serviceActivations>
</serviceHostingEnvironment>
I needed to change the registrations like so:
var builder = new ContainerBuilder();
builder.RegisterType<MyDbContext>().As<IDataContextAsync>().InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork>().As<IUnitOfWorkAsync>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepositoryAsync<>)).InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(typeof(AccountService).Assembly)
.Where(t => t.Name.EndsWith("Service"))
.As(t => t.GetInterfaces().FirstOrDefault(
i => i.Name == "I" + t.Name)).InstancePerLifetimeScope();
AutofacHostFactory.Container = builder.Build();
As well as setting each of my services to operate per call using the service behavior attribute below:
InstanceContextMode = InstanceContextMode.PerCall

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));

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