Is it possible to create stateful web service in C#?
Asked Answered
S

3

6

I have now something like this:

public class Service1 : System.Web.Services.WebService
{
    [WebMethod]
    public string Method1()
    {
        SomeObj so = SomeClass.GetSomeObj(); //this executes very long time, 50s and more
        return so.Method1(); //this exetus in a moment 
    }

    [WebMethod]
    public string Method2()
    {
        SomeObj so = SomeClass.GetSomeObj(); //this executes very long time, 50s and more
        return so.Method2(); //this exetus in a moment 
    }

 ...
}

Is it possible to make stateful web service so that I can reuse SomeObj so and just call methods on the same object?

So the client which will use this service would first call web method which would create so object and return some ID. And then in subsequent calls the web service would reuse the same so object based on ID.

EDIT


Here is my actual code:

[WebMethod]
public List<ProcInfo> GetProcessList(string domain, string machineName)
{
    string userName = "...";
    string password = "...";
    TaskManager tm = new TaskManager(userName, password, domain, machineName);

    return tm.GetRunningProcesses();
}

[WebMethod]
public bool KillProcess(string domain, string machineName, string processName)
{
    string userName = "...";
    string password = "...";
    (new TaskManager(userName, password, domain, machineName);).KillProcess(processName);               
}
Speight answered 7/11, 2010 at 12:57 Comment(0)
D
6

Stateful web services are not scalable and I wouldn't recommend them. Instead you could store the results of expensive operations in the cache. This cache could be distributed through custom providers for better scalability:

[WebMethod]
public string Method1()
{
    SomeObj so = TryGetFromCacheOrStore<SomeObj>(() => SomeClass.GetSomeObj(), "so");
    return so.Method1(); //this exetus in a moment 
}

[WebMethod]
public string Method2()
{
    SomeObj so = TryGetFromCacheOrStore<SomeObj>(() => SomeClass.GetSomeObj(), "so");
    return so.Method2(); //this exetus in a moment 
}

private T TryGetFromCacheOrStore<T>(Func<T> action, string id)
{
    var cache = Context.Cache;
    T result = (T)cache[id];
    if (result == null)
    {
        result = action();
        cache[id] = result;
    }
    return result;
}
Downstairs answered 7/11, 2010 at 12:59 Comment(8)
Can you store all object in the cache or there are any limitations ? My objects includes open WMI connections to various computers.Speight
By default the cache is stored in memory and if you start running on low memory it will automatically be evicted. So yes, you could store as much as you wish but you should always check that the object is in the cache and never rely on it being there because you stored it.Downstairs
@Darin Dimitrov, you should make your solution type-safe by using a lock. While one request is executing the "result = action();" line, which takes time, any other request will also see the cache as being null and repeat the same "result = action();" line. See en.wikipedia.org/wiki/Double-checked_lockingCapsicum
I really don't understand what you are saying. You must be mistaken. Write a test. :-)Capsicum
In other words: cache[id] will be null until action() is executed. any other thread testing it will also execute action().Capsicum
@Sklivvz, yes you are correct but as reads will be more frequent than writes locking on each read could be a bottleneck. Once the action is executed it should be OK. Also you are saying that the code is not thread safe: this is not true. The code is thread safe. The problem is that the action could indeed be executed twice but you won't get corrupt or partially assigned results.Downstairs
Yep there will be no corrupt results, only double execution (each time the cache is cleared).Capsicum
@Darin Dimitrov Thank you for your answer. New objects will be rerly created and there is almost impossible that clients would requiers two same objects. Also can you plese explain your code a bit, because I'm new to C# and I relly don't get it.Speight
C
1

Option 1

You can use your HttpSession.

//this executes very long time, 50s and more, but only once.
private SomeObj SessionSomeObj { 
  get 
  { 
    var ret = (SomeObj)Session["SomeObjStore"] ?? SomeClass.GetSomeObj();
    SessionSomeObj = ret;
    return ret; 
  }
  set { Session["SomeObjStore"] = value; }
}

[WebMethod(EnableSession = true)]
public string Method1()
{
    return SessionSomeObj.Method1(); //this exetus in a moment 
}

[WebMethod(EnableSession = true)]
public string Method2()
{
    return SessionSomeObj.Method2(); //this exetus in a moment 
}

Note that this will only work if one call per client is made at a time.

Option 2

You can leave the class as is but use the WebMethod differently. If you are calling from a .Net generated class, async methods are provided for these occurrences. Basically you invoke the Method1 begin request method and get a call back when the execution is finished. You might need to tweak the timeout parameter of the web service client class for this to work though.

Option 3

You can use the caching features of the SixPack library to do this effortlessly! ;-)


[Edited after comment] There are now two static fields in option 1 to allow two different instances, one per method, as requested.


[Edited after further explanation] Using Session to make the calls stateful.

See: http://msdn.microsoft.com/en-us/library/aa480509.aspx

Also added Option 3.

Capsicum answered 7/11, 2010 at 13:56 Comment(9)
Option 1 is no-go because different client need different "so" object to call method on.Speight
I think we have some missunderstanding. Client1 creates instance of "so" Object and then calls method1 to methodN on this instance. Client2 creates another instance of "so" oject and again calls same or different methods on this instance. So there needs to be different "so" instance for every clientSpeight
Then you could use a Dictionary instead... The main problem with this approach is that the objects don't persist beyond the lifetime of the AppDomain.Excuse
When it ends this "lifetime of the AppDomain" ?Speight
For a normal application when the process terminated. For ASP.net it can happen whenever IIS decides to restart it, in particular after configuration changes. weblogs.asp.net/owscott/archive/2006/02/21/ASP.NET-v2.0-2D00-AppDomain-recycles_2C00_-more-common-than-before.aspxExcuse
Is option 1 applicable to my real code and how ? Options 2 and 3 are not suitable for me.Speight
You need to use a method with parameters instead of a property, but yes, it should work.Capsicum
Ok! And what is the difference using Session or using Cache ?Speight
Session is a different instance per user. Cache is a single instance for all users.Capsicum
O
0

Change the ServiceContract of your interface into:

[ServiceContract(SessionMode = SessionMode.Required)]

And put the following attribute on your class:

[ServiceBehaviorAttribute(InstanceContextMode = InstanceContextMode.PerSession)]

See http://msdn.microsoft.com/en-us/library/system.servicemodel.sessionmode.aspx for more information and an example.

Omari answered 7/11, 2010 at 13:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.