【问题标题】:Accessing PerSession service simultaneously in WCF using C#使用 C# 在 WCF 中同时访问 PerSession 服务
【发布时间】:2012-07-01 03:56:55
【问题描述】:

1.) 我有一个主方法 Processing,它以字符串作为参数,并且该字符串包含一些 x 个任务。

2.) 我有另一种方法 Status,它通过使用两个变量 TotalTests 和 CurrentTest 来跟踪第一种方法。每次都会在第一种方法(处理)中循环修改。

3.) 当多个客户端并行调用我的 Web 服务以通过传递字符串来调用处理方法时,具有不同任务的处理将需要更多时间。因此,同时客户端将使用第二个线程调用 Web 服务中的 Status 方法来获取第一个方法的状态。

4.) 当第 3 点完成时,所有客户端都应该并行获取变量(TotalTests,CurrentTest),而不会与其他客户端请求混淆。

5.) 当我将所有客户端设置为静态时,我在下面提供的代码会混淆所有客户端的变量结果。如果我删除了变量的静态变量,那么客户端只会得到这两个变量的全 0,我无法修复它。请看下面的代码。

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : IService1
{
    public int TotalTests = 0;
    public int CurrentTest = 0;

    public string Processing(string OriginalXmlString)
    {
                XmlDocument XmlDoc = new XmlDocument();
                XmlDoc.LoadXml(OriginalXmlString);
                this.TotalTests = XmlDoc.GetElementsByTagName("TestScenario").Count;  //finding the count of total test scenarios in the given xml string
                this.CurrentTest = 0;
                while(i<10)
                {
                        ++this.CurrentTest;
                         i++;
                }
    }

    public string Status()
    {
        return (this.TotalTests + ";" + this.CurrentTest);
    }
}

服务器配置

<wsHttpBinding>
    <binding name="WSHttpBinding_IService1" closeTimeout="00:10:00"
      openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
      bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
      maxBufferPoolSize="524288" maxReceivedMessageSize="2147483647"
      messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
      allowCookies="false">
      <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
        maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
      <reliableSession ordered="true" inactivityTimeout="00:10:00"
        enabled="true" />
      <security mode="Message">
        <transport clientCredentialType="Windows" proxyCredentialType="None"
          realm="" />
        <message clientCredentialType="Windows" negotiateServiceCredential="true"
          algorithmSuite="Default" establishSecurityContext="true" />
      </security>
    </binding>
  </wsHttpBinding>

客户端配置

<wsHttpBinding>
            <binding name="WSHttpBinding_IService1" closeTimeout="00:10:00"
                openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
                bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                maxBufferPoolSize="524288" maxReceivedMessageSize="2147483647"
                messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                allowCookies="false">
                <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
                    maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
                <reliableSession ordered="true" inactivityTimeout="00:10:00"
                    enabled="true" />
                <security mode="Message">
                    <transport clientCredentialType="Windows" proxyCredentialType="None"
                        realm="" />
                    <message clientCredentialType="Windows" negotiateServiceCredential="true"
                        algorithmSuite="Default" establishSecurityContext="true" />
                </security>
            </binding>
        </wsHttpBinding>

下面提到的是我的客户端代码

class Program
{
static void Main(string[] args)
{
    Program prog = new Program();
    Thread JavaClientCallThread = new Thread(new ThreadStart(prog.ClientCallThreadRun));
    Thread JavaStatusCallThread = new Thread(new ThreadStart(prog.StatusCallThreadRun));
    JavaClientCallThread.Start();
    JavaStatusCallThread.Start();
}

public void ClientCallThreadRun()
{
    XmlDocument doc = new XmlDocument();
    doc.Load(@"D:\t72CalculateReasonableWithdrawal_Input.xml");
    bool error = false;
    Service1Client Client = new Service1Client();
    string temp = Client.Processing(doc.OuterXml, ref error);
}

public void StatusCallThreadRun()
{
    int i = 0;
    Service1Client Client = new Service1Client();
    string temp;
    while (i < 10)
    {
        temp = Client.Status();
        Thread.Sleep(1500);
        Console.WriteLine("TotalTestScenarios;CurrentTestCase = {0}", temp);
        i++;
    }
}
}

谁能帮帮我。

