Question

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 ?

Was it helpful?

Solution

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());
    }
}

OTHER TIPS

remove event by this code:

this.loginControl1.Authenticate -= loginControl1_Authenticate;

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

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top