Multicast delegates with IEnumator and yield return?
Hello,
I am trying do set up a multicast delegate to split my yield returns for a WebTest I am writing, but only the last delegate is firing. Is this a known problem, by design, or am I doing something wrong
Ahh. Ok. The problem is that you are trying to use the yield keyword to control state throughout the entire method. The yield statement can only be used in iterator blocks. For instance, a while or for loop.
The mistake you're making is to think of the yield return value as a sort of exception: whereever you cause it, it'll just bubble up through the delegates return value (after all they bubble through the functions return value too) and cause the IEnumerator generated to list an extra item.
The point is, yield return is merely syntactic sugar for generating an IEnumerator. when a function utilizing yield return is called, the runtime stored the program counter and execution context within the generated IEnumerator, such that whenever a yield return is called that program counter points to the next instruction to be executed and that the next call to MoveNext can start at the right point.
i.e. yield return is a fancy generator with an internal code pointer or something.
Now chain together multiple delegates those are executed in order; and the return value of the LAST one is returned. So those IEnumerators ARE actually created; except they're never used and immediately trashed! That's because you're generating (when that delegate is called) a pointer to that "execution context" which is the generated IEnumerator that would, _IF_ MoveNext is ever called, result in the runtime actually beginning execution until the first yield return is reached. That execution context stored within the IEnumerator, the delegates job is done - and the next delegate has its turn. etc etc etc. until the final delegate which is the only generated IEnumerable your loop ever gets to see and thus the only IEnumerable which gets a MoveNext call and finally therefore the only yield functions which ever even gets started executing.
Thanks so much for the reply. I'm not sure I understand you correctly - using yield multiple times in a method seems to work okay for me, actually this is basically how the test recorder works in VSTS.
The main problem is that I can't get the yields across multiple delegates - the last one in the multicast invocation list fires and executes as you would expect, its just that the other two are never invoked.
I'm considering doing some sort of system with adding steps to a list and then runnning through the list in a testrunner, which can then yield if the step is simple webtestrequest, or execute a delegate if custom handling is required at that moment.
What I'm trying to do is create reusable, simulated user clicks in my tests that I'm writing, with the ability to support non-deterministic tests. I need to be able to control the direction of the test, based on runtime information, but I don't want to have to deal setting and resetting the postrequest event and processing that. I'd rather do something like this:
public IEnumerator<WebTestRequest> GetRequestEnumerator() { EnumDelegate += new EnumDelegate( Login ); EnumDelegate += new EnumDelegate( Step1 ); EnumDelegate += new EnumDelegate( Step2 ); EnumDelegate += new EnumDelegate( DbCheck ); ... return EnumDelegate(); }
I might be able to do something with the GetInvocationList(), but I don't know how to pile those together and yield through them.
Here is some example code:
public abstract class MyTest : WebTest { public delegate IEnumerator<WebTestRequest> EnumerateTestDelegate(); public EnumerateTestDelegate TestEnumerator;
public IEnumerator<WebTestRequest> Login() { return Login("user", "pw"); }
public IEnumerator<WebTestRequest> Login(string username, string password) { WebTestRequest request1 = new WebTestRequest("http://dskdtaylor/PESWebUI"); request1.ThinkTime = 8; ExtractHiddenFields rule1 = new ExtractHiddenFields(); rule1.ContextParameterName = "1"; request1.ExtractValues += new EventHandler<ExtractionEventArgs>(rule1.Extract); //tests.Enqueue(request1); yield return request1;
Multicast delegates with IEnumator and yield return?
NePasSql
Ahh. Ok. The problem is that you are trying to use the yield keyword to control state throughout the entire method. The yield statement can only be used in iterator blocks. For instance, a while or for loop.
Thomas G&#252;rtl
oguevarra
I too am very interested in a solution like you sought. Do you care to share your final solution
BTW, I'm working with C# webtests also.
Regards,
Mountain
Devboy
hii all,
First time I am just learning Delegates in c#.
I have faced a conceptual problem.
In books it is written that A Multicasting Delegate should have a return type of Void unless it will throw a run time exception.
But i am written a code snippet with a Multicasting Delegate with a string return type .But it is not throw any exception at all.
Can anybody tell me what is happening here............
Plz somebody help me out...........
The code is given below:
delegate string Delegate_Multicast(int x, int y); class Class2{
static string Method1(int x, int y){
Console.WriteLine("You r in Method 1"); return "hi";}
static string Method2(int x, int y){
Console.WriteLine("You r in Method 2"); return "hi";}
public static void Main(){
Delegate_Multicast func = new Delegate_Multicast(Method1);func +=
new Delegate_Multicast(Method2); string s=func(1,2); // Method1 and Method2 are called}
Nagie_Stopy
The point is, yield return is merely syntactic sugar for generating an IEnumerator. when a function utilizing yield return is called, the runtime stored the program counter and execution context within the generated IEnumerator, such that whenever a yield return is called that program counter points to the next instruction to be executed and that the next call to MoveNext can start at the right point.
i.e. yield return is a fancy generator with an internal code pointer or something.
Now chain together multiple delegates those are executed in order; and the return value of the LAST one is returned. So those IEnumerators ARE actually created; except they're never used and immediately trashed! That's because you're generating (when that delegate is called) a pointer to that "execution context" which is the generated IEnumerator that would, _IF_ MoveNext is ever called, result in the runtime actually beginning execution until the first yield return is reached. That execution context stored within the IEnumerator, the delegates job is done - and the next delegate has its turn. etc etc etc. until the final delegate which is the only generated IEnumerable your loop ever gets to see and thus the only IEnumerable which gets a MoveNext call and finally therefore the only yield functions which ever even gets started executing.
The fix is easy: wanna chain Enumerators
just do:
IEnumerator<Bla> MyChainGenerator() {Pedro Ferreira
Thanks so much for the reply. I'm not sure I understand you correctly - using yield multiple times in a method seems to work okay for me, actually this is basically how the test recorder works in VSTS.
The main problem is that I can't get the yields across multiple delegates - the last one in the multicast invocation list fires and executes as you would expect, its just that the other two are never invoked.
I'm considering doing some sort of system with adding steps to a list and then runnning through the list in a testrunner, which can then yield if the step is simple webtestrequest, or execute a delegate if custom handling is required at that moment.
sean334343434
public IEnumerator<WebTestRequest> GetRequestEnumerator()
{
EnumDelegate += new EnumDelegate( Login );
EnumDelegate += new EnumDelegate( Step1 );
EnumDelegate += new EnumDelegate( Step2 );
EnumDelegate += new EnumDelegate( DbCheck );
...
return EnumDelegate();
}
I might be able to do something with the GetInvocationList(), but I don't know how to pile those together and yield through them.
Here is some example code:
public abstract class MyTest : WebTest
{
public delegate IEnumerator<WebTestRequest> EnumerateTestDelegate();
public EnumerateTestDelegate TestEnumerator;
public IEnumerator<WebTestRequest> Login()
{
return Login("user", "pw");
}
public IEnumerator<WebTestRequest> Login(string username, string password)
{
WebTestRequest request1 = new WebTestRequest("http://dskdtaylor/PESWebUI");
request1.ThinkTime = 8;
ExtractHiddenFields rule1 = new ExtractHiddenFields();
rule1.ContextParameterName = "1";
request1.ExtractValues += new EventHandler<ExtractionEventArgs>(rule1.Extract);
//tests.Enqueue(request1);
yield return request1;
WebTestRequest request2 = new WebTestRequest("http://dskdtaylor/PESWebUI/Logon.aspx");
request2.ThinkTime = 1;
request2.Method = "POST";
BindHiddenFields request2BindHiddenFields = new Microsoft.VisualStudio.QualityTools.WebTestFramework.BindHiddenFields();
request2BindHiddenFields.HiddenFieldGroup = "1";
request2.PreRequest += new EventHandler<PreRequestEventArgs>(request2BindHiddenFields.PreRequest);
request2.QueryStringParameters.Add("ReturnUrl", "/PESWebUI/Default.aspx");
FormPostHttpBody request2Body = new FormPostHttpBody();
StringHttpBody sbody = new StringHttpBody();
request2Body.FormPostParameters.Add("txtUserName", username );
request2Body.FormPostParameters.Add("txtPassword", password );
request2Body.FormPostParameters.Add("ddlDatabase", "benj_Keystone");
request2Body.FormPostParameters.Add("btnLogon", "Log On");
request2Body.FormPostParameters.Add("_ctl33", "");
request2Body.FormPostParameters.Add("_ctl34", "");
request2.Body = request2Body;
//tests.Enqueue( request2 );
yield return request2;
}
public override IEnumerator<WebTestRequest> GetRequestEnumerator()
{
TestEnumerator += new EnumerateTestDelegate( Login );
TestEnumerator += new EnumerateTestDelegate( MyTes1t );
TestEnumerator += new EnumerateTestDelegate( MyTest2 );
return TestEnumerator();
}
public IEnumerator<WebTestRequest> MyTest1()
{
WebTestRequest request1 = new WebTestRequest("http://dskdtaylor/PESWebUI/RPC/PortfolioEdgeUIServicesRPC.aspx");
yield return request1;
WebTestRequest request2 = new WebTestRequest("http://dskdtaylor/PESWebUI/RPC/PortfolioEdgeUIServicesRPC.aspx");
yield return request2;
}
public IEnumerator<WebTestRequest> MyTest2()
{
WebTestRequest request3 = new WebTestRequest("http://dskdtaylor/PESWebUI/RPC/PortfolioEdgeUIServicesRPC.aspx");
yield return request3;
WebTestRequest request4 = new WebTestRequest("http://dskdtaylor/PESWebUI/view/Default.aspx PESWebState_ObjectUniqueId=NULL*Po*Portfolio*1*-99999&ModuleId=2&ViewId=4");
yield return request4;
WebTestRequest request5 = new WebTestRequest("http://dskdtaylor/PESWebUI/view/DatasheetView.aspx PESWebState_ObjectUniqueId=NULL*Po*Portfolio*1*-99999&ModuleId=2&ViewId=4");
yield return request5;
}
}