Как добиться обработки событий в определенном порядке в C #?

У меня была идея зарегистрировать событие на базовой настраиваемой странице для каждой кнопки. Я могу проверить, было ли это сохранение или редактирование, а затем выполнить авторизацию и проверку. Производные страницы по-прежнему будут обрабатывать событие щелчка, чтобы выполнить специфику для сохранения на этой странице. Я не был уверен, какие события произойдут раньше. (но, вероятно, это в порядке подключения). Если родительская страница запускается первой, я могу получить ожидаемое поведение, установив свойство IsAuthorized или IsValidated и проверив это в обработчике кликов каждой производной страницы.

В любом случае, есть ли способ гарантировать, что обработчик событий базовой страницы запускается и завершается первым, или есть ли способ спасти это?

РЕДАКТИРОВАТЬ: Я упускаю из виду более простой дизайн, который не требует добавления стандартного кода для каждой кнопки редактирования / сохранения / обновления в приложении? И как правильно общаться между организаторами мероприятий? Например, базовая страница должна сообщать об успешной или неудачной проверке, производный класс должен сообщать об успехе или неудаче сохранения, чтобы его можно было записать в аудите.

//Base page-- seems like this will work if events happen in order of wireup.
protected override void OnInit(EventArgs e)
{
    foreach (Control possibleButton in Controls)
    {
        if(possibleButton is Button)
        {
            Button button = (Button) possibleButton;
            button.Command += ButtonCommandPreconditions;
        }
    }
    base.OnInit(e);
    foreach (Control possibleButton in Controls)
    {
        if(possibleButton is Button)
        {
            Button button = (Button) possibleButton;
            button.Command += ButtonCommandPostconditions;
        }
    }
}

void ButtonCommandPreconditions(object sender, CommandEventArgs e)
{
        if(e.CommandName=="Save" || e.CommandName=="Edit")
        {
            //Stuff that needs to happen before other handler   
            //Validate, display failures-- maybe set IsValdated property
            //Check for POST/GET, throw exception on GET.
            //Check for ID, throw exception on anonymous user
            //Check for authorization
            //Display authorization failures-- maybe set IsAuthorized property 
        }
}

void ButtonCommandPostconditions(object sender, CommandEventArgs e)
{
        if(e.CommandName=="Save" || e.CommandName=="Edit")
        {
             //Stuff that needs to happen *after* other handler
            //Log save
        }
}

Изменить: измененный код, чтобы отразить, что обработчики событий должны обрабатываться в порядке подключения.


person MatthewMartin    schedule 25.07.2009    source источник


Ответы (3)


Хотя я не готов предложить решение (так как это потребует довольно сложного понимания вашего дизайна), кроме как сказать вам разделить логику «Сохранить» (и подобных событий) в их собственную иерархию классов и вызвать их в одну жизнь от обработчиков событий. Тем не менее, я могу прокомментировать ваш исходный вопрос (перед вашим редактированием, выделенным жирным шрифтом). Проблема с вашей настройкой заключается в том, что, хотя вы можете вызывать события в определенном порядке, ни одно событие в любой .Net Framework не является гарантированным порядком доставки. Они могут выглядеть упорядоченными, когда вы тестируете на своей машине разработчика, но при практическом развертывании, когда нагрузка на систему возрастает, события будут в первую очередь отложены в пользу других операций. Событие приход к своим слушателям может по сути стать случайным.

Вы никогда не должны строить свою логику, основываясь на порядке получения событий. Каждое событие должно учитывать только текущее состояние, оно не может ничего предполагать ни о каком другом событии. Ваша цепочка событий сломается, когда вам это понадобится больше всего.

person Alex K    schedule 31.07.2009
comment
Ага! Это убеждает меня в том, что моя основная идея ошибочна. Спасибо! Это избавило меня от лишних усилий, чтобы попасть в тупик. - person MatthewMartin; 31.07.2009
comment
Думаю, тебе просто нужно было услышать это от кого-то другого? - person womp; 31.07.2009

