#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. Просто «заставить это работать сейчас», независимо от хорошей архитектуры.