ASMX Web Service Method Singleton - c#

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

Related

Threading issue in ASP.NET Core

I have an ASP.NET Core Web API project. That has one controller with a method called GetLocations
GetLocations connects to 5 other web services on the internet. Gathers some info and return a collection via json. In this method I am caching the data every 5 mins using In Memory caching.
If the cache expires, it tries to connect to all 5 services and get the info and so on.
My problem is:
I have a lot of users requesting this data constantly, 50 requests a second to this API.
When the cache expires I believe there is some kind of thread locking. I have limited visibility into the project at the moment but I suspect that all these requests are calling the method and reaching out to the 5 dependent services until one of them gets a completed response from all 5.
Is my assumption right? If so how can I go about fixing this? Will I need to make each call to the web services async? Will that help this scenario? I am not 100% sure because the requests are what triggers the method call.
You should definitely make the calls to the external services use Async / Await.
That's just a given - as the best practice is to always use async for I/O heavy operations (such as calling a third-party service).
Now, you should also create a class that manages these calls. You can add it as a Singleton in your IoCConfig. In that class, make sure you're "locking" to avoid the issue you just described and not call the underlying services numerous times while the cache is being built.
Check here:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement
You are facing this issue because of following reason.
You are using Cache and it will expire at some definite time.
After Cache expire you call external web service method to collect the data. Now at this point of time it might happen that other request in queue get choose for execution.
Once that another request is chosen for execution it also end up checking cache and it now data in cache then execute external service and so on for other request.
Solution to this.
First Check cache contains data or not.
If Not create lock so following section only be executed by single thread.
Now in that lock section again check for cache and if cache contains data then simply return but it does not contains then call external service.
At this point of time if another thread get selected for execution then it has to wait for execlsive section to complete its works.
Once that section get completed it store data in cache and so after if any queued or new request is there it choose data from cache.
Note : It should something like this.
public List<string> GetData()
{
if(Cache[key] == null)
{
lock(obj) // obj should be static
{
if(Cache[key] == null)
{
// Load data from service
Cache[key] == data;
}
}
}
return (List<string>)Cache[Key];
}

State inside self-hosted WCF service being lost with InstanceContextMode.Single

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

How to create a WCF Service that has an always running component?

Half a day of googling suggests, that it's a bit niche topic, and my question is quite specific. I'm using: VS2013, .NET 4.5, IIS 8.5
I have a ASP.NET website that needs to query a data source. Opening the data source is costly, but I can keep it open indefinitely.
My idea was: create a Command Line application or a Windows Service that will open the data source and then expose the querable objects to the ASP.NET website.
I don't like the idea of having this unmanaged (CommandLine) or managed apart from website (WinService) application that I have to deploy completely separately.
I've read that it is possible to create an always running WCF service hosted in IIS. I would like it to keep a list of object instances that would be returned as a result of a WCF call. Is that at all possible? If yes, how?
I've tried setting the WCF service AppPool to AlwaysRunning, enabling autostart on service application and I can access the service, but a simple test shows, that the service object is created every time anew:
public class MyService : IMyService{
{
private int _counter;
public int Test(){ return _counter++; }
}
My website creates a MyServiceClient from service reference and calls test - it returns 0 every time.
I've also found, that if I create any class in my WCF service application, I cannot access it from inside MyService methods. I can access though classes referenced from other projects. Why is that?
I think you're looking for a singleton service. By default the ServiceBehaviorAttribute.InstanceContextMode is set to PerSession. Instead set it to Single. Every client will then connect to the same instance of the service.
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class MyService : IMyService{
{
private int _counter;
public int Test(){ return _counter++; }
}
Personally, I prefer the singleton approach over static as discussed in here

How can I run a WCF service constructor once for all clients when it's InstanceContextMode is set to PerSession?

How can I run the code in the constructor of a WCF Service only once when the ServiceBehaviorAttribute.InstanceContextMode is set to PerSession?
[ServiceBehavior(
InstanceContextMode = InstanceContextMode.PerSession,
ConcurrencyMode = ConcurrencyMode.Single)]
public class SomeService : ISomeService
{
public SomeService()
{
this.RunThisOnceForAllClients();
}
private void RunThisOnceForAllClients() { }
}
Or, how can I make a method run automatically once the WCF Service is running but it will only run once for all client calls?
Please help. Thanks in advance.
I deploy my WCF Service using a Managed Windows Service. My code is in C#. Framework is in .NET 4. Project is build in Visual Stuido 2010 Professional. The service is consumed by a Windows Forms Application. In case you wonder on why do I need to do it like this, I need to execute an Uploader method that will upload the database of the service to another service, but it will be executed in a certain time so I put it in another thread that will always run as long as the service is running.
Why not run this operation just before you host the WCF Service in your windows service so it can be ready as soon as the WCF Service goes online. You can get from the running thread an event that it is finished and then deploy the WCF Service.
You need to write a service behavior or an endpoint behaviour. In this behaviour call the function at first call from a client and set a variable to true and store it in some permament memory or file location.
You may have a look at the following msdn article about Extending WCF
Use a static constuctor? It will be called once when(before) any action with that class is taken in your code.

Why is my Winforms-hosted WCF service single threaded?

I have a WCF service that I'm using to replace an old ASP.NET web service. The service appears to be working fine but it is unable to handle simultaneous requests for some reason. My implementation of the service has the following properties:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class HHService : IHHService
My host declaration looks like this:
baseAddress = new Uri("http://0.0.0.0:8888/HandHeld/");
host = new ServiceHost(typeof(HHService), baseAddress);
ServiceMetadataBehavior behavior;
behavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (behavior == null)
{
behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(behavior);
}
host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
host.AddServiceEndpoint(typeof(IHHService), new BasicHttpBinding(), "HHService.asmx");
HHService.LogMessage += new EventHandler<HHService.LogMessageEventArgs>(HHService_LogMessage);
host.Open();
The service runs and returns correct results, but if two clients try to make a call at the same time one client will block until the other is finished rather than the calls executing together. I'm not using any configuration files. I'm trying to do everything programmatically. Do i have something setup incorrectly that's causing this behavior? I've run other services using the NetTCPBinding without this problem.
EDIT:
In response to John Saunders:
I'm not familiar with any ASP.NET compatibility mode. I'm not using any session state the service is stateless it just processes requests. Aside from the implementation of the actual methods everything else I've done is in the code listed here.
Possible Solution:
I was calling the host.Open() function from the form_load event of the main form. I moved the call to a separate thread. All this thread did was call host.Open() but now the service appears to be behaving as I would expect.
If your instance context mode is PerCall, then your server is always single-threaded, since by definition, every call gets a new server instance.
This works okay in a IIS environment, where IIS can spin up several server instances to handle n concurrent callers, one each as a single-threaded server for each incoming request.
You mention in one of your comments your hosting your WCF inside a forms app - this might be a design decision you need to reconsider - this is not really optimal, since the Winforms app cannot easily handle multiple callers and spin up several instances of the service code.
Marc
Is there a lock somewhere in your service function?
Are you using ASP.NET compatibility mode? Session state?
My next question would be: what makes you think it's single-threaded? How did you determine that, and what test do you use to prove that you have not solved the problem? Could be a false positive.
This is answered in another question:
[ServiceBehavior(UseSynchronizationContext = false)]
WCF in Winforms app - is it always single-threaded?

Categories