webservice -是否有可能将每个客户端的服务隔离

本文关键字:客户端 服务 隔离 是否 有可能 webservice | 更新日期: 2023-09-27 18:17:56

我有一个遗留的System.Web.Services.WebService(不是WCF),我必须维护。

我偶尔会遇到一些奇怪的行为,我将其描述为竞争条件。

  • 服务挂起,需要重新启动

  • 有时我得到这个异常:

    System.NotSupportedException: Multiple simultaneous connections
    or connections with different connection strings inside the same 
    transaction are not currently supported.
        at MySql.Data.MySqlClient.MySqlConnection.Open()
        ...
    
我知道根本原因是什么。该服务利用一个与mysql对话的库,并没有考虑到web服务。不幸的是,我不能改变这个库。

一个webmethod示例如下:

[WebMethod(EnableSession = true)]
public void DoSomething()
{
    var login = this.Session["login"] as LoginDetails;
    ExternalLib.SetLoginData(login.Schema, login.User, login.Pass);
    ExternalLib.PerformTask();
}

这里的问题是:

  • ExternalLib.SetLoginData只是设置了一些全局变量
  • ExternalLib.PerformTask执行数据库调用,其中一些在事务中。
  • 过程如1. Create MySqlConnection or take it from cache 2. Create MySqlCommand 3. Execute Command 4. Dispose command

客户端a)调用DoSomething(),我初始化他的连接。他的工作完成了一半,客户端b)调用DoSomething(),这显然改变了客户端a的登录数据,事务内部的下一个调用将使用客户端b)的登录,从而导致事务。

无论如何,我知道这是一个糟糕的设计,但我的问题是如何解决这个问题。目前(因为我只有10个客户端),我在一个不同的端口上创建了一个专用的网站,所有这些端口都指向同一个根目录,但这是一个尴尬的解决方案。

也许有可能在其on域内运行每个会话。任何建议。如果我正确理解这个页面的WCF是默认行为:http://msdn.microsoft.com/en-us/magazine/cc163590.aspx


按服务每呼叫服务是Windows通信基础默认实例化模式。当服务类型为配置为每个调用激活,服务实例,公共语言运行时(CLR)对象,仅在客户端调用在时存在的进步。每个客户端请求获得一个新的专用服务实例。

webservice -是否有可能将每个客户端的服务隔离

考虑到这可能是一个线程问题,您可以锁定ExternalLib以防止单独的实例调用代码。

public class ExtenalLibWrapper
{
    private static object Locker = new object();
    public void DoSomething(LoginDetails details)
    {
        lock(Locker)
        {
            ExternalLib.SetLoginData(login.Schema, login.User, login.pass);
            ExternalLib.PerformTask();
        }
    }
}

我已经将所有的公共方法包装在一个整洁的执行包装器中,以提供全局异常日志记录。

这迫使我的web服务一个接一个地处理请求,但就像我提到的,最大。同时运行的客户端数量为10

public class MyService : System.Web.Services.WebService
{
    [WebMethod(EnableSession = true)]
    public static int Add(int value1, int value2)
    {
        return Execute(() =>
        {
            var calculator = new Calculator();
            return calculator.Add(value1, value2);
        });
    }
    private static Logger logger =
        LogManager.GetLogger(typeof(MyService).Name);
    private static System.Threading.SemaphoreSlim ss =
        new System.Threading.SemaphoreSlim(1, 1);
    private void Execute(Action method)
    {
        ss.Wait();
        try { method.Invoke(); }
        catch (Exception ex)
        {
            logger.FatalException(method.Method + " failed", ex); throw; 
        }
        finally { ss.Release(); }
    }
    private T Execute<T>(Func<T> method)
    {
        ss.Wait();
        try { return method.Invoke(); }
        catch (Exception ex)
        {
            logger.FatalException(method.Method + " failed", ex); throw; 
        }
        finally
        {
            ss.Release();
        }
    }
}