Resolving the same interface with Unity from two different assemblies in a single custom WCF Service Host Factory - wcf

I am building a POC that aims to demonstrate how a dependency hierarchy can be constructed from within a WCF service with Unity and keep all assemblies in an application completely loosely coupled.
What I did is to create the following class libraries:
Data Access Layer:
- One assembly with the repository interface.
- One assembly with the implementation of this interface that pretends to access a DB.
- One assembly with the implementation of this interface that pretends to access XML docs.
Business Layer:
- One assembly with the business object interface.
- One assembly with the implementation of this interface that receives on its constructor the repository interface.
Service Layer:
- One assembly with the service interface.
- One assembly with the implementation of this interface that receives on its constructor the business object interface.
Finally I created an assembly with a Service Host Factory, a Service Host, and an Instance Provider that will be in charge of creating the dependency hierarchy. The code looks like so:
public class UnityServiceHostFactory : ServiceHostFactory
{
private readonly UnityContainer _container;
public UnityServiceHostFactory()
{
_container = new UnityContainer();
new ContainerConfigurator().Configure(_container);
}
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new UnityServiceHost(_container, serviceType, baseAddresses);
}
}
public class ContainerConfigurator
{
public void Configure(UnityContainer container)
{
container.RegisterType<IInvoiceRepository, InvoiceRepository>("dbInvoiceRepository");
container.RegisterType<IInvoiceRepository, XmlInvoice>("xmlInvoiceRepository");
container.RegisterType<IInvoiceFinder, InvoiceFinder>();
}
}
public class UnityServiceHost : ServiceHost
{
public UnityServiceHost(UnityContainer container, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
if(container == null) throw new ArgumentNullException("container");
var contracts = ImplementedContracts.Values;
foreach (var c in contracts)
{
var instanceProvider = new UnityInstanceProvider(container, serviceType);
c.Behaviors.Add(instanceProvider);
}
}
}
public class UnityInstanceProvider : IInstanceProvider, IContractBehavior
{
private readonly UnityContainer _container;
private readonly Type _serviceType;
public UnityInstanceProvider(UnityContainer container, Type serviceType)
{
if (container == null) throw new ArgumentNullException("container");
if (serviceType == null) throw new ArgumentNullException("serviceType");
_container = container;
_serviceType = serviceType;
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
return _container.Resolve(_serviceType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
_container.Teardown(instance);
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.InstanceProvider = this;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
}
I am testing this with a console application, I instantiate the service proxy and make the call to its method but, since both registrations are named Unity does not know which one to instantiate. If I remove the name from either of them it gets resolved successfully.
Basically I would like to be able to do something like this:
static void Main(string[] args)
{
//First call, want it to resolve to the InvoiceRepository concrete type
//new InvoiceService(new InvoiceFinder(new InvoiceRepository))
var invoiceService1 = new InvoiceProxy();
var response1 = invoiceService1.GetSumarizedInvoiceBy(new InvoiceRequest(1));
//Second call, want it to resolve to the XmlInvoice concrete type
//new InvoiceService(new InvoiceFinder(new XmlInvoice))
var invoiceService2 = new InvoiceProxy();
var response2 = invoiceService2.GetSumarizedInvoiceBy(new InvoiceRequest(2));
}
Notice how invoiceService1 and invoiceService2 are two different instances of the same service but the dependency within its own dependency was resolved differently for both.
What do I have to do so that I can tell Unity which repository to instantiate when I am either instantiating the service proxy or calling its method?
Thanks for your help.

Pass the name you gave Unity in the RegisterType<>() call to Resolve<>().
From Resolving an Object by Type and Registration Name
// Create container and register types
IUnityContainer myContainer = new UnityContainer();
myContainer.RegisterType(typeof(MyServiceBase), typeof(DataService), "Data");
myContainer.RegisterType(typeof(MyServiceBase), typeof(LoggingService), "Logging");
// Retrieve an instance of each type
MyServiceBase myDataService = (MyServiceBase)myContainer.Resolve(typeof(MyServiceBase), "Data");
You are using the generic extension methods that are equivalent to this example.

Related

How to invoke an IApplicationService into a WCF SOAP services in abp Boilerplate?

I developed a MVC application using abp boilerplate and now I have the necessity to expose some services via WFC/SOAP.
The idea is to create a WFC Service, inject the required IApplicationService and use it.
Something like:
// this code does not work
public class MyFirstService : IMyFirstService, ITransientDependency {
private readonly ICourseAppService _courseAppService;
// Injection here does not work!
public MyFirstService(ICourseAppService courseAppService) {
_courseAppService = courseAppService;
}
public CourseDto GetData(int id) {
return _courseAppService.Get(id);
}
}
But this code does not work. :-(
The first error I have is from WCF saying the Service does not have a default constructor without parameters. So I am on the wrong way.
How can I inject the service into the SOAP service?
The answer https://stackoverflow.com/a/46048289/752004 did not help me.
WCF uses Reflection to create service instance, so if your service has no constructor without parameters , wcf will fail to create the service instance which is why wcf shows the error.
It is not easy to integrate injection framework with wcf.
You should customize instance provider(which provides wcf service instance).
https://blogs.msdn.microsoft.com/carlosfigueira/2011/05/31/wcf-extensibility-iinstanceprovider/
In your customized instance provider , you could provide your injected service instance in the method GetInstance.
Then you should make wcf use your own instance provider using service behavior.
For example
public class MyServiceAttribute : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher item in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher item1 in item.Endpoints)
{
item1.DispatchRuntime.InstanceProvider = new MyInstanceProvider(); // apply customized instanceProvider
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
Then you should customize a ServiceHost to apply the service behavior.
Like
public class MyUnityServiceHost : ServiceHost
{
protected MyUnityServiceHost()
{
}
protected override void OnOpening()
{
base.OnOpening();
if (this.Description.Behaviors.Find<MyServiceAttribute >() == null)
{
this.Description.Behaviors.Add(new MyServiceAttribute ());//add your behavior
}
}
}
At last, you should customize HostFactory to create your customized servicehost.
https://blogs.msdn.microsoft.com/carlosfigueira/2011/06/13/wcf-extensibility-servicehostfactory/
You could refer to the similar discussion below.
Injecting data to a WCF service
Abp uses the Castle Windsor, so following the suggestions from this answer and this article I found the solution.
Once imported the nuget package Castle.WcfIntegrationFacility, I created a new WCF library and in it I created a AbbModule class where I registered MyService (defined in pt. 3):
[DependsOn(typeof(BookingCoreModule), typeof(BookingApplicationModule))]
public class BookingSoapModule : AbpModule {
public override void Initialize() {
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
IocManager.IocContainer.AddFacility<WcfFacility>().Register(
Component
.For<IMyService>()
.ImplementedBy<MyService>()
.Named("MyService")
);
}
}
Then I created my IMyService interface (note that it extends ITransientDependency):
[ServiceContract]
public interface IMyService : ITransientDependency {
[OperationContract]
CourseDto GetCourse(int courseId);
}
Finally I implemented the interface with a constructor using injection:
public class MyService : IMySecondService {
private readonly ICourseAppService _courseAppService;
public IAbpSession AbpSession { get; set; }
public ILogger Logger { get; set; }
public MyService(ICourseAppService courseAppService) {
AbpSession = NullAbpSession.Instance;
Logger = NullLogger.Instance;
_courseAppService = courseAppService;
}
public CourseDto GetCourse(int courseId) {
AsyncHelper.RunSync(async () => {
var course = await _courseAppService.Get(courseId);
return course;
});
}
}

SignalR, WebAPI and MVC sharing the same dependency resolver kernel

I have an ASP.NET MVC app with SignalR and WebAPI. The app uses Ninject for dependency injection, but apparently SignalR and WebAPI are getting different kernels, so it fails to share a singleton object that should be shared for all the application.
I can see clearly in the log how an instance is created when SignalR gets a connection request, and other when WebAPI gets a request.
I want to have the same Ninject kernel shared among these three elements, so I can have unique singletons.
This is what I have done so far:
The first thing I have done is creating a NinjectModule declaring the binding:
public class MyDependencyModule: NinjectModule
{
public override void Load()
{
var binding = Bind<MustBeSingleton>().ToSelf();
binding.OnActivation((ctx, o) =>
{
Debug.Print("Registering item " + o.GetHashCode());
HostingEnvironment.RegisterObject(o);
});
binding.OnDeactivation(o =>
{
Debug.Print("Unregistering game connection " + o.GetHashCode());
});
binding.InSingletonScope();
}
}
I have also created a wrapper for Ninject in order to plug it in WebAPI:
public class NinjectDependencyScope : IDependencyScope
{
private IResolutionRoot resolver;
internal NinjectDependencyScope(IResolutionRoot resolver)
{
this.resolver = resolver;
}
public void Dispose()
{
IDisposable disposable = resolver as IDisposable;
if (disposable != null)
disposable.Dispose();
resolver = null;
}
public object GetService(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (resolver == null)
throw new ObjectDisposedException("this", "This scope has already been disposed");
return resolver.GetAll(serviceType);
}
}
public class NinjectDependencyResolver : NinjectDependencyScope, IDependencyResolver
{
private IKernel kernel;
public NinjectDependencyResolver(IKernel kernel)
: base(kernel)
{
this.kernel = kernel;
}
public IDependencyScope BeginScope()
{
return new NinjectDependencyScope(kernel.BeginBlock());
}
}
Also, I have created another wrapper for SignalR:
public class SignalRNinjectDependencyResolver : DefaultDependencyResolver
{
private readonly IKernel _kernel;
public SignalRNinjectDependencyResolver(IKernel kernel)
{
_kernel = kernel;
}
public override object GetService(Type serviceType)
{
return _kernel.TryGet(serviceType) ?? base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return _kernel.GetAll(serviceType).Concat(base.GetServices(serviceType));
}
}
Then I have created a Ninject kernel that does all the config:
public class ApplicationDependencies:StandardKernel
{
public ApplicationDependencies()
:base(new MyDependencyModule())
{
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(this);
Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver = new SignalRNinjectDependencyResolver(this);
}
}
The MVC application, uses NinjectHttpApplication as base class, so I indicate the kernel that must be used this way:
public class MvcApplication : Ninject.Web.Common.NinjectHttpApplication
{
protected override Ninject.IKernel CreateKernel()
{
return new ApplicationDependencies();
}
}
Also, in the SignalR configuration I specify the Resolver:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.MapSignalR<MyPersistentConnection>("/updates", new ConnectionConfiguration()
{
Resolver = GlobalHost.DependencyResolver
});
}
}
(I have tried also without specifying the resolver, and it does not work either).
Any idea?
Cheers.
I found the answer in another post: Singleton Scope binding not working as intended
Rather than binding as a singleton, "ToConstant" must be used:
var binding = Bind<MustBeSingleton>().ToConstant(new MustBeSingleton());
I have created a simple demo project with ASP.NET MVC, WebAPI and SignalR using the same dependency injection context.
https://drive.google.com/file/d/0B52OsuSSsroNX0I5aWFFb1VrRm8/edit?usp=sharing
The web app, contains a single page that shows the AppDomain and GetHashCode of an object that is supposed to be unique across the three frameworks, giving a result similar to:
Dependency Test
Framework IMySingletonService instance
MVC AppDomainId:2 / HashCode:5109846
WebAPI AppDomainId:2 / HashCode:5109846
SignalR AppDomainId:2 / HashCode:5109846
Other problem was, that Ninject was disposing my singleton because was IDisposable. I don't really understand why this happens, but that is another war.
Cheers.
In order keep this 3 things working.. you should check these references out:
Web API + Ninject
http://www.peterprovost.org/blog/2012/06/19/adding-ninject-to-web-api/
SignalR + Ninject https://github.com/SignalR/SignalR/wiki/Extensibility (last part:
When using ASP.NET MVC, configure SignalR first, then ASP.NET MVC)
For the second one, I refactored a little bit, since I need the kernel for SignalR Dependency Resolver
// Route SignalR.
GlobalHost.DependencyResolver = NinjectWebCommon.GetSignalrResolver();
RouteTable.Routes.MapHubs();
I defined GetSignalrResolver inside of NinjectWebCommon like this:
public static Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver GetSignalrResolver()
{
return new Microsoft.AspNet.SignalR.Ninject.NinjectDependencyResolver(bootstrapper.Kernel);
}
Note: There are 2 different DependencyResolver: one for Web API (1) assigned to GlobalConfiguration.Configuration.DependencyResolver and the other for SignalR (2) assigned to GlobalHost.DependencyResolver
in order to use a dependency resolver for both WebApi and SignalR you need to implement a class that looks like this:
public class NinjectDependencyResolver : Microsoft.AspNet.SignalR.DefaultDependencyResolver,
System.Web.Http.Dependencies.IDependencyResolver
{
public readonly IKernel Kernel;
public NinjectDependencyResolver(string moduleFilePattern)
: base()
{
Kernel = new StandardKernel();
Kernel.Load(moduleFilePattern);
}
public override object GetService(Type serviceType)
{
var service = Kernel.TryGet(serviceType) ?? base.GetService(serviceType);
return service;
}
public override IEnumerable<object> GetServices(Type serviceType)
{
IEnumerable<object> services = Kernel.GetAll(serviceType).ToList();
if (services.IsEmpty())
{
services = base.GetServices(serviceType) ?? services;
}
return services;
}
public System.Web.Http.Dependencies.IDependencyScope BeginScope()
{
return this;
}
public void Dispose()
{ }
}
then in your startup class you should register NinjectDependencyResolver for both WebApi and SignalR, like this:
public void Configuration(IAppBuilder app)
{
var dependencyResolver = new NinjectDependencyResolver("*.dll");
var httpConfiguration = new HttpConfiguration();
httpConfiguration.DependencyResolver = dependencyResolver;
app.UseWebApi(httpConfiguration);
var hubConfig = new HubConfiguration { Resolver = dependencyResolver };
app.MapSignalR(hubConfig);
}

