Интерполяция строк и C#10: рекомендую перечитать хорошую статью https://habr.com/ru/articles/590069/ из которой можно выяснить, что в некоторых случаях даже конкатенация строк может не выполнятся.
Управление вызывающему методу возвращается первым же await
То что await != wait никто и не спорит. Если это хитрая конструкция для принудительного завершения метода асинхронно, то есть более красивый и лаконичный метод Task.Yield.
Дополнение во втором комменте про контекст синхронизации - да, возможно эту проблему так решали.
Если сделать предположение что команды и обработчики живут в одной assembly, то запускается три раза регистрация одного и того-же.
Если сделать предположение что команды живут в одной assembly, а соответствующие им обработчики живут в другой assembly, как делают при выделении контракта взаимодействия в отдельный сборку, предложенный вариант работать не будет. Я бы рекомендовал в строке Assembly.GetAssembly(typeof(...))); указывать класс обработчика.
return await Task.Run<ActionResult>(async() => { бессмысленное и беспощадное решение. Создаем отдельный таск, через замыкания передаем в него аргументы чтобы подождать пока таск завершит свою работу и вернуть управление вызывающему методу.
Как уже было сказано в комментарии выше, проверять статус результата по префиксу не лучший способ. Возможно будет красивее возвращать не строку, а класс ответа, в котором будет признак успешности бизнес составляющей обработки.
public class StartRequest : IRequest<StartResponse> { }
public class BaseRequest { }
public class StartResponse : BaseResponse { }
public class BaseResponse
{
bool IsSuccess;
string Error;
}
Про post обработку сообщений медиатора озвучили, но почему бы не использовать тот же самый подход в ASP контроллере и не обрабатывать ошибки в middleware ? Код станет чище и лаконичнее.
IConfig = new AppConfiguration() Я не знаю вашего решения, но обычно конфигурации приложения и тд извлекается через ServiceProvider а не передаются с командой, но могут быть исключения.
не скажу уже точно по какой причине, но всегда включаю и build и buildTransitive причем из второго запускаю импорт первого, а само копирование делаю именно в build. Возможно buildTransitive не запускается если пакет подключен непосредственно к "исполняемому проекту".
Есть еще некоторое "неудобство" предложенного решения. Если подключить такой проект, например, к юнит тесту в этом же solution как project reference, то в целевом каталоге не будет нужных артефактов. Они появятся только при подключении как packageReference, т.е. нужно будет использовать какой-то, например локальный, репозиторий пакетов.
добавлю для несведущих, что имя <package>.props должно строго совпадать с именем выпускаемого пакета
я бы добавил <ItemGroup Condition="'$(_IsExecutable)' == 'true'"> для копирования файловых ресурсов только в "конечные" исполняемые проекты (в том числе тесты) а не во все промежуточные ClassLibrary, где они используются
магию создания пакетов можно применять и без nuspec. dotnet pack в большинстве случаев все делает сам. рекомендую посмотреть в сторону свойства IsPackable
при создании data пакетов магией dotnet pack обратите внимание на свойства IncludeBuildOutput и SuppressDependenciesWhenPacking
просмотр свойств - рекомендую собрать проект из командной строки с ключиком -bl и воспользоваться замечательным инструментом MSBuild Structured Log Viewer
вопрос скорее религии, но для создание артефактов проекта я агитирую за dotnet publish и dotnet pack а не создавать артефакты при каждой сборке проекта, так как придерживаюсь позиции, что сборка должна происходить как можно быстрее для уменьшения требуемого времени для запуска юнит тестов.
А можно чуть подробнее пояснить, по какой причине потребовалось реализация собственного микро-хоста? Единственный вариант, который я могу предложить, это реализация мелких утилит командной строки, но по причине требуемого runime размер все равно будет выходить негуманным.
Предложенный код требует copy paste либо оформление в виде собственной реализации Host подобной конструкции
Для консольных приложений отлично подходит такой способ инициализации и использования DI
Есть возможность использовать единые механизмы регистрации и инициализации сервисов как для минималистических консольных приложений так и для обычных сервисов / приложений.
Из коробки присутствует LogFactory
Из коробки штатная обработка Ctrl+C (в том числе через IHostApplicationLifetime)
Из коробки вкусности типа IHostedService, как некоторый аналог IApplicationRunner
Все сказанное ниже вкусовщина, но, как я считаю, при работе с изображениями и видеопотоками нужно более бережно обращаться с ресурсами во избежание проблем.
MediaFileProcess - содержит Disposible свойства, но не реализует интерфейс
MediaFileProcess.ctor
Создается Disposible объект Process, но уничтожение происходит внутри одного из методов а не в методе Dispose класса
InputStreams - не определено время жизни Stram'ов. Соответственно, если это файловый stram, невозможно определить момент освобождения файла. Если предполагается, что передается пользование а не владение потоками, то на вызывающей стороне нигде не обнаружил этому подтверждение.
MediaFileProcess.ProcessOnExited - целесообразность метода вызывает вопрос. возможно, планировалось в нем сделать очистку зарегистрированных обработчиков событий, но сейчас необходимость в нем неочевидна.
MediaFileProcess.ExecuteAsync
метод можно вызвать только один раз. при повторном вызове будут исключительные ситуации
спорное решение с обработкой всего ввода-вывода в синхронном потоке.
MediaFileProcess.Run
cancellationToken.Register может привести к утечке памяти, так как через cancellationToken будут заблокированы от финализации Process, Settings, InputStreams и тд.
Task.Run(WriteStandartInput, cancellationToken) - cancellationToken будет использоваться только как токен отмены создания таска, а не исполнения таска.
при аварийном закрытии приложения дочерние процессы могут остаться живыми. Для Windows рекомендую посмотреть в сторону kernel32 CreateJobObject.
MediaFileProcess.MultiInputWindowsOS - созданным pipe'ам не плохо бы сделать Dispose.
MediaFileProcess.MultiInpuLinuxOS
вероятность словить исключение при удалении файла стремится к 100%
не понятно как созданные файлы должны обрабатываться, ведь сразу после создания они удаляются.
MediaFileProcess.ReadStandartOutput - реализован метод Stream.CopyTo
по опыту, возможно возникновение OOM из-за подходов работы с объектами, попадающими в LOH. Вместо использования byte[] размером более 80 кб я бы посоветовал посмотреть в сторону System.Buffers.
using Nerdbank.Streams;
using System.Buffers;
var seq = new Sequence<byte>();
while (true)
{
var buffer = new byte[64 * 1024];
var lastRead = Process.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length);
if (lastRead == 0)
break;
seq.Append(buffer[..lastRead]);
}
return ((ReadOnlySequence<byte>)seq).AsStream();
Сугубо личное мнение по второй части в порядке уменьшения важности:
Придерживаясь подобного контракта мы всегда можем получить ответ на вопрос "задача успела завершится штатно до получения токена отмены или нет". Исключением является задача, в которой обрабатывается некоторый поток событий.
Используется только один TaskCompletionSource (Экономия на спичках)
У CancellationToken в StopAsync есть 5 секунд для корректного завершения
Важное уточнение, что это время задается HostOptions.ShutdownTimeout и задается для всего хоста целиком я не для каждого сервиса.
Метод ExecuteAsync в некотором роде нарушает контракт. При получении токена отмены он говорит "я завершился" а не OperationCanceledException. Я бы предложил следующее видение кода:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await WaitForAppStartup(lifetime, stoppingToken);
....
}
static async Task WaitForAppStartup(IHostApplicationLifetime lifetime, CancellationToken stoppingToken)
{
var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
using var reg1 = lifetime.ApplicationStarted.Register(() => tcs.TrySetResult());
using var reg2 = stoppingToken.Register(() => tcs.TrySetCanceled());
await tcs.Task;
}
Про метод Do я и не спорю. Автор поспешил и, возможно, не очень хорошо разбирается в магии async/await
А, главное, если моя задача в том, чтобы запросы выполнялись быстрее, как мне это поможет?
Запрос, в общем случае выполнится медленнее, из за накладных расходов на работу с тасками.
Но в зависимости от специфики приложения, приложение в целом может начать работать быстрее.
Так автор и не говорит, что он быстрее.
Он говорит, что операция может быть выполнена асинхронно, и на время ожидания ответа от сервера поток будет освобожден для выполнения других задач.
А мне вот интересно, если бы в статье был описан более частный случай с несогласованностью кодировок UTF-16 в приложении, UTF-8 в «консоли» Вы бы тоже писали, что эта проблема вот уже 100 лет как решена в linux, а Microsoft только на бабки пользователей разводит? :)
Ну и как я писал выше — путаете набор символов (char set) и кодировку (encoding).
Данные скрипты один из кирпичиков для Continuous Deployment, соответственно стояла задача сделать все логи в нормальной кодировке, чтобы при разборе проблем уметь сразу всю необходимую информацию.
Интерполяция строк и C#10: рекомендую перечитать хорошую статью https://habr.com/ru/articles/590069/ из которой можно выяснить, что в некоторых случаях даже конкатенация строк может не выполнятся.
То что
await != wait
никто и не спорит.Если это хитрая конструкция для принудительного завершения метода асинхронно, то есть более красивый и лаконичный метод
Task.Yield
.Дополнение во втором комменте про контекст синхронизации - да, возможно эту проблему так решали.
Неоднократная и хрупкая регистрация обработчиков:
Если сделать предположение что команды и обработчики живут в одной assembly, то запускается три раза регистрация одного и того-же.
Если сделать предположение что команды живут в одной assembly, а соответствующие им обработчики живут в другой assembly, как делают при выделении контракта взаимодействия в отдельный сборку, предложенный вариант работать не будет. Я бы рекомендовал в строке
Assembly.GetAssembly(typeof(...)));
указывать класс обработчика.return await Task.Run<ActionResult>(async() => {
бессмысленное и беспощадное решение. Создаем отдельный таск, через замыкания передаем в него аргументы чтобы подождать пока таск завершит свою работу и вернуть управление вызывающему методу.Как уже было сказано в комментарии выше, проверять статус результата по префиксу не лучший способ. Возможно будет красивее возвращать не строку, а класс ответа, в котором будет признак успешности бизнес составляющей обработки.
Про post обработку сообщений медиатора озвучили, но почему бы не использовать тот же самый подход в ASP контроллере и не обрабатывать ошибки в middleware ? Код станет чище и лаконичнее.
IConfig = new AppConfiguration()
Я не знаю вашего решения, но обычно конфигурации приложения и тд извлекается через ServiceProvider а не передаются с командой, но могут быть исключения.и на нем
dotnet pack
не скажу уже точно по какой причине, но всегда включаю и
build
иbuildTransitive
причем из второго запускаю импорт первого, а само копирование делаю именно вbuild
. ВозможноbuildTransitive
не запускается если пакет подключен непосредственно к "исполняемому проекту".Есть еще некоторое "неудобство" предложенного решения. Если подключить такой проект, например, к юнит тесту в этом же solution как project reference, то в целевом каталоге не будет нужных артефактов. Они появятся только при подключении как packageReference, т.е. нужно будет использовать какой-то, например локальный, репозиторий пакетов.
добавлю для несведущих, что имя
<package>.props
должно строго совпадать с именем выпускаемого пакетая бы добавил
<ItemGroup Condition="'$(_IsExecutable)' == 'true'">
для копирования файловых ресурсов только в "конечные" исполняемые проекты (в том числе тесты) а не во все промежуточные ClassLibrary, где они используютсямагию создания пакетов можно применять и без
nuspec
.dotnet pack
в большинстве случаев все делает сам. рекомендую посмотреть в сторону свойстваIsPackable
при создании data пакетов магией
dotnet pack
обратите внимание на свойстваIncludeBuildOutput
иSuppressDependenciesWhenPacking
просмотр свойств - рекомендую собрать проект из командной строки с ключиком
-bl
и воспользоваться замечательным инструментомMSBuild Structured Log Viewer
вопрос скорее религии, но для создание артефактов проекта я агитирую за
dotnet publish
иdotnet pack
а не создавать артефакты при каждой сборке проекта, так как придерживаюсь позиции, что сборка должна происходить как можно быстрее для уменьшения требуемого времени для запуска юнит тестов.А можно чуть подробнее пояснить, по какой причине потребовалось реализация собственного микро-хоста? Единственный вариант, который я могу предложить, это реализация мелких утилит командной строки, но по причине требуемого runime размер все равно будет выходить негуманным.
Предложенный код требует copy paste либо оформление в виде собственной реализации Host подобной конструкции
Для консольных приложений отлично подходит такой способ инициализации и использования DI
Есть возможность использовать единые механизмы регистрации и инициализации сервисов как для минималистических консольных приложений так и для обычных сервисов / приложений.
Из коробки присутствует LogFactory
Из коробки штатная обработка Ctrl+C (в том числе через IHostApplicationLifetime)
Из коробки вкусности типа IHostedService, как некоторый аналог IApplicationRunner
Готовые подходы интеграции с System.CommandLine
Все сказанное ниже вкусовщина, но, как я считаю, при работе с изображениями и видеопотоками нужно более бережно обращаться с ресурсами во избежание проблем.
MediaFileProcess - содержит Disposible свойства, но не реализует интерфейс
MediaFileProcess.ctor
Создается Disposible объект Process, но уничтожение происходит внутри одного из методов а не в методе Dispose класса
InputStreams - не определено время жизни Stram'ов. Соответственно, если это файловый stram, невозможно определить момент освобождения файла. Если предполагается, что передается пользование а не владение потоками, то на вызывающей стороне нигде не обнаружил этому подтверждение.
MediaFileProcess.ProcessOnExited - целесообразность метода вызывает вопрос. возможно, планировалось в нем сделать очистку зарегистрированных обработчиков событий, но сейчас необходимость в нем неочевидна.
MediaFileProcess.ExecuteAsync
метод можно вызвать только один раз. при повторном вызове будут исключительные ситуации
спорное решение с обработкой всего ввода-вывода в синхронном потоке.
MediaFileProcess.Run
cancellationToken.Register может привести к утечке памяти, так как через cancellationToken будут заблокированы от финализации Process, Settings, InputStreams и тд.
Task.Run(WriteStandartInput, cancellationToken) - cancellationToken будет использоваться только как токен отмены создания таска, а не исполнения таска.
при аварийном закрытии приложения дочерние процессы могут остаться живыми. Для Windows рекомендую посмотреть в сторону kernel32 CreateJobObject.
MediaFileProcess.MultiInputWindowsOS - созданным pipe'ам не плохо бы сделать Dispose.
MediaFileProcess.MultiInpuLinuxOS
вероятность словить исключение при удалении файла стремится к 100%
не понятно как созданные файлы должны обрабатываться, ведь сразу после создания они удаляются.
MediaFileProcess.ReadStandartOutput - реализован метод Stream.CopyTo
по опыту, возможно возникновение OOM из-за подходов работы с объектами, попадающими в LOH. Вместо использования byte[] размером более 80 кб я бы посоветовал посмотреть в сторону System.Buffers.
внутри реализован как
Т.е. внутри него и происходит ожидание сигнала, что хост завершил свою работу.
Сугубо личное мнение по второй части в порядке уменьшения важности:
Придерживаясь подобного контракта мы всегда можем получить ответ на вопрос "задача успела завершится штатно до получения токена отмены или нет". Исключением является задача, в которой обрабатывается некоторый поток событий.
Используется только один TaskCompletionSource (Экономия на спичках)
Код получается более лаконичным.
Важное уточнение, что это время задается HostOptions.ShutdownTimeout и задается для всего хоста целиком я не для каждого сервиса.
Метод ExecuteAsync в некотором роде нарушает контракт.
При получении токена отмены он говорит "я завершился" а не OperationCanceledException.
Я бы предложил следующее видение кода:
В 2006-2007 годы коллеги тестировали клеммники.
https://mastergrad.com/forums/tпро испытания клеммников WAGO и не WAGO
Про метод Do я и не спорю. Автор поспешил и, возможно, не очень хорошо разбирается в магии async/await
Запрос, в общем случае выполнится медленнее, из за накладных расходов на работу с тасками.
Но в зависимости от специфики приложения, приложение в целом может начать работать быстрее.
Он говорит, что операция может быть выполнена асинхронно, и на время ожидания ответа от сервера поток будет освобожден для выполнения других задач.
Необходимо реализовать у объекта интерфейс IDispatch. В C# с использованием рефлексии реализация доступа к методам и свойствам будет элементарен.
А чем этот момент не устроил в ActiveScriptSite? В ошибке будет указан и номер строки и что за ошибка.
С этим вопросом тоже все очень просто.
https://docs.microsoft.com/en-us/scripting/winscript/active-script-debugging-overview
Ну и как я писал выше — путаете набор символов (char set) и кодировку (encoding).