Ответы пользователя по тегу C#
  • Можете оценить уровень кода?

    @basrach
    Пунктуация и стилистика бросается в глаза, вы можете аргументировать типа "не на писателя устраиваюсь же", но встречают по одежке, знаете ли...
    По коду.
    Вот вы говорите DDD, а похоже на Transaction Script. Поскольку в классе FeedLoader в каждом не-query методе происходит сохранение изменений, т.е. нет понятия бизнес транзакции. В данном случае это не особо нужно, но раз уж вы про DDD заикались... Также богатой изолированной модели не видно. Возможно в данном случае опять же эту богатую модель неоткуда взять, потому как предметная область тривиальная. Но тогда выходит, что вы выбрали неправильный способ решения задачи. В модуле Core у вас какие-то классы, очевидно для сериализации, непонятно что они делают в Core. Да и вообще, если бы увидел код без описания, мне бы никогда даже не закралась мысль, что его создавали по DDD.
    API не REST, хотя в требованиях было про REST.
    Нет конфигурирования кэша, что, где и насколько кэшировать. При этом само кэширование реализовано в BusinessLayer, что странно, поскольку кэш, как и, например, DataAccess, это чисто инфраструктурные вещи.
    Логирование только в API и только ошибок.
    В тестах нет моков. И похоже что тестирование только интеграционное, т.е. без базы данных не получится протестировать ничего.
    Пустой catch в ApiTester. Да, это не основная кодовая база, но когда не работает и при этом не сообщает почему не работает, всегда бесит, даже если это тесты.
    Итоговая оценка зависит от ваших ожиданий и ожиданий ревьюверов. Если вы junior, и на эту же то позицию претендовали, то вполне неплохо. Если senior, то зависит от уровня ревьюверов/проекта/компании и т.д.
    Ответ написан
    1 комментарий
  • C# Watchdog для потока?

    @basrach
    как из дочернего потока завершить родительский.

    Потоки не имеют отношений между собой типа родительский/дочерний. Соответственно завершайте так как и всегда:
    var parentThread = new Thread(..
    ...
    parentThread.Abort();
    Ответ написан
    Комментировать
  • Что исправить в строке подключения к базе данных Entity Framework C#?

    @basrach
    Для того чтобы переносить базу данных вам нужно выбрать что использовать для начала:
    1) Sql Server CE (Compact Edition) - урезанный движок, который можно таскать с собой в виде библиотеки. Но он deprecated с 13 года.
    2 ) Sql Server LocalDB. Но его нужно устанавливать отдельно на каждой машине. Он поставлялся раньше вместе с VisualStudio, сейчас не знаю.
    3) sqlite, которая будет работать везде, но у нее есть свои минусы.
    4) Использовать обычный инстанс Sql Server (возможно Express, не важно), и таскать с собой только базу.
    В зависимости от того что вы выберете будет меняться строка подключения. Сейчас она у вас для обычного инстанса Sql Server:
    data source=DESKTOP-K4U1293;initial catalog=IPhoneDB;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework
    .
    Например для варианта 4, нужно ее изменить на:
    Server=DESKTOP-K4U1293;AttachDbFilename=|DataDirectory|mydbfile.mdf;Database=IPhoneDB;
    Trusted_Connection=Yes;
    .
    Для варианта 2:
    Server=(localdb)\v11.0;Integrated Security=true;
    AttachDbFileName=|DataDirectory|mydbfile.mdf;

    Вт тут много примеров.
    Ответ написан
    Комментировать
  • Как сделать задержку с интервалом?

    @basrach
    Красиво будет сделать вот так:
    1) Установить пакет System.Reactive
    2) Дописать:

    ...
    private Subject<int> myHotkey = new Subject<int>();
    
    public MainForm()
    {
        ...
    
        myHotkey
            .AsObservable()
            .Throttle(TimeSpan.FromSeconds(1))
            .Subscribe(hotkeyid => MessageBox.Show("Нажата горячая клавиша с ID:  " + hotkeyid));
    }
    
    ...
    
    protected override void WndProc(ref Message keyPressed)
    {
        ...
                switch (keyPressed.WParam.ToInt32())
                {
                    case 1: 
                    {
                        SetPrtSc(); 
                        myHotkey.OnNext(keyPressed.WParam.ToInt32());
                    }
                    break;
    ...
    Ответ написан
    Комментировать
  • Как передать функцию в c#?

    @basrach
    Delegate (с большой буквы) - это статический класс, которые содержит методы для работы с делегатами. Сам же делегат нужно объявить через ключевое слово delegate (с маленькой буквы), как класс или интерфейс. Например:
    public delegate void TestDelegate(string message);
    При этом использовать вы можете только конкретный тип делегата, точно также как вы используете конкретный MyClassи т.п., а не само ключевое слово class. В приведенном примере тип делегата будет TestDelegate. Чтобы передать с помощью делегата ссылку на функцию, сигнатуры функции и делегата должны полностью совпадать. В случае приведенного примера TestDelegate, функция должна также возвращать voidи принимать string.

    В вашем случае тип делегата должен быть такой: public delegate void MyDelegate(); Этому делегату вы можете присвоить ссылку на ваш метод void init().
    Но свой тип делегаты в данном случае объявлять нет необходимости. В Net. Framework уже есть целый ряд делегатов, как раз для таких целей. Это Func и Action.
    Вы можете просто заменить слово Delegate на Actionв вашем коде, и всё должно работать.
    Ответ написан
    Комментировать
  • Как подставить header в POST, C#?

    @basrach
    1. Сначала вам нужно разобраться какой тип авторизация используется: Basic Auth, Bearer Token, NTLM и т.д.
    2. Начинать писать код:

    using System.Net.Http;

    Выполнение запроса без авторизации:
    var client = new HttpClient();
    client.DefaultRequestHeaders.Add("MyHeader", "MyHeaderValue");
    var response = client.PostAsync("https://toster.ru", new StringContent("{\"Data\": \"Test\"}")).Result;
    	
    Console.WriteLine(response.Content.ReadAsStringAsync().Result);


    Basic авторизация:
    var username = "username";
    var password = "password";
    	
    var authValue = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{username}:{password}"));
    	
    var client = new HttpClient();	
    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authValue);
    client.DefaultRequestHeaders.Add("MyHeader", "MyHeaderValue");
    var response = client.PostAsync("https://toster.ru", new StringContent("{\"Data\": \"Test\"}")).Result;
    	
    Console.WriteLine(response.Content.ReadAsStringAsync().Result);


    Bearer token:
    var token = "token"; // вам нужно где-то получить токен
     	
    var client = new HttpClient();	
    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
    client.DefaultRequestHeaders.Add("MyHeader", "MyHeaderValue");
    var response = client.PostAsync("https://toster.ru", new StringContent("{\"Data\": \"Test\"}")).Result;
    	
    Console.WriteLine(response.Content.ReadAsStringAsync().Result);


    Если нужно для NTLM, могу скинуть пример, но навряд ли у вас этот случай.
    Ответ написан
    Комментировать
  • Как контролировать память кортежей?

    @basrach
    В приведенном примере проблемы нет, утечки быть не может. Если утечка 100% есть, то скорее всего, когда создавали этот пример на основе реального кода, вы упустили важную деталь из-за которой и происходит утечка.
    Ответ написан
  • Как удалить первые 12 байт массива?

    @basrach
    byte[] array = ...
    array = array.Skip(12).ToArray();
    Ответ написан
    Комментировать
  • Как использовать async/await в DI контейнере ASP.NET CORE?

    @basrach
    Посмотрите на сигнатуру второго вызова AddScoped. В Visual Studio это можно сделать наведя указатель на метод.
    Она будет такой: AddScoped>(x => ... Т.е. в контейнере регистрируется не Some, а Task. И это нормально, асинхронный метод всегда возвращает Task. Чтобы зарегистрировать Some, нужно чтобы лямбда внутри AddScoped была синхронная, этого можно достичь написав либо ...GetSomeAsync().Result, либо еще как-то получить результат, и непосредственно его уже возвратить.
    Ответ написан
    Комментировать
  • Нарушаю ли я ООП используя partial классы?

    @basrach
    Нет.
    Partail классы это механизм компилятора. А ООП стиль написания программ. Понятия из разных областей и не пересекаются никак.
    Ответ написан
    Комментировать
  • Как правильно написать метод?

    @basrach
    Ха!
    Методы #2, #3 и #4 абсолютно одинаковые, но результаты в таблице для #2 отличаются от последних в 6 раз.
    Это значит, что ваши замеры годятся только чтобы подтереть кое-что.
    Ответ написан
    Комментировать
  • Как сделать повторный вызов метода?

    @basrach
    Если хочется "четко и красава чтоб не хуже чем у пацанов", гляньте на либу Polly:
    public async Task<List<AccountListView>> GetAsync()
    {
    	var policy = Policy
    		.Handle<UnauthorizedException>()
    		.RetryAsync(
    			retryCount: 1,
    			onRetryAsync: (e, i) => Context.Renew());
    
    	return await policy.ExecuteAsync(TryGetAccountListAsync);
    }


    Созданную policy можно переиспользовать. Т.е. вам не придется больше плодить копипасту, достаточно объявить одну политику на приложение, и дальше просто вызывайте то что требует обновления токена через эту политику.
    Ответ написан
    2 комментария
  • Как ограничить количество одновременно запускаемых потоков?

    @basrach
    Самый простой вариант это использовать Parallel.Foreach.
    var myEntities = new MyEntity[100];
    var maxThreads = 15;
    
    System.Threading.Tasks.Parallel.ForEach(
        myEntities,
        new System.Threading.Tasks.ParallelOptions { MaxDegreeOfParallelism = maxThreads },
        entity =>
        {
            entity.Do()
        });


    Второй вариант использовать Semaphore или SemaphoreSlim.
    var myEntities = new MyEntity[100];
    var maxThreads = 15;	
    
    var semaphoreSlim = new SemaphoreSlim(maxThreads);
    var tasks = new List<Task>(myEntities.Length);
    foreach (var entity in myEntities)
    {
    	tasks.Add(Task.Run(() =>
    	{
    		semaphoreSlim.Wait();
    		try
    		{
    			entity.Do();
    		}
    		finally
    		{
    			semaphoreSlim.Release();	
    		}
    	}));
    }
    
    Task.WaitAll(tasks.ToArray());


    Если вы хотите использовать класс Thread напрямую, то
    var myEntities = new MyEntity[100];
    var maxThreads = 3;	
    
    var semaphore = new Semaphore(maxThreads, maxThreads);
    var threads = new List<Thread>(myEntities.Length);
    foreach (var entity in myEntities)
    {
    	var thread = new Thread(() =>
    	{
    		semaphore.WaitOne();
    		try
    		{
    			entity.Do();
    		}
    		finally
    		{
    			semaphore.Release();
    		}
    	});
    	
    	threads.Add(thread);
    	
    	thread.Start();
    }
    
    foreach (var thread in threads)
    	thread.Join();


    Хотя из условий задачи кажется, что здесь вполне можно обойтись Task. Task предпочтительней еще и по той причине, что потоки переиспользуются. Т.е. задействованных потоков будет столько, сколько одновременно выполняется задач, например 15. В случае же создания потока напрямую через класс Thread, 15 будут работать, остальные просто ждать, при этом все равно расходуя ресурсы.
    Ответ написан
    Комментировать
  • Почему не инициализируются свойства сервиса при добавлении через AddTransient в ASP.NET Core?

    @basrach
    Потому что встроенный DI контейнер ASP. Net Core не поддерживает инъекцию свойств и полей.
    А методом services.Configure вы на самом деле регистрируете опции, которые можно потом получить так: IOptions
    Т.е. вам нужно сделать так:
    services.AddTransient<IEmailService, EmailService>();
    services.Configure<EmailServiceOptions>(options =>
    {
        options._emailAddress = "email";
        options._emailPassword = "password";
        options._name = "name";
    });
    
    class EmailService
    {
      public EmailService(IOptions<EmailServiceOptions> optionsAccessor)
      {
        _emailAddress = optionsAcessor.Value.EmailAddress;
        ....
      }
    }
    Ответ написан
    1 комментарий
  • Почему не работает коммуникация между клиентом и сервером?

    @basrach
    Навскидку есть несколько подозрительных мест:
    1. Флаг _isWaitResponce в методе SendMessageFromQueue класса TcpCommunicatorBase, который запрещает отправку если поднят (имеет значение true). При этом сбрасывается он только после получения ответа, и то не гарантированно, для этого нужно чтобы выполнился ряд условий, и он не сбросится в случае исключения. Таким образом этот флаг может быть причиной ошибки, если клиент не получил ответа после отправки, либо при получении ответа произошла ошибка.
    2. Флаг inRead в методе BeginListenForMessage того же класса. Этот флаг также запрещает чтение если поднят, и гарантированно не сбрасывается. Т.е. может остаться в состоянии true если произошло исключение про чтении сообщения. Соответственно сломанное чтение на клиенте может заблокировать отправку.
    3. В CustomSynchronizationContext с хабра есть баг, когда сообщение может висеть в очереди потока и не обрабатываться до тех пор, пока не придет еще одно.

    PS.
    Имхо, в целом код не выглядит надежным. Когда отправка завязана на получение, и все это в одном классе, и для обоих процессов используются те же переменные, всё это резко усложняет понимание и отладку, и снижает надежность кода.
    Ответ написан
    Комментировать
  • System.IO.FileNotFoundException: "Невозможно загрузить файл или сборку "sorttbls.nlp" или один из зависимых от них компонентов. Что делать?

    @basrach
    ...файл или сборку «sorttbls.nlp» или один из зависимых от них компонентов...
    - чего-то не хватает, либо самого «sorttbls.nlp», либо еще чего-то. Что делать? - искать то, чего недостает. Если вы уже пользуетесь декомпилятором, то в нем должна быть функция просмотра зависимостей, сравните список зависимостей с тем, что у вас в билд директории.
    Ответ написан
    Комментировать
  • Как собрать .NET Core проект под ubuntu в один файл?

    @basrach
    Это относится не только к Core под Ubuntu. К обычному .Net под Windows это тоже относится.
    Решают эту проблему добавлением всех зависимостей исполняемого файла (например .exe) в ресурсы этого самого .exe. Затем при запуске приложения регистрируется обработчик события AppDomain.AssemblyResolve, который достаёт нужную сборку из ресурсов, загружает ее в домен и возвращает ссылку на нее среде. AppDomain.AssemblyResolve вызывается каждый раз когда приложению нужна какая-нибудь зависимость.
    Это просто и несложно реализуется, при этом это не займет много времени.
    Также есть и библиотеки, которые встраиваются в процесс билда и позволяют это дело автоматизировать. Выше уже был упомянут ILMerge, также есть Fody, возможно есть еще какие-то аналоги.
    Ответ написан
    Комментировать
  • Как убрать полный путь до файлов проекта .NET Core в исключениях?

    @basrach
    Эта информация хранится в файлах с расширением *.pdb. Они нужны для дебагера. Удалить часть пути из них невозможно, но можно удалить файл целиком и тогда в исключениях не будет информации об исходном коде вообще, будет только стек вызовов (имя класса, название метода и т.п.). Также можно в .csproj добавить условие, чтобы при релизном билде эти файлы не создавались:
    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
      <DebugType>none</DebugType>
      <DebugSymbols>false</DebugSymbols>
    </PropertyGroup>

    В основном эти файлы нужны для дебага. Без них дебаггер не будет корректно работать, т.е. он не сможет сопоставить в общем случае инструкцию с исходным кодом. Соотвественно не в релизе отключать генерацию этих файлов не стоит.
    Ответ написан
    Комментировать
  • Когда используете принудительную сборку мусора?

    @basrach
    Вызов GC.Collect() нужен для сборки объектов в 1 и во 2 поколениях. То что в 0 поколении и так прекрасно собирается. Но то что переживает 0-вое поколение, т.е. объекты в 1-ом и во 2-ом поколениях, очень редко удостаиваются внимания сборщика, если нет жесткого прессинга по памяти. При этом, очень большие объекты, в том числе массивы, при создании помещаются сразу в кучу во 2-ое поколение.
    Соответственно, нужно чтобы совпало:
    1) В программе есть некоторая операция, которая создает очень большой объект, такой, что он сразу попадает во 2-ое поколение.
    2) Свободной памяти на сервере больше нет, либо очень мало.
    3) Нужно избежать простоя, связанного со сборкой мусора, при запуске следующей подобной операции.
    4) В некоторой точке программы точно можно гарантировать, что нет ни одной ссылки на тот большой объект (иначе GC его не удалит)
    И только тогда можно дернуть GC.Collect(). Иначе смысла вызывать его нет.
    Т.е. вы на сто процентов уверены, что будет задержка вызванная сборкой мусора, и именно тем, что сборщик полезет чистить 1-ое и 2-ое поколение, но вас не устраивает то, когда именно произойдет эта задержка, и вы переносите её на более ранее время (возможно более подходящее/приемлемое) принудительным вызовом GC.Collect().
    Главное нужно понимать, что GC.Collect() волшебным образом не очистит память. Чтобы этот вызов что-то почистил нужно быть на 100% уверены, что в памяти есть объекты на которые нет ссылок, но которые застряли в памяти. Иными словами, что нет утечек памяти. (да да, программы на дот.нет тоже могут течь) Как правило, если устранить утечки, т.е. выстроить правильную работу с памятью, то потребность принудительно указывать сборщику мусора отпадет сама собой.
    Ответ написан
    Комментировать
  • Стоит ли читать книгу Иэн Гриффитса "Программирование C# 5.0"?

    @basrach
    Книгу с названием "Программирование C# 5.0" читать в любом случае уже нежелательно, ибо на дворе C# 7.2 и скоро наступит C# 8.0.
    Данного автора не читал, не скажу за качество, но могу посоветовать:
    для начинающих: Эндрю Троелсен
    для более углубленного изучения: Джеффри Рихтер
    В принципе, если хорошо усвоите эти две книги, то будете свободно владеть языком.
    Ответ написан
    Комментировать