Pull to refresh
15
0
Кудрявцев Денис @kuda78

User

Send message

Интерполяция строк и C#10: рекомендую перечитать хорошую статью https://habr.com/ru/articles/590069/ из которой можно выяснить, что в некоторых случаях даже конкатенация строк может не выполнятся.

Управление вызывающему методу возвращается первым же await

То что await != wait никто и не спорит.
Если это хитрая конструкция для принудительного завершения метода асинхронно, то есть более красивый и лаконичный метод Task.Yield.

Дополнение во втором комменте про контекст синхронизации - да, возможно эту проблему так решали.

  1. Неоднократная и хрупкая регистрация обработчиков:

        container.RegisterInstance<TextWriter>(new StringWriter(sb))
                    .RegisterMediator(new HierarchicalLifetimeManager())
                    .RegisterMediatorHandlers(
                        Assembly.GetAssembly(typeof(StartRequest)));

        container.RegisterInstance<TextWriter>(new StringWriter(sb))
                    .RegisterMediator(new HierarchicalLifetimeManager())
                    .RegisterMediatorHandlers(
                        Assembly.GetAssembly(typeof(RejectRequest)));

         container.RegisterInstance<TextWriter>(new StringWriter(sb))
                   .RegisterMediator(new HierarchicalLifetimeManager())
                   .RegisterMediatorHandlers(
                        Assembly.GetAssembly(typeof(ApproveRequest)));
  • Если сделать предположение что команды и обработчики живут в одной assembly, то запускается три раза регистрация одного и того-же.

  • Если сделать предположение что команды живут в одной assembly, а соответствующие им обработчики живут в другой assembly, как делают при выделении контракта взаимодействия в отдельный сборку, предложенный вариант работать не будет. Я бы рекомендовал в строке Assembly.GetAssembly(typeof(...))); указывать класс обработчика.

  1. return await Task.Run<ActionResult>(async() => { бессмысленное и беспощадное решение. Создаем отдельный таск, через замыкания передаем в него аргументы чтобы подождать пока таск завершит свою работу и вернуть управление вызывающему методу.

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

public class StartRequest : IRequest<StartResponse> { }

public class BaseRequest { }

public class StartResponse : BaseResponse {  }

public class BaseResponse 
{
  bool IsSuccess;
  string Error;
}
  1. Про post обработку сообщений медиатора озвучили, но почему бы не использовать тот же самый подход в ASP контроллере и не обрабатывать ошибки в middleware ? Код станет чище и лаконичнее.

  2. IConfig = new AppConfiguration() Я не знаю вашего решения, но обычно конфигурации приложения и тд извлекается через ServiceProvider а не передаются с командой, но могут быть исключения.

test.csproj
<Project Sdk="Microsoft.NET.Sdk">

	<PropertyGroup>
		<TargetFramework>net6.0</TargetFramework>
		<IsPackable>true</IsPackable>
		<IncludeBuildOutput>false</IncludeBuildOutput>
		<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
	</PropertyGroup>

	<ItemGroup>
		<Content Include="$(MSBuildThisFileFullPath)" Pack="true" PackagePath="none\$(PackageId).props" PackageCopyToOutput="true" CopyToOutputDirectory="Always"/>
	</ItemGroup>
</Project>

и на нем dotnet pack

  1. не скажу уже точно по какой причине, но всегда включаю и build и buildTransitive причем из второго запускаю импорт первого, а само копирование делаю именно в build. Возможно buildTransitive не запускается если пакет подключен непосредственно к "исполняемому проекту".

  2. Есть еще некоторое "неудобство" предложенного решения. Если подключить такой проект, например, к юнит тесту в этом же solution как project reference, то в целевом каталоге не будет нужных артефактов. Они появятся только при подключении как packageReference, т.е. нужно будет использовать какой-то, например локальный, репозиторий пакетов.

  1. добавлю для несведущих, что имя <package>.props должно строго совпадать с именем выпускаемого пакета

  2. я бы добавил <ItemGroup Condition="'$(_IsExecutable)' == 'true'"> для копирования файловых ресурсов только в "конечные" исполняемые проекты (в том числе тесты) а не во все промежуточные ClassLibrary, где они используются

  3. магию создания пакетов можно применять и без nuspec. dotnet pack в большинстве случаев все делает сам. рекомендую посмотреть в сторону свойства IsPackable

  4. при создании data пакетов магией dotnet pack обратите внимание на свойства IncludeBuildOutput и SuppressDependenciesWhenPacking

  5. просмотр свойств - рекомендую собрать проект из командной строки с ключиком -bl и воспользоваться замечательным инструментом MSBuild Structured Log Viewer

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

А можно чуть подробнее пояснить, по какой причине потребовалось реализация собственного микро-хоста? Единственный вариант, который я могу предложить, это реализация мелких утилит командной строки, но по причине требуемого runime размер все равно будет выходить негуманным.

  • Предложенный код требует copy paste либо оформление в виде собственной реализации Host подобной конструкции

  • Для консольных приложений отлично подходит такой способ инициализации и использования DI

_host = Host.CreateDefaultBuilder()
    .ConfigureLogging(cfg => cfg.ClearProviders()...)
    .ConfigureAssetManager(cfg => cfg.AddFileProvider())
    .Build();

_assetManager = _host.Services.GetRequiredService<IAssetsManager>();

...

public static IHostBuilder ConfigureAssetManager(this IHostBuilder hostBuilder, ...)
{
...
}
  • Есть возможность использовать единые механизмы регистрации и инициализации сервисов как для минималистических консольных приложений так и для обычных сервисов / приложений.

  • Из коробки присутствует 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.

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

await host.RunAsync();

внутри реализован как

await host.StartAsync(token).ConfigureAwait(false);
await host.WaitForShutdownAsync(token).ConfigureAwait(false);

Т.е. внутри него и происходит ожидание сигнала, что хост завершил свою работу.

Сугубо личное мнение по второй части в порядке уменьшения важности:

  • Придерживаясь подобного контракта мы всегда можем получить ответ на вопрос "задача успела завершится штатно до получения токена отмены или нет". Исключением является задача, в которой обрабатывается некоторый поток событий.

  • Используется только один 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

А, главное, если моя задача в том, чтобы запросы выполнялись быстрее, как мне это поможет?

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

Необходимо реализовать у объекта интерфейс IDispatch. В C# с использованием рефлексии реализация доступа к методам и свойствам будет элементарен.

проводить синтаксический контроль исходного скрипта и генерировать сообщения о синтаксических ошибках

А чем этот момент не устроил в ActiveScriptSite? В ошибке будет указан и номер строки и что за ошибка.

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

С этим вопросом тоже все очень просто.
https://docs.microsoft.com/en-us/scripting/winscript/active-script-debugging-overview
А мне вот интересно, если бы в статье был описан более частный случай с несогласованностью кодировок UTF-16 в приложении, UTF-8 в «консоли» Вы бы тоже писали, что эта проблема вот уже 100 лет как решена в linux, а Microsoft только на бабки пользователей разводит? :)

Ну и как я писал выше — путаете набор символов (char set) и кодировку (encoding).
Данные скрипты один из кирпичиков для Continuous Deployment, соответственно стояла задача сделать все логи в нормальной кодировке, чтобы при разборе проблем уметь сразу всю необходимую информацию.
Так большая же часть программ написана вот таким образом:
	static void Main(string[] args)
	{
		Console.WriteLine("c'est-à-dire хелло Βικιπαίδεια");
	}
Самая большая проблема данного решения — требует изменения кода устанавливаемого приложения. Это далеко не всегда возможно.

Information

Rating
Does not participate
Registered
Activity