I read this Article and I thought I write a little ping Service (Both Service and Client can ping).
however, when I use
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession,
ConcurrencyMode = ConcurrencyMode.Multiple)]
My ping service only creates one instance. But I want multiple instances. My callback interface is saved in a private but it is overwritten because when the clients register, they register to one instance and not different.
Is that Bad design in my case or why does wcf only create one instance?
The problem was with the Windsor Container.
When I instanciate a Component with ´.AsWcfService´ Windsor doesn't care about the ServiceBehavior. So I need to set the lifestile to transient for this case.
Related
I'm using SimpleInjector and I get the following error when I try to call SimpleInjectorServiceHostFactory-->CreateServiceHost(Type serviceType, Uri[] baseAddresses). This error appear only for the WCF services marked as [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)].
The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.
For the services marked as [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] the SimpleInjectorServiceHostFactory-->CreateServiceHost(Type serviceType, Uri[] baseAddresses) method works perfectly even if the service doesn't have a default parameter-less constructor.
Any idea how to have an injection of the parameters for [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] services just like it happens with [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] services without the default parameter-less constructor ?
Thanks
After some digging I found that this is a bug in Simple Injector, or at least a misinterpretation in the way WCF handles this.
The ServiceHost class needs the singleton object in its ctor instead of the type. If the type is supplied the ServiceHost will try to create the instance and it therefore needs a default constructor.
I created a issue on GitHub for this.
but why this default constructor is not required for the "PerCall" WCF Service
Because in that case WCF will call back into the container for creating the type and Simple Injector offcourse can handle the parameters in the constructor.
A possible workaround would be to indeed configure your service as PerCall and assuming you want Single for some caching, refactor the cache out of the WCF implementation into its own class and register this as a singleton in Simple Injector.
You have to have a default constructor which is what WCF service calls to create your instance. You can take over this process by using your own IInstanceProvider and inject what you need.
If you are using Singleton (Single) then ServiceHost expects that you pass an already created instance (which you can create with any kind of constructor). I am not sure what your DI fx does to constructing this kind of instance. See this answer WCF ConcurrencyMode Single and InstanceContextMode PerCall as well.
In my Managed Application, I currently have my WCF services running as:
SomeService service = new SomeService(container) //IUnityContainer
ServiceHost serviceHost = new ServiceHost(service, serviceAddress);
Whats the catch ? SomeService defined with:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single
This is not good anymore, I need to make it InstanceContextMode.PerCall.
When trying to .Open() If changing the InstanceContextMode to "PerCall" - it will throw:
System.InvalidOperationException: In order to use one of the ServiceHost constructors that takes a service instance, the InstanceContextMode of the service must be set to InstanceContextMode.Single. This can be configured via the ServiceBehaviorAttribute. Otherwise, please consider using the ServiceHost constructors that take a Type argument
Is this the solution to my problem ? How do I pass values to the constructor on my wcf service?
My Main concern:
I run different types of services inside this managed application, It seems that this solution is good only if i run one type of service.
When more than one service instance will be needed (PerCall or PerSession) then passing a single service instance into the ServiceHost isn’t enough... which is the exception.
Controlling instance creation is managed by the IInstanceProvider.
Is this the solution to my problem ? How do I pass values to the constructor on my wcf service?
This only answers half your question. You are using Unity. The management of creating the container needs to be part of the implementation. The most common solution is to use Unity.WCF which is also available as a NuGet package.
Note that Unity.WCF doesn’t support object lifetimes based WCF OperationContexts. There are multiple (more complicated) implementations like this that do.
I would create a WCF service with some methods. One of these methods (that is, the Connect method) should be the first to be called in order to use the service: in other words, before you can use all other methods of service, must be called the Connect method. For this reason, I defined it with IsInitiating property set to true, and I have defined the other methods with this property set to false.
In addition, the node offering the service must be able to refuse the connection request from another node (for example, if other nodes are already using the service): is there a way to prevent the use of the service?
Thanks a lot!
Well, sure.
First understand that by default, WCF services are an "instance-per-request" construct; The HttpApplications that IIS maintains in the app pool will "new up" a copy of your service contract class, make the call pertaining to the request, then the object will go out of scope and be destroyed. You can override this by stating that your service should run in "instance-per-session" mode:
[ServiceContract(SessionMode = SessionMode.Required)]
public interface IMyServiceContract
{
...
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyServiceImplementation: IMyServiceContract
{
...
}
Now, when your service is called, a "session" is established between client and server, and a single copy of your class will be created and remain in memory for the life of that session (unless the app pool is refreshed, which can happen automatically or by a manual action within IIS). This is the first step.
Now, you can do one of two things:
Simply check in any method besides Connect() whether Connect() has been called on this instance since its creation. If not, throw out.
Have the Connect() method return some instance-scoped token or GUID that the client must then pass to all other method calls. If the GUID the caller provides doesn't match the one kept in instance memory, then throw out of the method.
Understand that sessions can time out between requests. If this happens, your current instance will leave scope and be destroyed, and a new instance will be created to handle subsequent requests. I would thus opt for the second option even though the system can identify instances based on their session; the GUID ensures that both the client AND service instance have not changed since the last call.
I've been preparing for MS 70-513 exam and with the Self-paced training kit content cd there are some practice tests.
The following is one question of those tests which I kindly ask someone to explain. The correctly marked answer is A, however I don't understand why D isn't the correct one.
Question:
What is the default behavior for instancing service objects in WCF?
A - Each instance is associated with one user-defined service object.
B - Each instance handles all requests for the lifetime of the requesting application.
C - A new instance is created for each client request.
D - A new instance is created for each new client session.
Thakns in advance,
Bruno
I got an answer from msdn forums, I guess I'm convinced with it.
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/2e35729f-603a-4a52-a3b7-920c9f3a8100/
Quoting the answer:
This is a tricky one - I assume that the reason D is not true is that
not every client creates a session. For example basicHttpBinding does
not support sessions. So if you don't always have a session - D is not
always true. However, A is always true (unless you create your own
custom behavior) because every InstanceContext object wraps a single
user-defined service object (an object created according to your
service type)
It's PerSession
http://msdn.microsoft.com/en-us/library/ms733040.aspx
If you use the default instancing behavior in WCF, all calls between a WCF client object are handled by the same service instance. Therefore, at the application level, you can think of a session as enabling application behavior similar to local call behavior. For example, when you create a local object:
A constructor is called.
All subsequent calls made to the WCF client object reference are processed by the same object instance.
A destructor is called when the object reference is destroyed.
Sessions enable a similar behavior between clients and services as long as the default service instance behavior is used. If a service contract requires or supports sessions, one or more contract operations can be marked as initiating or terminating a session by setting the IsInitiating and IsTerminating properties.
I would think D is correct as well, since InstanceContextMode is set to PerSession by default (link).
What is the best possible way to share a single instance of a WCF Service (the SoapClient class) across multiple instances of an application (WPF)?
I need to do this because I need to enable duplex communications with callbacks, so i need to "register the application" to the the service so that other users using the application will get notified whenever a new user logs in.
Btw the below is striked out because I have confirmed that for the notifications to work, the registrants need to register to the same wcf service instance...thus now I need a way to share this instance
I am currently developing an application and I need some way to inform the users that are currently using the application whenever someone logs in the application.
I have tried using the WCF Duplex thing, but and I can't get it to work...and I think the reason behind it is because notifications and subscriptions need to occur to the same instance of the WCF Service.
But since this application will be deployed on multiple users' pcs, I cannot share only one instance of this wcf service eh? (or can I ?)
Is there a way to share a common instance of a wcf service (the SoapClient) for all the users? Or am I going about this the wrong way?
Currently I'm accessing the WCF Service through a class library via a public property that sends a new isntance of the wcf service every time it is accessed, and I think that that is the reason on why the notifications are not working on multiple instances of the application.
The following are the methods (in the class library) that the client application (a wpf app) uses to gain access to the service methods:
public static MusicRepo_DBAccess_ServiceClient GetService(object instanceContext)
{
return new MusicRepo_DBAccess_ServiceClient(new InstanceContext(instanceContext), dualBinding, endpointAddress);
}
public static MusicRepo_DBAccess_ServiceClient GetService()
{
return new MusicRepo_DBAccess_ServiceClient(new InstanceContext(new WSGateway()), dualBinding, endpointAddress);
}
In the main application window, I am then getting a new instance from the above overloaded method passing in this as the instanceContext parameter and the Open it to wait for the notifications but I am never notified when another user logs in from another instance of the application.
This is how I am notifying the registrars (excerpt) in the service login method:
if (!_callbackList.Contains(newUser))
{
_callbackList.Add(newUser);
}
_callbackList.ForEach(c => c.NotifyNewUserOnline(loggedInUser));
The solution was simple. All I needed was to change InstanceContextMode to Single:
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.Single)]