【问题标题】:C# WCF service - Access FoxPro DLL in multiuser scenarioC# WCF 服务 - 在多用户场景中访问 FoxPro DLL
【发布时间】:2013-06-01 03:48:28
【问题描述】:

我计划将 WCF-Console-Application 作为 Windows 服务运行。 WCF-Console-Application 调用 FoxPro-DLL 来访问 FoxPro-DBF 中的数据(读取和写入)。 不同的客户端(WPF-Application)应该使用 Console-Application 的 WCF-Service 来显示和编辑来自 FoxPro-DBF 的数据。

如果一次只有一个客户端调用 WCF-Console-Application,则一切正常。但是 WCF-Console-Application 不能正确处理来自多个客户端的并行调用。

WCF-Console-Application 由以下类组成:

  • main Class:派生自ServiceBase,既可以从控制台调用,也可以作为服务启动

    公共类服务: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:安装Service,派生自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);                                     
    }
}
  • 用于访问 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 为 WPF 应用程序生成 output.config 和 TestServer.cs。

FoxPro-DLL 是作为多线程 COM 服务器构建的,它已正确注册并使用了 VFP9T.DLL。函数 loadData() 有一点延迟,用于测试并行调用。当我从多个 FoxPro 实例调用 DLL 时,一切都按预期工作。如果我将 dll 包含到 WPF 应用程序中并从那里调用它,它也可以工作。只有通过 WCF-Console-Application 进行多次调用,它才能正常工作。

当我通过控制台运行 WCF-Console-Application 并在 TestServer.LoadData() 中的每一行之前创建一个 Console.WriteLine 时,第二个调用将 hangig 保持在前面

DataAccess.accessData.CTABLE = "patient";

直到第一次通话结束。最糟糕的是:当我进行并行调用时,返回的 XML 字符串是相同的。在使用 ID 进行第一次通话后,我立即使用不同的 ID 开始第二次通话。对于这两个调用,我都得到了带有第二个 ID 的 XML-String。

为了让 WCF-Console-Application 内的 FoxPro-DLL 并行调用正常工作,我可以改变什么?我尝试了 InstanceContextMode 和 ConcurrencyMode 的每种组合,但没有成功。我需要线程安全吗?如果是这样,我需要改变什么? 此项目不能使用 ODBC 或 SQL-Server。

感谢您的任何建议和建议!

编辑:如果我重新启动 WCF-Console-Application,则只有第一个并行测试会提供错误的 XML 字符串。如果我进行第二次并行调用,则返回的 XML 字符串是正确的。但是并行调用的问题依然存在。

【问题讨论】:

    标签: c# wcf dll foxpro multi-user


    【解决方案1】:

    BasicHttpBinding 不支持会话。您需要使用InstanceContextMode.PerCall 来确保每个调用都在自己的线程中运行。

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class TestServer : ITestServer
    {
       ...
    }
    

    更新:

    DataAccess.accessData static?如果是 - 那需要改变。静态数据在多个线程之间共享。因此,如果使用InstanceContextMode.PerCall,服务可以返回由另一个调用更新的数据。 ConcurrencyMode.Single 仍然可以用于可变静态数据,但您会遇到单线程的所有可伸缩性问题。

    【讨论】:

    • BasicHttpBinding 仅用于通过 svcutil.exe 获取有关 WCF 服务的信息。如果我删除 BasicHttpBinding 的端点并将 InstanceContextMode 更改为 PerCall,则没有任何变化。行为仍然相同。编辑:如何在服务器端检查使用了哪个绑定?
    • 确实,DataAccas.accessData 是静态的。如果我改变这个,一切都会完美!谢谢!
    • 挺尴尬的,但是并行数据访问又不行了。我使用两个 ServiceContract(文件传输、外部 exe 启动)扩展了 WCF 服务。我为接口制作了两个新的 CS 文件和两个带有部分类的附加 CS 文件。两者都按预期工作,但不知何故并行数据访问中断了。我不确定它是否与扩展有关,因为如果我用上面的代码制作一个空白项目,它就不能正常工作。没有静态变量,它仍然是 PerCall。这是否取决于我如何将 dll 包含到我的项目中?
    猜你喜欢
    • 1970-01-01
    • 2011-07-03
    • 1970-01-01
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多