Reputation: 407
I have an event and its method declared as follows, it's an authenticating event for a windows forms login control:
public event EventHandler<AuthenticateEventArgs> Authenticate;
protected void OnAuthenticate(AuthenticateEventArgs e)
{
EventHandler<AuthenticateEventArgs> handler = Authenticate;
if (handler != null)
{
handler(this, e);
}
if (e.Authenticated)
{
OnLoggedIn(new EventArgs());
}
else
{
OnLoggedError(new EventArgs());
}
}
The event is raised on a button click, now assume on some other project there are subscribers to this event as follows:
this.loginControl1.Authenticate += loginControl1_Authenticate;
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000);
ea.Authenticated = false;
};
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000);
ea.Authenticated = true;
};
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000);
ea.Authenticated = false;
};
The System.Threading.Thread.Sleep(2000);
is just a simulation for some process that takes some time. The problem is that the last subscriber performs the If condition in the OnAuthenticate
method and raises the other event, the before subscribers does not. Code works perfectly for one subscriber. Where is the problem in this situation ?
Upvotes: 0
Views: 342
Reputation: 2520
Since you wanted asynchronous execution of your authentication methods, you could work like this.
Create a delegate that returns a bool
public delegate bool Authenticate(object sender, AuthenticateEventArgs e);
Authenticate authHandler;
You may or may not use the arguments but you can use or remove that later.
Create your authentication methods
bool AuthenticationMethod1(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000); //Simulate some long running task.
return false; //Return true or false based on authentication failed or succeeded.
}
bool AuthenticationMethod2(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000); //Simulate some long running task.
return true; //Return true or false based on authentication failed or succeeded.
}
bool AuthenticationMethod3(object o, AuthenticateEventArgs ea)
{
System.Threading.Thread.Sleep(2000); //Simulate some long running task.
return false; //Return true or false based on authentication failed or succeeded.
}
Hook up the handlers
authHandler += AuthenticationMethod1;
authHandler += AuthenticationMethod2;
authHandler += AuthenticationMethod3;
Now execute
if (authHandler != null)
{
foreach (Authenticate handler in authHandler.GetInvocationList())
{
handler.BeginInvoke(this, e as AuthenticateEventArgs, new AsyncCallback(Callback), handler);
}
}
LAST PART : Have you callback defined
void Callback(IAsyncResult ar)
{
Authenticate d = (Authenticate)ar.AsyncState;
if (d.EndInvoke(ar))
{
OnLoggedIn(new EventArgs());
}
else
{
OnLoggedError(new EventArgs());
}
}
Upvotes: 1
Reputation: 73482
This is well known behavior, Events execute in the order of subscription, so subscriber who suscribed last will overwrite all the previous values. You get to see the last subscriber's update (in this case false).
You can fix this by checking already authenticated and skip processing.
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
if(ea.Authenticated)
{
return;
}
System.Threading.Thread.Sleep(2000);
ea.Authenticated = false;
};
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
if(ea.Authenticated)
{
return;
}
System.Threading.Thread.Sleep(2000);
ea.Authenticated = true;
};
this.loginControl1.Authenticate += delegate(object o, AuthenticateEventArgs ea)
{
if(ea.Authenticated)
{
return;
}
System.Threading.Thread.Sleep(2000);
ea.Authenticated = false;
};
Reason why because you see Authenticated
false always is handler(this, e);
will return only after invoking all subscribed methods. So your last subscriber sets Authenticated
to false so you see false only when if (e.Authenticated)
is executed.
Upvotes: 0
Reputation: 610
The problem is all the event subscribers will executes on same thread as the function who triggered them so when the execution reaches
handler(this, e);
the execution will move to the first subscriber code
System.Threading.Thread.Sleep(2000);
ea.Authenticated = false;
and then the execution goes to the second subscriber. At the end it will execute the last subscriber code and then the execution backs to the caller function
protected void OnAuthenticate(AuthenticateEventArgs e)
and the execution continue from the event trigger line to the second if statement and at that point the value of ea.Authenticated is false as last subscriber set it.
if you want to raise each event on a different threads check Trigger events on separated threads
Upvotes: 0
Reputation: 310
remove event by this code:
this.loginControl1.Authenticate -= loginControl1_Authenticate;
Upvotes: 0