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?
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 the following problem. There is a WCF service hosted within a windows service like this:
sHost = new ServiceHost(typeof(DataService));
_thread = new Thread(new ThreadStart(sHost.Open));
_thread.Start();
Where DataService is a WCF Service Contract in the solution.
Several layers below the WCF service is a cache in a seperate assembly. But, every time a new connection/proxy to the WCF service is made, a new instance of the service is created. This results in a new instance of the cache being created in the DAL. So what I would like to do is have the WCF service and therefore all classes down the stack instanced once and only once (with some exceptions due to multiplicity requirements). So, the WCF service should be instanced and listen for new connections, rather than have DataService instanced every single time a new connection is made.
I hope this makes sense. How do I do this?
Many thanks,
Fugu
Instantiate DataService yourself and pass the instance to the ServiceHost constructor:
sHost = new ServiceHost(new DataService());
I think alexdej answer is correct, but without seeing your code cannot comment to why you get a Null reference exception.
I can however point you to these videos', I completed these only 2 days ago to help learn WCF and I am sure they will answer your question.
Self hosting WCF - http://channel9.msdn.com/shows/Endpoint/Endpoint-Screencasts-Self-hosting-WCF-Services/
Hosting WCF as a windows service - http://channel9.msdn.com/shows/Endpoint/endpointtv-Screencast-Hosting-WCF-Services-in-Windows-Services/
I have some Service class which is defined as InstanceContextMode.Single, and is well known in the hosting application. (The host creates an instance, and passes that to the WebServiceHost)
Hosting app:WebServiceHost host = null;
SomeService serviceInstance = new SomeService("text", "more text");
host = new WebServiceHost(serviceInstance, baseUri);
Problem:
When I go to use the variables initialised when the service is created (ie, when a call is made to the service), they are either null or empty...
Am I wrong in assuming that as the instance being initialised in the hosting application is used for each request to the WebServiceHost?
Any pointers here would be great.
Your assumption seems correct to me.
Did you put the right code in the service constructor?
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);
}
}
I am very new to web service stuff so please be kind.
I have written a simple POJO class, and deployed it on an axis2 server:
public class Database {
private Project project;
public void login(){
project = new Project();
project.setDescription("Hello there");
project.setName("To me");
}
public Project getProject(){
return project;
}
}
I call the service from a c# client:
localhost.Database db = new WindowsFormsApplication1.localhost.Database();
db.login();
localhost.getProjectResponse pr = new WindowsFormsApplication1.localhost.getProjectResponse();
pr = db.getProject();
When I debug the response is null.
At the java end, when I call getProject, the project object is null.
What's happening?
How do I preserve the state of project between service calls?
For most toolkits, web services are stateless by default. I think axis is no different.
If you want to maintain state between calls then you will need to enable sessions. An example on how to maintain sessions in axis can be found at:
http://kickjava.com/src/test/session/TestSimpleSession.java.htm
On the .NET side you will need to assign a CookieContainer to your request to store the session identifier. See HOW TO: Use CookieContainer to Maintain a State in Web Services for more information.
I think your code would look something like this:
localhost.Database db = new WindowsFormsApplication1.localhost.Database();
// Assign the CookieContainer to the proxy class.
db.CookieContainer = new System.Net.CookieContainer();
db.login();
localhost.getProjectResponse pr = new WindowsFormsApplication1.localhost.getProjectResponse();
pr.CookieContainer = db.CookieContainer;
pr = db.getProject();
I think that should let you do what you want -- but I wouldn't recommend it.
Designing service interfaces is a bit different than designing object oriented interfaces. Service interfaces typically eschew the use of state and instead require the consumer to provide all of the relevant information in the request.
From Service-Oriented Architecture:
Services should be independent,
self-contained requests, which do not
require information or state from one
request to another when implemented.
I would definitely recommend reading that article and perhaps revisiting your design.
I'm not sure why #shivaspk left a comment instead of writing an answer, it is quite correct: web service calls (not just axis calls) are meant to be stateless, so although the project object gets created by
db.login();
when you call
db.getProject();
It is being called on a different instance of your Database class that was created by Axis to service the second call.
There is no really good answer to your question, except for you to rethink what you are trying to do. If you need some kind of authentication (via login), then that authentication needs to be part of every web service call.