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);
}
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;
}
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.
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.
Related
I created WCF service and calling it from the android studio and its working fine, but after implementing WCF perSession functionality it is working for a single user at a time.
Problem:
My problem is when i am hitting WCF url with multiple user my sessionID get overwrite by the latest logged in user, So how to maintain session for multiple user like we do in web application.
I used this to creste session in WCF:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
and this is my method to create sessionid within services class
public class MyService : IMyService
{
static string currentSessionID = string.Empty;
public string createSession()
{
currentSessionID = DateTime.Now.Ticks.ToString();
return currentSessionID;
}
public string Login()
{
var mysessionId = createSession();
return mysessionId;
}
public string Mymethods(string data)
{
string response = "";
if(data.StartsWith("01"))
response = Login();
return response;
}
}
and am hitting this createSession() method only in login function.
Please help me out of this.....
Thanks in advnance.
We have an application that contacts several diffrent remote services(SOAP, HTTPREQUEST). We then do different actions(import, export, update, delete).
Today we have two client classes and four action classes.
QUESTION!
How can I decouple these two modules so that I have to do the least changes. IE only add a new action/new client. Nothing more.
Client class
Authorizes our client against the remote service, it handles logging in and out.
Action class
Holds the url, method to invoke against the client. Aswell as the ExecuteActionMethod
Usage
Client class get's decorated with an action and then performs the action with the client.
Fears
I dont want to: - create a new action class everytime I add a new client class - create a new client class everytime I add a new action class - No god object factory that needs to know everything
Problem
The problem with this approach is that when talking to different clients, I need diffrent information in this case different URLS, talking to the Soap service needs invoking of the correct method. The action itself is the keeper of this information. But as I dig deeper this certainly is something that will change.
Scenario 1#
I end up creating classes that combine both action and result. So I have classes like "HttpImport"(based on HttpClient and ImportAction). Which results in X(Clients) * Y(Actions) which now would total at 8 classes, which is really bad.
Scenario 2#
Time for some code! In this scenario the implementation binds my classes together even though I use abstractions.
Problem here is that every action need to have a property for each of the clients(remember they visit different endpoints). So if i were to add one more client I would have to go through all the actions and add another property for that clients endpoint, aswell as add another deocrator to delegete all calls to the correct endpoint(remember i have three properties now in every action). If I were to create another action, it would just be that action. So N*times actions + 1(the action), in this case 5 changes. A little bit better but still not there.
Scenario 3#
This is the God object factory. Here we get rid of the properties holding the endpoints, and we supply the enpoint via the constructor. This will result in methods for creating all sorts of clients and actions. Same as above X(Clients) * Y(Actions) if something were to be added, these accumulate into 8 new methods inside the factory. The factory must also hold endpoint information.
Code
My code has evolved to the 2:nd scenario. I dont want to build the factory, and I'm looking to you guys.
Something tells me that the client classes does to much and should somehow be decoupled, from the classes they instansiate inside.
Main
static void Main(string[] args)
{
IAction iact = new ImportAction();
IDecorator idec = new HttpDecorator(iact);
IClient icli = new HttpClient(idec);
Console.Write(icli.connect().ToString());
Console.ReadKey();
}
IAction
public interface IAction
{
string[] Execute();
string HttpString { get; }
string SoapMethod { get; }
}
ImportAction
class ImportAction : IAction
{
private string soapmethod;
private string httpUrl;
public ImportAction()
{
this.HttpString = #"http://www.hereiswereactionsgo.com";
}
public string[] Execute()
{ //Execute the action!
return null;
}
public string HttpString { get; set; }
public string SoapMethod { get; set; }
}
IDecorator
public interface IDecorator
{
string GetActionString();
}
HttpDecorator
class HttpDecorator : IDecorator
{
private IAction _action;
public HttpDecorator(IAction action)
{
this._action = action;
}
public string GetActionString()
{
return _action.HttpString;
}
public string[] Execute()
{
throw new NotImplementedException();
}
}
IClient
public interface IClient
{
bool connect();
}
HttpClient
class HttpClient : IClient
{
private string _username;
private string _password;
private IDecorator _myaction;
private HttpWebRequest webReq;
public HttpClient(IDecorator action)
{
this._username = "myusername";
this._password = "mypassword";
this._myaction = action;
}
public bool connect()
{
bool result = false;
webReq = (HttpWebRequest)WebRequest.Create(_myaction.GetActionString());
webReq.Credentials = new NetworkCredential(_username, _password);
HttpWebResponse myHttpWebResponse = (HttpWebResponse)webReq.GetResponse();
if (myHttpWebResponse.StatusCode == HttpStatusCode.OK)
{
result = true;
}
return result;
}
}
Visitor pattern seems suitable for this (Visitor) .
Treat Actions as the Visitors and Clients as Elements to visit. Keeping Action as an abstract class rather than interface may help by providing the boilerplate code.
To add a new Action extend BaseAction. Implement methods such as getHttpUrl(), getHttpBody() etc.
To add a new Client will require changes to existing classes. You have to implement corresponding methods in each Action class. I assume adding a new Client will happen less frequently.
The sample code below follows Java syntax.
public static void main() {
new HttpClient().performAction(new ImportAction());
}
public interface Client {
performAction(Action);
}
public class HttpClient implements Client {
public void accept(IAction a) {
a.visitHttp(this);
}
}
public abstract class Action {
public visitHttp(HttpClient c) {
getHttpUrl();
c.connect(getHttpUrl());
c.send(getHttpBody());
c.close;
}
public visitSoap(SoapClient c) {
}
public abstract String getHttpUrl();
public abstract String getHttpBody();
}
ImportAction extends Action {
#Override
getHttpUrl() {
}
#Override
getHttpBody() {
}
}
I have created a win service that has this method on start:
private String bamboo = "";
public String Baboon = "";
protected override void OnStart(string[] args)
{
this.bamboo = "bamboo";
this.baboon = "baboon";
}
after the service starts i have methods for returning the field and the property:
public String GetValueBamboo()
{
return this.bamboo;
}
public String GetValueBaboon()
{
return this.Baboon;
}
these methods return empty strings when i try to get the values from another app:
public void GetValues()
{
var binding = new NetNamedPipeBinding();
var epAddrs = new EndpointAddress("net.pipe://localhost/PipeReverse");
var pipeFactory =
new ChannelFactory<ITaskManager>(
binding,
epAddrs);
var proxy =
pipeFactory.CreateChannel();
Assert.AreEqual(proxy.GetValueBamboo(),"bamboo"); // returns false
Assert.AreEqual(proxy.GetValueBaboon(),"baboon"); // returns false
}
I have noticed that if i use the same proxy object to first set the values of the field/property(didn't write those methods, pretty straightforward), then the results come out as expected but every call after with a new proxy object again returns empty strings.
is there a way that i can set some properties and/or fields on the service and that they persist between calls to the service?
The solution that i ended up using is just to declare the fields static, and then it works.
I would still like to know why instanced fields in a service reset after reinitializing the client, so if anyone has any idea about this I would very much like to know.
Sorry if i dont explain myself clearly but I can't seem to wrap my head around how to present the problem at hand.
We have some utility classes used in web and windows forms development. We use the following pattern in order to dynamically call the corresponding methods depending on whether the code runs inside a web or in windows forms.
The following code is a stripped down version so you can observe the logic clearlier:
public static class Constants
{
private static ConstantsWinFormsWebCommonProvider _Provider;
public static ConstantsWinFormsWebCommonProvider Provider
{
get
{ /*in websites the static variables reset to null in case of Appdomain recycle
*so we check beforehand, this also serves for first time use in both: web and winforms */
//lazy loading, initializes when provider is required for the first time or if gets null because appdomain recycle...
if (_Provider != null) return _Provider;
_Provider = Fun.WebSite()? (ConstantsWinFormsWebCommonProvider)new ConstantsWebProvider() : new ConstantsWinFormsProvider();
Initialize();
return _Provider;
}
}
public static void Initialize()
{
Provider.Initialize();
}
public static string DataBaseName
{
get { return Provider.DataBaseName ; }
set { Provider.DataBaseName = value; }
}
}
public class ConstantsWinFormsWebCommonProvider
{
internal bool isInitialized { get; set; } //variable only used in the winform part for now, shouldnt affect the web part issue
internal string DataBaseName { get; set; }
}
public class ConstantsWebProvider: ConstantsWinFormsWebCommonProvider
{
public override void Initialize()
{
base.Initialize();
string errordetails= "";
if (!Fun.InitializeWeb(ref errordetails)) //fills all the Provider properties and connects, otherwise returns false
{
throw new Exception("Problem initializating web " + errordetails));
}
}
}
public class ConstantsWinFormsProvider: ConstantsWinFormsWebCommonProvider
{
public new string DataBaseName
{
get
{//lazy loading so it connects only when required
if (isInitialized) //(!string.IsNullOrEmpty(base.DataBaseName))
{
return base.DataBaseName ;
}
bool resul = Fun.InicializeWinForms();
if (resul) return base.DataBaseName ;
MessageBox.Show("Problem initializing");
return null;
}
set
{
Fun.WinFormsValueSavingSistemx(value); //example call, not related with the issue at hand
}
}
}
The problem is that occasionally in the case of the web we get errors because the properties inside the Provider variable are null, but the Provider itself is not null.
In theory it shouldn't be possible (in the case of the web we initialize all the Provider properties in the Initialize() function and if the appdomain recycles causing the properties to go null the Provider variable should be null too causing it to call Initialize() again when you try to access one of its properties).
What's the cause and recommended solution to this problem?
Edit:
In case this provides more hindsight, the problem for now has happened in the first page load of one aspx (isPostback = false, where it would access the Provider.DataBaseName for the first time in the first visit to that aspx or if coming again from another aspx). Also in the documented cases they where using IE8 and IE9...
In entire application use the static class tConfig.ConnectionString to download the necessary connectionstring. Unfortunately, I need to be able to modify connectionsting depending on whether the reference is to the TransactionScope. Currently, I have this piece of code, but static class calls me StackOverflow error. Please help to implement the functionality in this class static (or some better solution).
public static class tConfig
{
public static string ConnectionString
{
get {
if (System.Transactions.Transaction.Current != null)
return "ConnectionString with scope";
else
return "ConnectionString without scope";
}
}
}
[ServiceContract]
public interface IMyService
{
[OperationContract]
string GetData;
[OperationContract]
string GetDataWithScope;
}
public class MyService : IMyService
{
public string GetData
{
using (var context = new MyEntities(tConfig.ConnectionString)
{
return context.table1.where(x=>x.ID == 1).Select(x=> x.F_NAME).FirstOrDefault().ToString();
}
}
public string GetDataWithScope
{
using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(600)))
{
using (var context = new MyEntities(tConfig.ConnectionString)
{
return context.table1.where(x=>x.ID == 1).Select(x=> x.F_NAME).FirstOrDefault().ToString();
}
}
}
}
I think that it is bad idea to use transdactions this way. When the transaction completes? in your code there is no Complete or RollBack call. The scope for every call will be different because of different threads of each call.
see this link. It describes the approach to use trasactions on wcf level.
In this situation client can create and complete transaction scope.