Как дождаться ответа обработчика событий в C #?

#c# #event-handling

#c# #обработка событий

Вопрос:

У меня есть приложение (A1), которое разделено на несколько уровней. Некоторые уровни используются для взаимодействия с пользователем, некоторые используются для взаимодействия с различными базами данных, а некоторые включают бизнес-логику. У нас есть другое стороннее приложение (A2), которое отправляет запрос на (A1), и A1 должен ответить на запрос. Ниже приведена архитектура A1.

T3 (этот уровень получает запрос от приложения A2)

T2 (бизнес-логика)

T1 (пользовательский интерфейс)

T2 содержит всю бизнес-логику. Проблема, с которой я сталкиваюсь, заключается в том, что я получаю запрос от приложения A2. Мне нужно ответить на запрос на основе некоторой бизнес-логики, которая представлена в T2. Я могу вызвать событие из T3, на которое подписан T2, но мне нужно получить данные из обработчика событий, как показано ниже;

T3:

 public Response CanStore(string materialType){
    //Invoke event and wait to get response from T2
    return response.;

}
  

T2: подписался на событие T3

  public async void canStore(object sender, EventArgs e){
     //Perform some logic and response result to T3
}
  

Возможно ли это?

Комментарии:

1. Когда вы говорите «Уровень», являются ли эти логические уровни (т.Е. Сборки в рамках одного и того же запущенного процесса) или отдельными службами, использующими что-то вроде HTTP или gRPC?

2. ДА. Это тот же запущенный процесс

3. Итак, почему T3 -> T2 вообще использует событие? Почему бы просто не вызвать метод и не получить ответ?

4. @Fildor Если бы это было так, вы могли бы сделать прямой вызов из приложения, получающего событие, в бизнес-логику, как у меня это было бы. Я не уверен, почему у вас все наоборот

5. «Потому что у T3 нет ссылки на T2.» … почему бы и нет? А что такое T3? Какова его цель? Это какой-то API (возможно, в качестве альтернативы использованию пользовательского интерфейса в T1?)? Это кажется вероятным, поскольку вы говорите, что он может получать запросы от других приложений. Если это так, то для него имело бы смысл иметь ссылку на уровень бизнес-логики. Но почему T2 должен ссылаться на T3? Это кажется неправильным. У T3 также есть какая-то другая цель? Если это так, то рассмотрите возможность разделения компонента на 2 (или более), которые имеют определенные цели.

Ответ №1:

Мне кажется, что у вас неправильная архитектура

Если T2 имеет бизнес-логику, а T1 — это пользовательский интерфейс, которому предположительно необходим доступ к бизнес-логике, а T3 — это приложение, которое получает сообщения от внешней стороны, а также нуждается в доступе к бизнес-логике, то и T1, и T3 нуждаются в ссылке на T2.

Тогда это всего лишь простое внедрение зависимостей бизнес-логики в T3!

 public class T3Service
{
    private readonly IT2BusinessLogic businessLogic;

    public T3Service(IT2BusinessLogic businessLogic)
    {
        this.businessLogic = businessLogic;
    }

    public Response CanStore(string materialType)
    {
        var t2Response = businessLogic.CanStore(materialType);
        // Do what you like to build response to external service
        return response;

    }
}
  

Комментарии:

1. Вы правы. Сначала я спорю с человеком, который создал эту архитектуру. Но почему-то есть и другие ограничения, потому что это очень большое приложение, которое содержит около 20-30 слоев.

2. @umer тогда вы задали слишком упрощенный вопрос о сложной проблеме. Я могу ответить только на основе предоставленной вами информации.

3. @umer Поднимите и отмените ярко-красный флаг, сейчас . Это звучит так, как будто вы ходите на цыпочках и исправляете исправления вокруг общего сломанного дизайна. Продолжение в том же духе только ухудшит ситуацию.

4. ОК. Я согласился со всеми вами, но опять же, есть ли какая-либо возможность дождаться ответа обработчика событий.

5. @umer Вероятно, да, но если вы не можете связать ответ с запросом, от этого мало толку.

Ответ №2:

Помимо архитектурных проблем и предполагая, что вы можете изменить T3 и T2, вы можете обойти злоупотребление EventArgs с помощью некоторого пользовательского EventType . НЕ мой любимый, но может решить вашу проблему.

Пусть T2 манипулирует EventArg для сохранения требуемого результата внутри. После завершения вашего обработчика событий вызывающий сайт T3 может получить результат из EventArg.

Что-то вроде

 public Response CanStore(string materialType){
    //Invoke event and wait to get response from T2
    myEvent.Invoke?(sender, myCustomEventArgs);
    await myCustomEvent.Completion.Task;

    return myCustomEvent.ResponseFromSubscriber;
}
  

с помощью MyCustomEvent ваше текущее событие расширяется двумя свойствами,

 MyCustomEventArgs: MyCurrentEventArgs
{
    // makes your Event "awaitable";
    TaskCompletionSource<bool> Completion{ get; } = new TaskCompletionSource<bool>; 
    Response ResponseFromSubscriber{ get; set; } // As you need 
}
  

и подписчик

 public async void canStore(object sender, EventArgs e){
    //Perform some logic and response result to T3
    if(e is MyCustomEventArgs myCustomEventArgs)
    {
        myCustomEventArgs.ResponseFromSubscriber = new Reponse(); // Your whatever 
        myCustomEventArgs.Completion.SetResult(true); // Triggers the Task completion for your awaiting EventInvoker
    }
}
  

Комментарии:

1. Есть ли какой-либо вред в использовании этого подхода?

2. public Response CanStore(string materialType){ вы не можете использовать await здесь…

3. Я только что заметил асинхронный обработчик событий и пошел по этому пути, чтобы показать, как заставить его вписаться в асинхронную среду. Совсем не обязательно и не потокобезопасно. Даже не сохранить для нескольких подписчиков, все возятся с EventArgs. Просто «заставить это работать сейчас», независимо от хорошей архитектуры.