【问题讨论】:

  • @Beygi ----我已按照您的建议进行了修改。请看一下。
  • 您确定您的绑定已启用会话吗?如果没有,请提供客户端和服务器配置。两个电话都在一个会话中吗? - 请提供客户代码。
  • @DmitryHarnitski --- 提供了您要求的完整附加信息。请看一下
  • 每个新客户端都使用新的 sesson,因此 Processing() 在 session1 中运行,而 Status() 在 session2 中运行。创建一个客户端对象并将其用于两种方法
  • @Dmitry Harnitski ---- 客户端是相同的,只有单个客户端会调用这两种方法。

标签: c# wcf session wcf-binding wcf-client


【解决方案1】:

首先因为需要同时访问服务,当服务在处理第一个客户端调用(Processing)时,需要将服务并发模式改为Multiple。

另外你想维护每个客户端的处理状态,所以你需要将实例上下文模式设置为PerSession。

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode= InstanceContextMode.PerSession)]

注意

  • 默认 InstanceContextMode 是 PerSession
  • 默认 ConcurrencyMode 为 Single

您可以执行以下操作以确保您的配置与 PerSession InstanceContextMode 兼容,使用此方法,WCF 将在必要时抛出运行时异常

[ServiceContract(SessionMode=SessionMode.Required)]

注意使用 InstanceContextMode.PerSession,您将为每个创建的代理获得不同的实例

所以每个客户端只需要一个“Service1Client”实例,您将调用它的 Process 方法并从中检索状态。

对于虚拟繁重处理,您可以仅在“处理”方法(服务端)中使用 Thread.Sleep(millisecond) 进行测试建议。

对于客户端应用,如果要调用“Processing”方法,然后使用Status方法获取状态,则需要异步调用Process方法。

1.在solution-explorer中右击服务引用,选择“配置服务引用”,勾选“生成异步操作”,点击确定。

2.像这样更改您的客户端代码

static void Main(string[] args)
{
    StartProcessing();
    StatusReport();

    Console.ReadLine();
}

static ServiceClient Client = new ServiceClient();
private static bool Completed = false;

public static void StartProcessing()
{
    XmlDocument doc = new XmlDocument();
    doc.Load(@"D:\t72CalculateReasonableWithdrawal_Input.xml");
    bool error = false;

    Client.ProcessingCompleted += Client_ProcessingCompleted;
    Client.ProcessingAsync(doc.OuterXml);

    Console.WriteLine("Processing...");
}

static void Client_ProcessingCompleted(object sender, ProcessingCompletedEventArgs e)
{
    // processing is completed, retreive the return value of Processing operation
    Completed = true;
    Console.WriteLine(e.Result);
}

public static void StatusReport()
{
    int i = 0;
    string temp;
    while (!Completed)
    {
        temp = Client.Status();
        Console.WriteLine("TotalTestScenarios;CurrentTestCase = {0}", temp);
        Thread.Sleep(500);
        i++;
    }
}

【讨论】:

  • @Beygi---- 如果我只为每个客户端做一个 Service1Client 实例,那么我无法让线程并行工作。检查我是否得到正确的输出。
  • @krishna555 '更新已成功完成。' :)
  • 我是否需要在服务器(wcf 服务)端进行任何代码更改,才能使上述编写的客户端正常工作?
  • 您在上面提供的客户端代码没有并行执行,因为它首先调用处理,然后在那里等待直到完成,然后给出状态方法的最终结果。
  • @krishna555 不要忘记您需要执行我在此答案中提供的所有步骤,而不仅仅是客户端代码。
【解决方案2】:

PerSession 不会使您的静态变量不在对象实例之间共享。 PerSession 上下文模式所做的唯一一件事就是控制对象的生命周期。

对于PerSession,WCF 在会话结束之前不会销毁服务对象。会话可以由客户端或超时(默认为 10 分钟)显式关闭。来自具有相同会话 ID 的客户端的每个下一次调用都将由 WCF 路由到现有对象。

变量不应是静态的,以防止在不同服务实例之间共享。只要您使用InstanceContextMode.PerSession 和维护会话的绑定,变量的状态就会由 WCF 维护。

public int TotalTests = 0;
public int CurrentTest = 0;

我还会在合同中添加SessionMode.Required,以确保服务配置正确。

 [ServiceContract(SessionMode = SessionMode.Required )]

【讨论】:

  • 我也很累,但这次变量值显示为 0。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多