问题
I have an legacy System.Web.Services.WebService (not WCF) that I have to maintain.
Ocassionly I run into some wired behaviours that I would describe as race conditions.
Either the service hangs and has to be restarted.
Sometimes I get this exception:
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() ...
I know whats the root cause. The service utilizes a lib that talks to mysql and was not designed with webservices in mind. Unfortunatly I cannot change this lib.
One example webmethod looks like this:
[WebMethod(EnableSession = true)]
public void DoSomething()
{
var login = this.Session["login"] as LoginDetails;
ExternalLib.SetLoginData(login.Schema, login.User, login.Pass);
ExternalLib.PerformTask();
}
So the problem here is this:
ExternalLib.SetLoginData
just set's some global varsExternalLib.PerformTask
performs database calls, some inside a transaction.- The process is like
1. Create MySqlConnection or take it from cache 2. Create MySqlCommand 3. Execute Command 4. Dispose command
Client a) calls DoSomething()
and I init his connection. Half way done with his job Client b) calls DoSomething()
which apparently changes the Login-Data for client a and the next call inside the transaction will use the login from client b) which causes the transaction.
Anyway, I know this is a bad design but my question is how to workaround this. Currently (since I only have 10 clients) I created a dedicated Website on a differnet port which all point to the same root directory but this is an akward solution.
Maybe there is a possibility to run every session inside its on realm. Any suggestions. If I understand this page correctly for WCF is is the default behaviour: http://msdn.microsoft.com/en-us/magazine/cc163590.aspx
Per-Call Services
Per-call services are the Windows Communication Foundation default instantiation mode. When the service type is configured for per-call activation, a service instance, a common language runtime (CLR) object, exists only while a client call is in progress. Every client request gets a new dedicated service instance.
回答1:
Seeing as this is probably a threading issue you can lock the ExternalLib
to prevent separate instances from calling the code.
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();
}
}
}
回答2:
I already wrapped all my public methods in a neat execute wrapper to provide global exception logging.
This forces my webservice to process one request after another, but like I mentioned, the max. number of simultanious clients is 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();
}
}
}
来源:https://stackoverflow.com/questions/17044555/system-web-services-webservice-is-it-possible-to-isolate-the-service-per-clien