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

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

Related

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 would I add a WCF service to an existing winforms application?

I have a winform application that handles some data entry and billing. I'd like to add a WCF service that is accessible over the local LAN only. I'd like my billing program to query my database and fetch some data for the client. It is important that this be done in the -same- program instead of creating another.
My question is it difficult to setup a WCF service like this where I'm starting from an existing winform application instead of creating a fresh WCF service. Is it a simple matter of putting the right using directives or is something else fundamentally missing since I didn't set it up as a WCF service from the get go?
Another concern is do I need to worry about threading or is that automatically handled by the WCF service? For instance, if 10 computers all query my winforms application at the same time will WCF seamlessly handle that or I do I need to implement additional functionality to handle this?
Thanks for reading
Basically, to create a WCF service, you need three things:
a service contract (typically expressed as a .NET interface) to define the methods the service provides. This also includes what datatypes the methods expect (and possibly return)
[ServiceContract(Namespace="http://services.yourcompany.com/Service/2012/08")]
interface IMyService
{
[OperationContract]
SomeReturnType ThisIsYourMethod(string input, int value, .....);
}
[DataContract(Namespace="http://data.yourcompany.com/Service/2012/08")]
public class SomeReturnType
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
a service implementation that creates the actual service code to be called. This is just a plain .NET class the implements the service contract
public class MyServiceImplementation : IMyService
{
SomeReturnType ThisIsYourMethod(string input, int value, .....)
{
/// .... do some processing, fetch data etc.
return ......
}
}
a service host to actually host the WCF runtime and spin up the whole WCF processing; this is a ServiceHost instance (or derived class) that will be able to host your service. This class needs to be instantiated and opened somewhere in the startup process of your Winforms application. Once the service host is open, your services are available to be called from the outside world. You'll need to make sure to close the service host when your Winforms application is closing down.
and you might need - in addition - some configuration settings in your app.config file to define what endpoints (address, binding, contract) your WCF service offers up to the world.
So this is really quite simple - just create those items in your Winform project, and you're done.
Please look at this article Hosting and Consuming WCF Services
Windows service hosting the WCF ServiceHost (example from this article)
using System;
using System.ServiceModel;
using System.ServiceProcess;
using QuickReturns.StockTrading.ExchangeService;
namespace QuickReturns.StockTrading.ExchangeService.Hosts
{
public partial class ExchangeWindowsService : ServiceBase
{
ServiceHost host;
public ExchangeWindowsService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Type serviceType = typeof(TradeService);
host = new ServiceHost(serviceType);
host.Open();
}
protected override void OnStop()
{
if(host != null)
host.Close();
}
}
}
Another concern is do I need to worry about threading or is that
automatically handled by the WCF service? For instance, if 10
computers all query my winforms application at the same time will WCF
seamlessly handle that or I do I need to implement additional
functionality to handle this?
I think wcf will easily handle this load. But it depends on operations that you want to do on it.
For services hosted as windows services and web-services, your clients also need a proxy class to gain access to exposed contract members.

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.

How can I use web service from Silverlight app?

I am trying to get data form a web service inside a silverlight app. Unfortunately the silverlight app (Bing map app) just hangs when trying to connect.
I use the same code in a console app and it works just fine.
Is there anything special I need to do in silverlight to get it to work? I don't get any exceptions - it just hangs.
I based my service and client code off of this example
http://www.switchonthecode.com/tutorials/wcf-tutorial-basic-interprocess-communication
Problems and Questions:
1. Why can't I set breakpoints in my sliverlight code?
2. How can I successfully call WCF service from a silverlight app? (links to SIMPLE working examples would be great - all the ones I seem to find seem to be quite advanced (RIA, Duplex, etc) Many of these also show xml and other non C# "code" - frankly I don't know what those do and how they relate to the projects, code and services.
(Clearly I am quite ignorant about WCF and silverlight)
As per request for code:
[ServiceContract]
public interface ILGSMapServer
{
[OperationContract]
List<double> GetLatitudes();
}
public class TreeWorkClient
{
ChannelFactory<ILGSMapServer> httpServer;
public ILGSMapServer httpProxy;
public TreeWorkClient()
{
httpServer = new ChannelFactory<ILGSMapServer>(new BasicHttpBinding(), new EndpointAddress("http://localhost:8000/GetLatitudes"));
httpProxy = httpServer.CreateChannel();
}
public List<TreeWorkItem> GetLocations()
{
List<double> lats = httpProxy.GetLatitudes();
//... do stuff in code
return ret;
}
}
I agree with John Saunders - it would be easier to answer this if you published the client code.
However as a guess, a common problem with calling services from Silverlight applications is the restriction Silverlight puts on cross domain calls.
In summary, if your service is at a different domain from the site-of-origin of the Silverlight application, you need to create a client access policy file at the service location.
See this for details:
http://msdn.microsoft.com/en-us/library/cc197955(v=vs.95).aspx
Given your example code you should be seeing the
System.InvalidOperationException: The contract 'ILGSMapServer'
contains synchronous operations, which are not supported in
Silverlight. Split the operations into "Begin" and "End" parts and set
the AsyncPattern property on the OperationContractAttribute to 'true'.
Note that you do not have to make the same change on the server.
You'd need to change your service contract to the following
[ServiceContract]
public interface ILGSMapServer {
[OperationContract( AsyncPattern = true )]
IAsyncResult BeginGetLatitudes( AsyncCallback callback, object context );
List<double> EndGetLatitudes( IAsyncResult result );
}
This also means you'll need to do something completely different in your GetLocations() function as this function will return before the results from the Web have been returned.
Try taking a look at the examples here.
Other options involve using the "Add Service Reference" rather than manually defining it in code.
I believe you need to have this attribute on WCF service for SL to consume it:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
As for debugging - you can debug Silverlight, try using IE for that, its most natural browser for SL debugging (sadly).
Once you start debugging it will be more clear whats wrong when you catch cross domain exception or some other.

ASMX Web Service Method Singleton

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

Categories