I am new to WCF and want to know how do I have multiple services in one project and exposing single end point. I did some home work and realized that we can use interfaces to achieve this. But I am unable to proceed on this.
Can you all give you opinion.
Example:
I have services like Employee Service and Customer Service
From client I should access it like IService.IEmployee.GetEmployee(); or IService.ICustomer.GetCustomer().
Hope I made it clear. Please guide me
Each service has always its own endpoint and each implemented service contract within the service requires its own endpoint as well.
You need facade in this case. You will have single service with single contract which will wrap the logic for both Employee.Service and Customer.Service.
The simplest implementation is like:
public interface ICustomerService { ... }
public interface IEmployeeService { ... }
[ServiceContract]
public interface IService : ICustomerService, IEmployeeService { ... }
// Your WCF service
public class Service : IService { ... }
Now Service can either implement both ICustomerService or IEmployeeService interfaces or it can just internally create instances of some implementations and delegate calls like:
public class Service : IService
{
private CustomerService customerService;
public Service()
{
customerService = new CustomerService();
}
public Customer GetCustomer()
{
retunr customerService.GetCustomer();
}
}
If you have a service (implementation), you can expose any number of endpoints for that service. A service implementation class can implement multiple service contracts, e.g.
public class YourServiceImplementation : IEmployeeService, ICustomerService
{
.....
}
Each endpoint you define for this service implementation however requires one single contract to be associated with it - you cannot have a single endpoint that supports multiple contracts at the same time:
<services>
<service name="YourNamespace.YourServiceImplementation">
<endpoint name="Employee"
address="http://YourServer/Services/EmployeeServices.svc"
binding="basicHttpBinding"
contract="YourNamespace.IEmployeeService" />
<endpoint name="Customer"
address="http://YourServer/Services/CustomerServices.svc"
binding="basicHttpBinding"
contract="YourNamespace.ICustomerService" />
</service>
</services>
So if your client needs to access both the employee and the customer service, you would need to add two separate client-side proxies, one for each service contract.
Related
I was exploring ChannelFactory and while doing so I did following:
A service contract in assembly named "Common":
namespace Common
{
using System.ServiceModel;
[ServiceContract(Name = "ITestService", Namespace = "http://test/")]
public interface ITestService
{
[OperationContract(Name = "SayHello")]
string SayHello(string request);
}
}
A service hosted under web application called "WcfServiceApp":
Note that I have created another service interface (contract) to create a service. But the names of contracts and the namespaces are same as the contract defined in the "Common" assembly.
namespace WcfServiceApp
{
[ServiceContract(Name = "ITestService", Namespace = "http://test/")]
public interface ITestServiceWithDiffDotNetName
{
[OperationContract(Name = "SayHello")]
string SayHelloAgain(string name);
}
// This service implements new service contract.
public class TestService : ITestServiceWithDiffDotNetName
{
public string SayHelloAgain(string request)
{
return "hello " + request;
}
}
// This service implements common service contract
public class TestService2 : Common.ITestService
{
public string SayHello(string request)
{
return "hello " + request;
}
}
}
There are two ".svc" files (TestService.svc and TestService2.svc), each for services created above. Web.config has following:
<system.serviceModel>
<services>
<service name ="WcfServiceApp.TestService" >
<endpoint binding="basicHttpBinding" contract="WcfServiceApp.ITestServiceWithDiffDotNetName"></endpoint>
</service>
<service name ="WcfServiceApp.TestService2" >
<endpoint binding="basicHttpBinding" contract="Common.ITestService"></endpoint>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
A client which calls these two services:
Note that client is using ChannelFactory and the service contract defined in "Common" library.
ChannelFactory<ITestService> commonServiceChannel = new ChannelFactory<ITestService>(new BasicHttpBinding(), "http://localhost/WcfServiceApp/TestService.svc");
var proxy = commonServiceChannel.CreateChannel();
var response = proxy.SayHello("Mike"); // response = "Hello"
ChannelFactory<ITestService> commonServiceChannel2 = new ChannelFactory<ITestService>(new BasicHttpBinding(), "http://localhost/WcfServiceApp/TestService2.svc");
var proxy2 = commonServiceChannel2.CreateChannel();
var response2 = proxy2.SayHello("Mike"); // response2 = "Hello Mike"
Question:
I observed that first service (created using some different service contract) receives the null argument whereas argument received in second service (created using service contract used to create ChannelFactory) is "Mike" as expected.
In Fiddler, I can see that request parameter correctly.
Why does this happen?
If all XML names and namespaces are same (although names of .NET interfaces are different), should the service call not succeed as underlying SOAP messages would be same?
I am afraid what will happen if my customer's applications want to create service in Java and my application is supposed to call it?
Try this, get the WSDL document from each version of your service. Compare the WSDL documents (.NET 4.5 supports single file WSDL documents out of the box) to see what WCF is expecting in the soap message for each service. Chances are a default XML namespace was taken from the .NET (different) namespaces somewhere thus making the "identical" service contracts actually be different. WCF does a lot for you in naming the XML namespaces and you will likely need to manually override those defaults throughout the service, operation and data contracts to make both services support identical soap messages.
On integrating with Java, as long as the Java client can be generated from the WSDL the service outputs there's a chance there won't be any issues. The big exception is configuring the security and authentication aspects of the Java client. This good blog post on specific WCF bindings for Java interop would be worth a look when working with Java based clients.
Thanks to Sixto Saez first.
I compared the WSDL files generated (using svcutil.exe) by both the services and found that they were NOT EXACTLY SAME. However, it was not due to any conflict in the name or namespace of either ServiceContract or OperationContract itself!
It was due to difference in the parameter names used in the definition of the OperationContracts!
You can see that OperationContract "SayHello" has a parameter named "request". On the other hand, OperationContract named "SayHelloAgain" has a parameter named "name". When I changed the name of the parameter from "request" to "name" as it is in second OperationContract, it worked!
So the conclusion is:
ChannelFactory WORKS when the contracts (.NET classes, interfaces) are NOT defined in some common library consumed by client and service. Only thing is that WSDLs generated by those service contracts have to match with each other.
I tried to minimize writing of code for WCF CRUD part of big project with use of generics and castle WCF facility.
I have WCF service contract:
[ServiceContract]
public interface IResourceService : ICRUDService<DTOResource>
{
[OperationContract]
DTOResource Get(long id);
}
and generic interface
public interface ICRUDService<T> where T is IDTO
{
T Get(long id);
}
also generic MVC controller (1 controller for all basic crud for dtos and services)
public class CRUDController<T> : Controller where T is IDTO
{
readonly ICRUDService<T> service;
public CRUDController(ICRUDService<T> service)
{
this.service = service;
}
}
On the client side i register WCF client in Windsor Container
Component
.For<IResourceService , ICRUDService<DTOResource>>()
.AsWcfClient(... standard stuff... )
Everythig is working fine, components and services registered, controller created properly,
service
readonly ICRUDService<T> service;
in controller is of type
Castle.Proxies.IResourceService
But when i try to use service in controller i have error
Method Get is not supported on this proxy, this can happen if the method is
not marked with OperationContractAttribute or if the interface type is not
marked with ServiceContractAttribute.
When in controller i hardcode cast
((IResourceService)service).Get(id);
all is running properly, so i believe this problem is solvable.
I've also tried to use Forward (with same result) :
Component
.For<IActionTypeService>
.Forward<ICRUDService<DTOResource>>().AsWcfClient(...
How to make it work?
In the end i had to use 'Channel Factory' on client side.
I was able to use Windsor WCF Facility on server side to register generic contract :
[ServiceContract]
public interface ICRUDService<I>
{
[OperationContract]
I Get(int id);
}
with generic implementation
public class CRUDService<I, IEntity> : ServiceBase, ICRUDService<I>
{
public I Get(int id)
{
...
}
in standard way (for multiple types)
private void InstallExample<I, IEntity>(IWindsorContainer container)
{
container.Register(
Component
.For<ICRUDService<I>>()
.ImplementedBy(CRUDService<I, IEntity>)
.Named("somename")
.AsWcfService(
new DefaultServiceModel()
.Hosted()
.PublishMetadata(x => x.EnableHttpGet())
.AddEndpoints(WcfEndpoint
.BoundTo(new BasicHttpBinding())
.At("someAddress")
)
)
.LifeStyle.PerWcfOperation();
}
with fileless activation in web.config
<add factory="Castle.Facilities.WcfIntegration.DefaultServiceHostFactory, Castle.Facilities.WcfIntegration" service="ClientService" relativeAddress="./ClientService.svc" />
On server side it works perfectly. Sadly on client side i didn't found working solution for WCFFacility and i had to use ChannelFactory (which is working perfectly)
ChannelFactory<ICRUDService<I>> factory = new ChannelFactory<ICRUDService<I>>(someBinding, someEndpoint);
For the rest (standard non generic services i'm using WCF Facility without any problems.
I think you need to put the ServiceContract attribute on ICrudService<>, add the OperationContract to the method there and remove the duplicate declaration of Get() from IResourceService.
I have created three assemblies. A web site, a WCF service and a contracts assembly that holds the interfaces that the services implement. I would like to use Castle Windsor to create the services for me on the client (website) so that I do not have to have an endpoint in the web.config of the web site for each service that I wish to use.
I would like to look at the contract assembly and get all the service interfaces in a namespace. Right now for every service I have something like the following when registering the components with the container.
container.Register(Component.For<ChannelFactory<IMyService>>().DependsOn(new { endpointConfigurationName = "MyServiceEndpoint" }).LifeStyle.Singleton);
container.Register(Component.For<IMyService>().UsingFactoryMethod((kernel, creationContext) => kernel.Resolve<ChannelFactory<IMyService>>().CreateChannel()).LifeStyle.PerWebRequest);
and in my web.config I have the setup code.
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="AuthToken" type="MyNamespace.Infrastructure.AuthTokenBehavior, MyNamespace.Contracts" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior>
<AuthToken />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00">
<readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647"></readerQuotas>
<security mode="None" />
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint name="MyServiceEndpoint" address="http://someurl/MyService.svc" binding="wsHttpBinding" contract="MyNamespace.Contracts.IMyService"></endpoint>
</client>
</system.serviceModel>
I end up with multiple service endpoints that all look almost exactly the same and when we deploy onto clients machines they have to set the address of every endpoint even though the base url is the same for every one.
I would like to have a base url in my web.config that is grabbed through code and then have the services registered with the container using reflection on the contracts assembly. I do need the specialised endpoint behaviour that is in the above config file.
Where so I start? the WcfFacility looks great but the doco is a bit lacking...
I agree the docs for the wcf facility are lacking and that is sad because it is a really great tool and it would be a real shame if people didn't use it because they could not get started, so let me see if I can help you out a little bit if I can...
Let's create a three project application that has:
A class library for shared contracts
A console application that acts as a server
A console application that acts as a client
The idea is that we want to be able to use the service names when we register the services and to share a base URL (I think that is what you were asking and if not, hopefully you can extrapolate from here). So, firstly, the shared contracts simply has this in it (nothing special, normal WCF fare):
[ServiceContract]
public interface IMyService1
{
[OperationContract]
void DoSomething();
}
[ServiceContract]
public interface IMyService2
{
[OperationContract]
void DoSomethingToo();
}
Now the server console application looks like this, we firstly implement the service contracts (again nothing special there, just classes implementing interfaces) and then just register them all as services (notice no need for any configuration file here and you can change the way you decide what are services etc using all the options that Windsor gives you - my scheme is a bit limited but it gives you an idea):
namespace Services
{
public class MyService1 : IMyService1
{
public void DoSomething()
{
}
}
public class MyService2 : IMyService2
{
public void DoSomethingToo()
{
}
}
}
//... In some other namespace...
class Program
{
// Console application main
static void Main()
{
// Construct the container, add the facility and then register all
// the types in the same namespace as the MyService1 implementation
// as WCF services using the name as the URL (so for example
// MyService1 would be http://localhost/MyServices/MyService1) and
// with the default interface as teh service contract
var container = new WindsorContainer();
container.AddFacility<WcfFacility>(
f => f.CloseTimeout = TimeSpan.Zero);
container
.Register(
AllTypes
.FromThisAssembly()
.InSameNamespaceAs<MyService1>()
.WithServiceDefaultInterfaces()
.Configure(c =>
c.Named(c.Implementation.Name)
.AsWcfService(
new DefaultServiceModel()
.AddEndpoints(WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format(
"http://localhost/MyServices/{0}",
c.Implementation.Name)
)))));
// Now just wait for a Q before shutting down
while (Console.ReadKey().Key != ConsoleKey.Q)
{
}
}
}
And that is the server, now how to consume these services? Well, actually that is quite easy, here is a client console application (it references just the contracts class library):
class Program
{
static void Main()
{
// Create the container, add the facilty and then use all the
// interfaces in the same namespace as IMyService1 in the assembly
// that contains the aforementioned namesapce as WCF client proxies
IWindsorContainer container = new WindsorContainer();
container.AddFacility<WcfFacility>(
f => f.CloseTimeout = TimeSpan.Zero);
container
.Register(
Types
.FromAssemblyContaining<IMyService1>()
.InSameNamespaceAs<IMyService1>()
.Configure(
c => c.Named(c.Implementation.Name)
.AsWcfClient(new DefaultClientModel
{
Endpoint = WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format(
"http://localhost/MyServices/{0}",
c.Name.Substring(1)))
})));
// Now we just resolve them from the container and call an operation
// to test it - of course, now they are in the container you can get
// hold of them just like any other Castle registered component
var service1 = container.Resolve<IMyService1>();
service1.DoSomething();
var service2 = container.Resolve<IMyService2>();
service2.DoSomethingToo();
}
}
That's it - hopefully this will get you started (I find that experimenting and using the intellisense usually gets me where I need to go). I showed you both the service and client sides but you can just use one or the other if you prefer.
You should be able to see where the binding is configured and how I have gone about constructing the URLs so in your case you could easily just pluck your base URL from a configuration file or whatever you want to do.
One last thing to mention is that you can add your custom endpoint behaviour by adding it as an extension to the endpoint, so in the client example you would have something like this:
Endpoint = WcfEndpoint
.BoundTo(new WSHttpBinding())
.At(string.Format("http://localhost/MyServices/{0}", c.Name.Substring(1)))
.AddExtensions(new AuthTokenBehavior())
When using WCF,
how can you choose which type an interface will be serialized into?
for example, the CarA type is used in the server, and they are sent to client over WCF as ICars, and the client should receive them as CarBs.
public interface ICar
{
int Size { get; set; }
int Price { get; set; }
}
public class CarA : ICar
{
//ICar implementations...
}
public class CarB : ICar
{
//ICar implementations...
}
[ServiceContract]
public interface ICarManager
{
[OperationContract]
ICar GetMyCar(int userID);
}
"which type an interface will be serialized into" - this makes zero sense. If you send CarA's to your client the client will receive CarA's.
I think you may be asking about how to tell WCF that your service contract contains types which are derived from another type. To do this you can use ServiceKnownTypeAttribute.
UPDATE
If you want to return a type of ICar in your service operation you need to specify the concrete types which will be available on your endpoint. You do this by using the ServiceKnownTypeAttribute like so:
[ServiceKnownType(typeof(CarA))]
[ServiceKnownType(typeof(CarB))]
[ServiceContract]
public interface ICarManager
{
[OperationContract]
ICar GetMyCar(int userID);
}
You could look at customize reply message by implement custom extension using IDispatchMessageFormatter
try this link if help you
IDispatchMessageFormatter – How to customize reply Messages on the server side
From Your server side you can only expose unique endpoint so you will expose only one endpoint as ICar and then you will choose which implementation of ICar you want to host via ServiceHost ,it can be CarA or CarB.
Now on client side you will just have a only interface as ICar and you should not be confused with which type should be serialized using your interface Icar. You will be only calling server side to get the service not the implementation.
More on ServiceHost
Use the ServiceHost class to configure and expose a service for use by
client applications when you are not using Internet Information
Services (IIS) or Windows Activation Services (WAS) to expose a
service. Both IIS and WAS interact with a ServiceHost object on your
behalf
I'm trying to create a service reading the dead-letter from the transactional system dead letter queue.
The service configuration looks like this:
<service name="NotificationDeadLetterQueueService">
<endpoint
address="net.msmq://localhost/system$;DeadXact"
binding="netMsmqBinding"
contract="INotificationService"
/>
</service>
My service interface:
[ServiceContract]
public interface INotificationService
{
[OperationContract(IsOneWay=true)]
[NetDataContractSerializerFormatAttribute]
void HandleNotification(Notification notification);
}
With my service implementation:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class NotificationDeadLetterQueueService : INotificationService
{
#region INotificationService Members
[OperationBehavior(TransactionScopeRequired = true)]
public void HandleNotification(Notification notification)
{
throw new NotImplementedException();
}
#endregion
}
And start the host like this:
ServiceHost serviceHost = new ServiceHost(typeof(NotificationDeadLetterQueueService));
serviceHost.Open();
So far everything looks like described in many books and tutorials, but when I generate dead-letters within the transactional dead-letter queue the service does not get invoked. What is wrong in my service?
Thanks for help
Enyra
I found the problem, which is quite trick. The msmq client endpoint uses a binding configuration with , so the dead-letter queue service also needs a binding which defines the security mode to none.