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.
Related
I am using WCF service and self hosting it as not everything is contained within the service itself (some external events are happening outside of the service):
WCF Service and I am self hosting it in a C# Console App. When WCF clients conncet they call the Login function, and I (try!) to store their callback via GetCallbackChannel
3rd party DLL which calls my console back via a delegate on a different thread from the library
On this console callback I then call in to the WCF service who pool which is then passed on to the WCF service who then broadcasts to all connected clients via a callback contract.
All is fine with the client connecting, calling Login, and I save the callback interface object.
However when I access the code from my service, i find it is an entirely new object and my _endPointMap is empty (despite me storing it in the Login method which is called by the client):
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service : IService, IEndpointNotifier
{
public readonly TwoWayDictionary<string, IClientCallback> _endpointMap = new TwoWayDictionary<string, IClientCallback>();
// called by WCF client when they click the login button - it works
public void Login(string username)
{
var callback = OperationContext
.Current
.GetCallbackChannel<IClientCallback>();
_endpointMap.AddOrUpdate(username, callback);
list.Add(username);
}
// called by the WCF self-host console app
public void IEndpointNotifier.Notify(string info, string username)
{
// at this point my list _endpointMap is empty despite
// having received a Login previously and adding to the
// list. so i am unable to call my clients back!!
_endPointMap.Count(); // is 0 at this point?!!
}
}
My main console app starts up the service fine also as below:
static void Main(string[] args)
{
var service = new Service();
var host = new ServiceHost(service);
// between the above line and the task below calling
// service.Notify I click a number of times on client
// which calls the Login method
Task.Run(() =>
{
for (var i = 0; i < 3; i++)
{
Thread.Sleep(10000);
// at this point, service seems like a new object?!
// any data saved in the list member will be lost,
// and will only see the single entry from time of
// construction
service.Notify("hi","bob");
}
});
Console.ReadLine();
}
Questions please
The object seems totally different to the one that was modified in a previous operation (on login from client) - is there any way to tell what service object I am actually looking at (equivalent to the old C++ days and looking at the address pointer for this)?
The singleton attribute seems to be ignored [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)] Any ideas what I am doing wrong (why the internal list variable keeps getting reset)?
The WCF service cannot be self contained. How does one achieve communication between WCF self-hosted app and the WCF service according to best practice or is this abusing WCF and what is was designed for (when considering scopes etc)?
I set the breakpoint in the constructor with some dummy values. That breakpoint is only ever hit the first time when i construct it. When i go in to the object via the service.Notify method although the object seems new (members are empty) the constructor breakpoint is not hit - how so?
I have hosted the 3rd party app behind a static global member variable that I control. So I am responsible for all communication and state and cleanup between the 3rd party lib and the normal wcf calls. I am responsible for thread lifetimes for the 3rd party app. If I create them I have to close them. Holding references in my own lists.
It is like it is a separate app but they just happen to be in the same process space. All communication to and from the 3rd party app is controlled by me formally.
You will probably need a thread that looks for completed or abandoned 3rd party objects after usage to kill them your self outside of normal wcf msg processing.
This lets the wcf part be a normal threaded (thread pool) concept with no special declarations.
side note:
I would take out the loop and make it two lines in your simple model.
service.Notify("hi")
Console.ReadLine();
This will expose your object lifetime details instead hiding them for 3 seconds.
I found why the values were not being saved... my WCF client proxy was connecting to the VS WCF Service Host and not my host in the code!
I noticed this when I saw the WCF Service Host running in the service bar tray.
I disabled WCF Service Host starting up for the WCF .svc service by right clicking on the WCF Project -> Properties -> WCF Options -> unticked Start WCF Service Host when debugging another project in the same solution
I have a WCF service that holds local data members.
When the service get a call from the client the data members value is null.
The only way I find to solve it is to set the data members to static.
Can someone explain why the members get null value and if there another way to solve this issue.
Probably because of the InstanceContextMode you are using, or the one set by default (which is per session).
If you set the InstanceContextMode to Single (or Per Session if polled by the same client instance), only one instance of your WCF service will be instantiated and thus your instance members will be intact unless they are changed in your code somehow.
This instance will be accessed by the clients depending on the concurrency mode.
Check this article for more information:
http://www.codeproject.com/Articles/86007/3-ways-to-do-WCF-instance-management-Per-call-Per
You have the modes :
Per call
Per Session
Single
I want to create a web-service that run a specific method on startup.
this is the service's interface:
namespace MyClass
{
[ServiceContract]
public interface IService
{
[OperationContract]
string getData();
}
}
and on the service itself i want a specific method (not one of those) to run when the service loads (or deployed to IIS). is there a way to do so?
You need to be clear what really happens when a WCF service is hosting in IIS.
IIS provides a service host that loads on demand
when a request comes in, IIS instantiates the service host, which in turns instantiates an instance of your service class, passes it the parameters from the request, and then executes the appropriate method on the service class
As such, there is no point in time when the "service loads" and then just lingers around in memory. The "service" isn't just loaded when IIS starts up and then would be "present and ready" at all times...
So where do you want to plug in??
when the service host loads in IIS? In that case, you'd have to create your own custom service host and register it with IIS so that IIS would use your custom host instead of the WCF default service host
when the actual service class is instantiated to handle the request? THen put your logic into the constructor of your service class - it will be executed each time a service class is instantiated to handle a request
Though this might not be exactly what you want, you could use the class's constructor, perhaps:
public class Service : IService
{
public Service()
{
//code here will execute when an instance
//of this service class is instantiated
}
string getData() { ... }
}
It would be more clear if you could inform us of the method you wish to call, and any surrounding information about it, so that you don't get ill-advice. Specifics are nice.
Here's where I put some code in order to get (and cache) data on the webservice start (in VB). You do need to trigger the service by navigating to any valid or invalid
Public Module WebApiConfig
Public Sub Register(ByVal config As HttpConfiguration)
'Run this method on startup to cache the addresses
Address.GetAll()
config.Routes.MapHttpRoute(
name:="DefaultApi",
routeTemplate:="api/{controller}/{id}",
defaults:=New With {.id = RouteParameter.Optional}
)
End Sub
End Module
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).
Not sure if this is the right terminology, let me explain what I want.
I have a web service that's available on the network - the web service has 1 web method.
What I want is... if the web service is running and performing tasks and another call is made to this web service, I want the 2nd call to fail or pend for a certain period of time then fail. Because only 1 instance of this web service should be called at once.
I was thinking of writing a value to the application object (like in asp.net) but then I have to be very careful to make sure that this value gets updated, in case of any errors, it might not... so this is dangerous, and would leave the web service in a state where no one can get to it.
Is there not a more dynamic way to determine if the web service is getting called or not?
You cannot do this with legacy ASMX web services. They have no support for different instance schemes.
I believe you can do this with WCF, as you can configure the service to have only a single instance.
If you are using WCF, this is simple. Use the service throttling settings to specify that you want MaxConcurrentCalls = 1 and MaxInstances = 1. You'll also want to set the ConcurrencyMode to Single for your ServiceBehavior.
I dont know much about web services on whether you can configure a web server to only start 1 instance of your web service, but you could try creating a mutex within your web service.
A Mutex is an interprocess synchronization object which can be used to detect if another instance of your web service is running.
So, what you can do is create a mutex with a name, then Wait on it. If more than 1 instance of your web service is alive, then the mutex will wait.
You could implement the check inside of the webmethod since it will be running in the same IIS process
You could create a poor man's mutex and have the first instance create a file and have consecutive instances check the existence of the file. Try Catch your web method and place the deletion of the file in the finally.
If you are WCF I recommend "bobbymcr" answer, but for legacy web service you can use Monitor instead or mutex as mutex is costly (because it is a kernel object) but if you do not care about performance and responsiveness of the service use the Mutex simply.
See this sample for using Monitor class
private static object lockObject = new object();
public void SingleMethod()
{
try
{
Monitor.TryEnter(lockObject,millisecondsTimeout);
//method code
}
catch
{
}
finally
{
Monitor.Exit(lockObject);
}
}