События - это просто делегаты многоадресной рассылки, поэтому обработчики событий будут вызываться в том порядке, в котором они были добавлены к событию.

Честно говоря, я бы посоветовал этого не делать. Даже если это работает, это довольно непрозрачно - у вас есть два отдельных обработчика событий в разных классах, обрабатывающих одно и то же событие без очевидной связи между ними.

Было бы лучше передать событие родительскому обработчику для обработки с каждой из ваших унаследованных страниц, прежде чем выполнять свою собственную обработку. Таким образом, будет четкая связь, код будет легко следовать, и вам не придется полагаться на предположения о порядке событий.


ИЗМЕНИТЬ для новых вопросов:

И как правильно общаться между организаторами мероприятий?

Вы должны увидеть мой ответ в этот пост. Обработчики событий на самом деле не предназначены для взаимодействия друг с другом в ASP.Net (по крайней мере, для обработчиков событий жизненного цикла WebControl / страницы), так как нет гарантии порядка активации. Если у вас есть обработчики событий, полагающиеся на информацию, установленную в обработчиках событий-родственников, вероятно, в вашем дизайне есть недостаток.

Например, базовая страница должна сообщать об успешной или неудачной проверке, производный класс должен сообщать об успехе или неудаче сохранения, чтобы его можно было записать в аудите.

На самом деле есть несколько способов справиться с этим. Во-первых, просто сделайте вызовы базового класса абстрактными или виртуальными методами, которые можно переопределить на производных страницах. Во время обработки нажатия кнопки на вашей базовой странице выполните всю вашу проверку, затем вызовите абстрактный метод «DoSave ()» ваших производных страниц и убедитесь, что это удалось.

Или напишите свои собственные события и поднимите их, когда это будет необходимо. Создайте событие «Validation» в своем базовом классе и пусть ваши производные страницы подписываются на него, если им нужно ответить. Убедитесь, что ваш базовый класс поднимает его, когда он завершит проверку. Создайте «Сохраненное» событие в вашем базовом классе, обработчик для него, и пусть ваш базовый класс подписывается на него. Попросите ваши производные классы инициировать событие после завершения сохранения. Теперь базовый класс может обрабатывать его регистрацию.


Я хотел бы добавить, что похоже, что вы загружаете ужасно много вещей в щелчки кнопок базового класса. Можете ли вы выполнить анонимный идентификатор и проверку GET / POST при отображении страницы и просто не отображать кнопки, если у них нет разрешения?

person womp    schedule 25.07.2009

Обработчики событий вызываются в порядке добавления. Так что, если вы можете гарантировать, что родительский обработчик добавлен первым, все в порядке. (Я подозреваю, что вы это уже знаете.)

Вы можете избавить себя от головной боли, обрабатывая события Button Command в своем базовом классе, а затем делегируя их своим производным классам с новым реализованным в них событием ButtonCommand. Единственное реальное изменение существующего производного кода - это более пристальное внимание к параметру «отправитель» в производном обработчике событий. Например:

class BaseClass
{
    public event CommandEventHandler ButtonCommand = delegate { };

    protected override void OnInit(EventArgs e)
    {
        foreach (Control possibleButton in Controls)
        {
            if (possibleButton is Button)
            {
                Button button = (Button) possibleButton;
                button.Command += AnyButtonCommandHandler;
            }
        }
        base.OnInit(e);
    }

    void AnyButtonCommandHandler(object sender, CommandEventArgs e)
    {
        // validation logic. if fails, return.

        ButtonCommand(sender, e);
    }
}

class DerivedClass : BaseClass
{
    public DerivedClass()
    {
        base.ButtonCommand += base_ButtonCommand;
    }

    void base_ButtonCommand(object sender, CommandEventArgs e)
    {
        if (sender == button1) { ... }
        else if (sender == button2) { ... }
        // etc.
    }
}

Вы также можете подумать о замене события ButtonCommand на protected void OnButtonCommand(object sender, CommandEventArgs e) (или сделать и то, и другое); тогда вы можете просто переопределить OnButtonCommand в производном классе.

person Ben M    schedule 25.07.2009