Can't transmit standard serializable object across WCF

I have created a very simple server and client console app demonstrating the issue I have in that I am trying to bring an instance of a serializable object across to the client but it fails on the server.
What am I missing?? I am NOT concerned right now having it Service orientated using DataContracts - I am simply trying to understand why the code as it stands doesn't bring the EJob accross to the client (it DOES however calls the 'Hello from the server' message)
Many thanks.
EDIT
Even if I decorate the EJob class with a DataContract attribute (like below) it STILL doesn't work - the object I receive on the client has LastName set to null?????
[DataContract]
public class EJob
{
[DataMember]
public string LastName = "Smith";
}
SERVER
namespace testServer
{
[ServiceContract()]
public interface IRemoteClient
{
[OperationContract]
void SayHi(string msg);
[OperationContract]
void ProcessJob(EJob job);
}
[Serializable()]
public class EJob
{
public string LastName = "Smith";
}
class Program
{
static void Main(string[] args)
{
MngrServer.SendJob();
}
}
public class MngrServer
{
public static void SendJob()
{
try
{
// send this off to the correct exe
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, true);
string address = string.Format("net.tcp://localhost:33888/BatchMananger/client");
EndpointAddress epa = new EndpointAddress(address);
// create the proxy pointing to the correct exe
IRemoteClient clientProxy = ChannelFactory<IRemoteClient>.CreateChannel(binding, epa);
clientProxy.SayHi("Hello from server"); <-- THIS WORKS FINE
EJob job = new EJob { LastName = "Janssen" };
clientProxy.ProcessJob(job); <-- THIS RAISES AN EXCEPTION see below...
}
catch (Exception ex)
{
string msg = ex.Message;
//The formatter threw an exception while trying to deserialize the message: There was an error while
//trying to deserialize parameter http://tempuri.org/:job. The InnerException message was ''EndElement' 'job'
//from namespace 'http://tempuri.org/' is not expected. Expecting element 'LastName'.'.
}
}
}
}
CLIENT
namespace testClient
{
[ServiceContract()]
public interface IRemoteClient
{
[OperationContract]
void SayHi(string msg);
[OperationContract]
void ProcessJob(EJob job);
}
[Serializable()]
public class EJob
{
public string LastName = "Smith";
}
class Program
{
static void Main(string[] args)
{
MngrClient.Prepare();
Console.ReadLine();
}
}
/// <summary>
/// STATIC / INSTANCE
/// </summary>
public class MngrClient : IRemoteClient
{
public void SayHi(string msg)
{
Console.WriteLine(msg);
}
public void ProcessJob(EJob job)
{
Console.WriteLine(job.LastName);
}
public static void Prepare()
{
// allow this class to be used! - so instances are created and info directly passed on to its static members.
ServiceHost sh = new ServiceHost(typeof(MngrClient));
// create the net binding
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, true);
// define the tcpaddress
string address = string.Format("net.tcp://localhost:33888/BatchMananger/client");
// add a service point so my server can reach me
sh.AddServiceEndpoint(typeof(IRemoteClient), binding, address);
// now open the service for business
sh.Open();
}
}
}
Your EJob datacontract is in a different namespace on the server vs. the client. You need to either declare both classes in the same namespace, or use attributes to set the namespace on the client to match the namespace on the server
(Either the Datacontract attribute has a namespace value that you can pass, or there is a separate namespace attribute that you can use to tell WCF to use an alternate namespace for the contract, can't remember off the top of my head)
EDIT
Just verified -- it's the Namespace property of the DataContractAttribute that you want, so in your client-side declaration:
[DataContract(Namespace="EJobNamespaceAsItIsDeclaredOnTheServer")]
public class EJob ...
Now, it is very common to put all of your DataContracts in a separate assembly (called a contract assembly) that is referenced by both the client and the server. You would want just the contract class definitions in that assembly, nothing else.
You somehow have it all a bit backwards...
given your service contract of IRemoteClient, you should then have an implementation class on the server-side that implements that interface:
public class ServiceImplementation : IRemoteClient
{
public void SayHi(string msg)
{
.....
}
public void ProcessJob(EJob job)
{
.....
}
}
Also: the service methods should be returning something to the caller! Without a return type, you're kinda creating a black-hole of a service - you can call its methods, but nothing gets returned.... Plus: the service implementation class should NOT be hosting itself! Make that a separate class
you should then have a host class on the server side that hosts this service:
public class HostForYourService
{
public HostForYourService()
{
// send this off to the correct exe
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, true);
string address = string.Format("net.tcp://localhost:33888/BatchMananger/client");
EndpointAddress epa = new EndpointAddress(address);
ServiceHost sh = new ServiceHost(typeof(ServiceImplementation));
// define the tcpaddress
sh.AddServiceEndpoint(typeof(IRemoteClient), binding, address);
// now open the service for business
sh.Open();
}
and then your client should build the client-side proxy for this service and call it
public class YourServiceClient
{
public void CallService()
{
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, true);
string address = string.Format("net.tcp://servername:33888/BatchMananger/client");
EndpointAddress epa = new EndpointAddress(address);
// create the proxy pointing to the correct exe
IRemoteClient clientProxy = ChannelFactory<IRemoteClient>.CreateChannel(binding, epa);
clientProxy.SayHi("Hello from server"); <-- THIS WORKS FINE
EJob job = new EJob { LastName = "Janssen" };
clientProxy.ProcessJob(job);
}
}
But again: typically, your service methods should be returning something that the client can then operate on - after all, you typically don't want to do a Console.WriteLine on the server - you want to compute something, look up something etc. and return a response to the client which then in turns can e.g. output the result to the console or something....

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.

