I plan to run a WCF-Console-Application as a Windows service. The WCF-Console-Application calls a FoxPro-DLL to access data in FoxPro-DBF (read and write). Different clients (WPF-Application) should consume the WCF-Service of the Console-Application to display and edit the data from the FoxPro-DBF.
If only one Client at a time calls the WCF-Console-Application, everything works fine. But the WCF-Console-Application does not handle parallel calls from multiple clients correctly.
The WCF-Console-Application consists of these classes:
main Class: derived from ServiceBase, can be either called from console or started as service
public class Service : ServiceBase { public ServiceHost serviceHost = null;
const string CONSOLE = "console";
public Service()
{
this.ServiceName = "ServiceTest";
}
public static void Main(string[] args)
{
if (args.Length == 1 && args[0].Equals(CONSOLE))
{
new Service().startConsole();
}
else
{
ServiceBase.Run(new Service());
}
}
private void startConsole()
{
Console.WriteLine(string.Format("{0}::start Service...", GetType().FullName));
OnStart(null);
Console.WriteLine(string.Format("{0}::ready (ENTER to stop)", GetType().FullName));
Console.ReadLine();
OnStop();
Console.WriteLine(string.Format("{0}::stop Service", GetType().FullName));
}
protected override void OnStop()
{
if (this.serviceHost != null)
{
this.serviceHost.Close();
this.serviceHost = null;
}
}
protected override void OnStart(string[] args)
{
if (this.serviceHost != null)
{
this.serviceHost.Close();
}
this.serviceHost = new ServiceHost(typeof(Server.TestServer));
this.serviceHost.Open();
}
ServiceInstaller: installs the Service, derived from Installer
[RunInstaller(true)]
public class InstallService : Installer
{
public InstallService()
{
process = new ServiceProcessInstaller();
process.Account = ServiceAccount.LocalSystem;
service= new ServiceInstaller();
service.ServiceName = "ServiceTest";
service.Description = "ServiceTest";
service.DisplayName = "ServiceTest";
service.StartType = ServiceStartMode.Automatic;
Installers.Add(process);
Installers.Add(service);
}
}
public static class DataAccess
{
public static foxprotest.foxprotest accessData = new foxprotest.foxprotest();
}
[ServiceContract(Namespace = "http:/localhost.TestServer", SessionMode = SessionMode.Allowed)]
public interface ITestServer
{
[OperationContract]
String loadData(int id);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TestServer : ITestServer
{
public String loadData(int id)
{
//set table
DataAccess.accessData.CTABLE = "patient";
//set ID
DataAccess.accessData.NPATIENT = id;
//fetch the data
DataAccess.accessData.FetchData();
//return data as XML
return DataAccess.accessData.CRESULT;
}
}
This is how the App.Config looks like: the binding is set to netTcpBinding
<system.serviceModel>
<services>
<service name="Server.TestServer" behaviorConfiguration="MyFileServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8888/"/>
<add baseAddress="net.tcp://localhost:52/"/>
</baseAddresses>
</host>
<endpoint address="TestServer" binding="basicHttpBinding"
name="b" contract="Server.ITestServer" />
<endpoint address="TestServer" binding="netTcpBinding"
name="c" contract="Server.ITestServer" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyFileServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceThrottling maxConcurrentCalls="80" maxConcurrentSessions="80"
maxConcurrentInstances="80" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
I used svcutil.exe to generate the output.config and TestServer.cs for the WPF-Application.
The FoxPro-DLL is built as multi-threaded COM server, it is registered correctly and VFP9T.DLL is used. There is a little delay in the function loadData(), to test the parallel calls. When I call the DLL from multiple FoxPro-Instances, everything works as expected. If I include the dll into the WPF-Application and call it from there, it also works. Only with multiple calls through the WCF-Console-Application, it does not work correctly.
When I run the WCF-Console-Application through console and make a Console.WriteLine before every line in TestServer.LoadData(), the second call keeps hangig in front of
DataAccess.accessData.CTABLE = "patient";
until the first call is finished. The worst thing is: the returned XML-string is the same when I make parallel calls. Immediately after the first call with an ID, i start a second call with a different ID. For both calls I get the XML-String with the second ID.
What can I change, to get the parallel call to the FoxPro-DLL inside the WCF-Console-Application working? I tried every combination of InstanceContextMode and ConcurrencyMode, without success. Do I need thread safety? If so, what do I have to change? The use of ODBC or a SQL-Server are no option for this project.
Thanks for any suggestion and advice!
EDIT: If I restart the WCF-Console-Application, only the first parallel test delivers wrong XML-strings. If I do a second parallel call, the returned XML-strings are correct. But the problem with the parallel call still exists.
BasicHttpBinding
does not support sessions. You need to use InstanceContextMode.PerCall
to make sure that every call is run in its own thread.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TestServer : ITestServer
{
...
}
Update:
Is DataAccess.accessData
static
? If yes - that needs to be changed. Static data is shared across multiple threads. Therefore service can return data updated by another call if InstanceContextMode.PerCall
is used. ConcurrencyMode.Single
still can be used with mutable static data but you will get all scalability issues of single thread.