CF WCF сервис - доступ к FoxPro DLL в многопользовательском сценарии
Я планирую запустить приложение WCF-Console в качестве службы Windows. WCF-Console-Application вызывает FoxPro-DLL для доступа к данным в FoxPro-DBF (чтение и запись). Различные клиенты (WPF-приложение) должны использовать WCF-сервис консольного приложения для отображения и редактирования данных из FoxPro-DBF.
Если только один Клиент одновременно вызывает Консольное приложение WCF, все работает нормально. Но консольное приложение WCF не обрабатывает параллельные вызовы от нескольких клиентов правильно.
Консольное приложение WCF состоит из следующих классов:
Основной класс: производный от ServiceBase, может быть вызван из консоли или запущен как служба
открытый класс 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: устанавливает Сервис, полученный из Установщика
[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);
}
}
- статический класс для доступа к FoxPro-DLL
public static class DataAccess
{
public static foxprotest.foxprotest accessData = new foxprotest.foxprotest();
}
- ITestServer, интерфейс с ServiceContract
[ServiceContract(Namespace = "http:/localhost.TestServer", SessionMode = SessionMode.Allowed)]
public interface ITestServer
{
[OperationContract]
String loadData(int id);
}
- TestServer, внедрение ITestServer
[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;
}
}
Вот так выглядит App.Config: для привязки установлено значение 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>
Я использовал svcutil.exe для генерации файла output.config и TestServer.cs для WPF-приложения.
FoxPro-DLL построен как многопоточный COM-сервер, он правильно зарегистрирован и используется VFP9T.DLL. Есть небольшая задержка в функции loadData(), чтобы проверить параллельные вызовы. Когда я вызываю DLL из нескольких экземпляров FoxPro, все работает как положено. Если я включаю dll в WPF-приложение и вызываю его оттуда, это также работает. Только с несколькими вызовами через WCF-Console-Application, это не работает правильно.
Когда я запускаю WCF-Console-Application через консоль и создаю Console.WriteLine перед каждой строкой в TestServer.LoadData(), второй вызов сохраняет зависание перед
DataAccess.accessData.CTABLE = "patient";
пока не закончится первый звонок. Хуже всего то, что возвращаемая XML-строка одинакова, когда я выполняю параллельные вызовы. Сразу после первого звонка с идентификатором я начинаю второй звонок с другим идентификатором. Для обоих вызовов я получаю строку XML со вторым идентификатором.
Что я могу изменить, чтобы параллельный вызов FoxPro-DLL внутри WCF-Console-Application работал? Я попробовал каждую комбинацию InstanceContextMode и ConcurrencyMode, но безуспешно. Нужна ли мне безопасность ниток? Если так, что я должен изменить? Использование ODBC или SQL-Server не подходит для этого проекта.
Спасибо за любые предложения и советы!
РЕДАКТИРОВАТЬ: Если я перезапущу консольное приложение WCF, только первый параллельный тест доставит неправильные XML-строки. Если я сделаю второй параллельный вызов, возвращенные XML-строки верны. Но проблема с параллельным вызовом все еще существует.
1 ответ
BasicHttpBinding
не поддерживает сессии. Вам нужно использовать InstanceContextMode.PerCall
чтобы убедиться, что каждый вызов выполняется в своем собственном потоке.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TestServer : ITestServer
{
...
}
Обновить:
Является DataAccess.accessData
static
? Если да - это нужно изменить. Статические данные совместно используются несколькими потоками. Поэтому сервис может вернуть данные, обновленные другим вызовом, если InstanceContextMode.PerCall
используется. ConcurrencyMode.Single
все еще может использоваться с изменяемыми статическими данными, но вы получите все проблемы с масштабируемостью одного потока.