Using Castle Windsor WCF facility with WCF 4 REST service gives 'Could not find a component' error

As a long time reader of StackOverflow but not finding the solution to my problem here is my first attempt to ask a question, so don't be too harsh on me :-)
I have the following WCF 4 REST service definitions:
Service contract
namespace RestService2.Service
{
[ServiceContract]
public interface ISampleService
{
[WebGet(UriTemplate = "")]
List<SampleItem> GetCollection();
[WebInvoke(UriTemplate = "", Method = "POST")]
SampleItem Create(SampleItem instance);
[WebGet(UriTemplate = "?id={id}")]
SampleItem Get(int id);
[WebInvoke(UriTemplate = "?id={id}", Method = "PUT")]
SampleItem Update(int id, SampleItem instance);
[WebInvoke(UriTemplate = "?id={id}", Method = "DELETE")]
void Delete(int id);
}
}
Service implementation
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class SampleService : ISampleService
{
private IDatabase db;
public SampleService(IDatabase db)
{
this.db = db;
}
public SampleService()
{
}
public List<SampleItem> GetCollection()
{
return db.Items.Values.ToList();
}
public SampleItem Create(SampleItem instance)
{
// Add the new instance of SampleItem to the collection
db.Items.Add(instance.Id, instance);
return db.Items[instance.Id];
}
//..Rest omitted..
}
Database interface:
using RestService2.Entities;
namespace RestService2.Service
{
public interface IDatabase
{
Dictionary<int, SampleItem> Items { get; }
}
}
Database implementation:
public class Database : IDatabase
{
private Dictionary<int, SampleItem> items;
public Database()
{
}
public Dictionary<int, SampleItem> Items
{
get
{
return items;
}
}
}
..and the global.asax file
namespace RestService2.Web
{
public class Global : HttpApplication
{
static IWindsorContainer Container {get; set;}
void Application_Start(object sender, EventArgs e)
{
BuildContainer();
RegisterRoutes();
}
private void BuildContainer()
{
Container = new WindsorContainer();
Container.AddFacility<WcfFacility>()
.Register(Component.For<ISampleService>().ImplementedBy<SampleService>().Named("SampleService"))
.Register(Component.For<IDatabase>().ImplementedBy<Database>());
}
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("SampleService",
new DefaultServiceHostFactory(), typeof(SampleService)));
}
}
}
The service contract, service implementation, database interface and database implementation are in assembly A, SampleItem (an entity) is defined in assembly B and the global.asax.cs is in assembly C.
I have added references from assembly A and B to assembly C.
When I try to access the service help page (or any service method for that matter) I get the following error message: Could not find a component with type RestService2.Service.SampleService, RestService2.Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, did you forget to register it?
Any idea what could be problem? How should i configure the container correctly?
Regards
I was able to build a REST service using the WCFIntegration facility, using both interfaces for registration and through the MVC routing mechanism.
// SVC Routes
routes.Add(new ServiceRoute("Example", new WindsorServiceHostFactory<RestServiceModel>(), typeof(IExample)));
Remember to register the service/implementation in Windsor before calling RegisterRoutes (where this code would be). Additionally, make sure you call this route registration before your default route, otherwise that will be used instead.
The service can then just be called via the route, ie:
http://localhost:80/Core/Example/GetAll/
since you registered the routing by concrete type
RouteTable.Routes.Add(new ServiceRoute("SampleService",
new DefaultServiceHostFactory(), typeof(SampleService)));
and as far as I remember you cannot do otherwise... I mean you cannot register by interface, you need to register into the container by concrete as well
instead of
.Register(Component.For<ISampleService>().ImplementedBy<SampleService>().Named("SampleService"))
try
.Register(Component.For<SampleService>().Named("SampleService"))