Задать вопрос
Ответы пользователя по тегу ASP.NET
  • Почему я получаю ObjectDisposedException из за DbContext-а? Когда он очищается?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Я не вижу откуда у вас вызывается JobWasExecuted, поэтому ответ пока будет чисто теоретический. Контекст БД (потомок DbContext), получаемый из контейнера DI в настроке по умолчанию (как у вас, судя по написанному), имеет время жизни Scoped. То есть, при обработке запросов к веб-серверу он создается отдельно для каждого запроса и очищается при завершении обработки этого запроса.

    Просто так взять и обратиться к такому контексту из других частей программы, которые могут работать независимо от обработчика запроса (например, из фонового сервиса) нельзя: после завершения запроса получите именно такую ошибку.

    Так что, если вам нужно получить контекст БД с нестандартным временем жизни, придется отказаться от получения его через DI со стандартной настройкой.

    Время жизни контекста, получаемого через DI, можно изменить, но тут надо хорошо понимать, что и как вы делаете. Например, к DbContext нельзя обращаться параллельно из нескольких потоков - но при типичной обработки запроса такого и не случается. Так что у меня есть сомнение что вам стоит менять штатную настройку.

    Штатным способом получения контекстов с нестандартным временем жизни является сервис фабрики на базе DbContextFactory (его тоже можно получить через DI, как - см. документацию). Но следить за временем жизни такого контекста вам придется самостоятельно.
    Ответ написан
    1 комментарий
  • Почему данные из представления не передаются в модель в ASP.NET?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Источник проблемы с привязкой действия: в теле ответа у вас нет ничего, кроме antiforgery token. Так что надо смотреть на клиенте HTML, который сгенерил движок представления - похоже, кривизна там, форма сгенерена неверно.
    Перед просмотром имеет смысл включить для окна/вкладки, где будет страница с формой, средства отладки браузера (F12) и смотреть ошибки, которые они выведут - может, они что найдут в авттоматическом режиме.

    Конкретную причину ошибки я пока не опознаю. Подозрение у меня вызывает атрибут asp-for=Id для первого элемента формы: значения атрибутов tag helper чисто по синтаксису надо указывать в кавычках, потому что они подчиняются тому же синтаксису, что и обычные атрибуты HTML. Но в этом ли причина, утверждать не могу.
    Ответ написан
  • Батчинг входящих запросов с неблокирующим ожиданием?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    А вам точно это нужно?
    Потому что тут у вас появляется нюанс, который не совсем ложится на схему работы веб-приложения: у ваших отдельных запросов появляется общее состояние. Как минимум, это - накапливаемый пакет запросов, а ещё, наверное, в это состояние входит нечто общее для всех запросов для работы с БД: подключение или, если используется Entity Framework, DbContext. Это общее состояние придется как-то хранить, регулировать доступ к нему (DbContext к примеру, параллельный доступ не поддерживает в принципе), и вовремя это состояние удалять. Если посмотреть на стандартные механизмы ASP.NET Core, то сессия (ISession) для этого, наверное, не подойдет - там можно хранить только сереализуемые в байты пассивные объекты, и насчет регулировки доступа там непросто. Подойдет концентратор (Hub) SignalR, у которого есть сохраняемый между вызовами контекст подкючения - но ради него, скорее всего, потребуется менять способ вызова API из браузера: у него там своя клиентская библиотека.
    Ну и, по-любому, как-то надо реализовывать активную часть - которая, собственно, отслеживает пакет изменений и вовремя отправляет его в БД.
    Ваша идея
    Шедуллер в фоне будет периодически читать коллекцю объектов на запись и устанавливать результат выполнения в соотвтвующий TaskComplitionSource.

    мне не совсем нравится. Зачем периодически? Kучше чтобы эта активная часть срабатывала по факту добавления запроса в пакет - например, асинхронно ожидала Task от TaskComplitionSource. который метод добавления запроса завершал бы по факту добавления завершающего пакет запроса. Но и завершение по таймауту тоже предусмотреть надо - по жизни оно всякое бывает: обычно для таких целей используется WaitAny для комбинации основной ожидаемой задачи с Delay по таймауту.
    Ну, а ещё требуется, наверное, чтобы для каждого пользователя состояние было свое. В принципе, это делается, но надо делать. Для SignalR для этого можно использовать Hub.Context.Items - это словарь, который может содержать произвольные объекты, и сохраняется на время действия всего подключения.

    А ещё у меня, в принципе, есть своя самодельная библиотека, которая решает ту же задачу - сохранение контекста сеанса, в том числе - активного, с выполняющимся кодом. Я описывал ее недавно в статьях (кроме основной статьи есть дополнительная) на Хабре. Можете попробовать её, если переделывать API не хочется: она вполне годится для работы с API на базе MVC API Controller. или Minimal API. В принципе, она заточена немного под другую задачу - получние и возврат дополнительных результатов в фоне, но для вашей задачи она тоже подойдет. Напишу тут сразу технические подробности как использовать: ссылку на активную часть, собирающую и отправляющую пакет, можно хранить в IActiveSession.Properties, точно так же, как если бы вы хранили ее в Hub.Context.Items, а обработчик завершения, который прибирает за собой (в SignalR его место в OnDisconnect) - привязать к IActiveSession.CompletionToken через его метод Register. В общее для всез запросов состояние входит свой контейнер сервисов со своей областью действия в течение всего существования состояния, так что, если для работы с БД требуется Scoped-сервис из DI-контейнера, то его можно получить оттуда (в дополнительной статье написано, как, а также написано, как защититься от нежелательного одновременного доступа к такому сервису).

    Только вот библиотека эта, естественно, ни разу не стадартная, use at your own risk. Но если попробуете ее, мне будет интересно, что получилось. В том числе - и обнаруженные ошибки, заодно я и для вас их исправлю :-) .

    PS И неплохо было бы IMHO, чтобы дальнейший диалог, если он будет, шел на русском языке, без "батч", "эвейтить", "шедулер" и прочих транслитераций, IMHO лучше уж по-английски писать, если перевод неизвестен. А то я человек старый, мне читать этот пиджин тяжеловато.

    PPS А ещё благодарю за идею, о том, в какую сторону мне развивать мою библиотеку.
    Ответ написан
    1 комментарий
  • Как правильно разделить логику между контроллерами с usecases?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Абстрактно "правильного" варианта реализации нет, всё зависит от конкретных деталей.
    В частности, посмотрите, используют ли ваши методы действий одни и те же внешние зависимости, или каждый - свои. Во втором случае некотоый смысл разнести их по разным контролерам есть: снизить сложность, которую дают лишние, не обусловленые задачей связи (coupling). А первом случае - нет (но может и появиться по другим основаниям).

    А вся эта теория "чистой архитектуры" - она абстрактная. Ее полезно знать, чтобы иметь возможность рассмотреть больше вариантов решения, и видеть больше про возможных подводных камней, а вот фанатично ей следовать не стоит (если, конечно, ваша задача - сделать что-то полезное, а не спихнуть курсовик и т.п.).
    Ответ написан
    Комментировать
  • Почему не удается подключиться к веб серверу?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Вы делаете приложение на ASP.NET Core? В таком случае это означает, что не запустилось приложение. У вас там должна текстовая консоль при запусе выражения вылезать. Посмотрите, что там написано.
    Или в окне Output/Debug - туда это тоже дублируется.
    Ответ написан
    Комментировать
  • Почему получаю System.InvalidOperationException: No authentication handler is registered for the scheme?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Вы неправильно используете метод ControllerBase.Forbid. Передаваемая в него строка (она, как понимаю, преобразуется в массив строк из одного элемента) - это не сообщение об ошибке, а название использованного метода аутентификации.
    Ответ написан
    Комментировать
  • Как корректно завершить webapplication, чтобы вызвался и исполнился до конца IHostLifetime.ApplicationStopping.Register(mymethod)?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Для ответа на вопрос несколько не хватает информации - как у вас организована реакция на отмену IHostApplicationLifetime.ApplicationStoping (что и как делает регистрируемый callback при отмене), какие сервисы вы используете и как они реагируют на отмену того же маркера.
    И IMHO информацию лучше собирать методом проб и ошибок. Самое первое, что вы можете попробовать - это такой вариант. Во-первых, нужно зарегистрировать свой callback последним. А для этого нужно вызвать конструкторы всех сервисов в надежде, чо если они и регистрируют callback на этот маркер, то делают это у себя в конструкторах и только потом регистрировать свой callback (если вы эти сервисы получаете путем внедрения зависимостей через конструктор, это получится автоматически). Во-вторых, нужно выполнить всю работу в своем callback синхронно, в частности, если сообщения отправляются сервисами асинхронно, то подождать завершение каждого (Tasl.WaitAll вам в помощь), а не ждать каждый через await (или явно - используя ContinueWith - но сейчас так никто не делает).
    Идея рассчитана на то, что реализация IHostApplicationLifeTime отменяет (ЕМНИПпроверено в исходниках) этот маркер простым Cancel и ждет завершения отмены (и в документации об этом ожидании смутно упомянуто, т.е., это не хак, зависящий от реализации) , а Cancel вызывает все зарегистрированные callback синхронно, в порядке, обратном регистрации (это ЕМНИП тоже документированнное поведение).
    Попробуйте для начала так, прежде чем применять более крутые меры (например, подменять реализации IHostLifeTime и IHostApplicationLifetime(UPD: погорячился, эту реализацию подменять нельзя, там обязан быть класс ApplicationLifetime) - технически это реально, но лучше такое оставить на потом).
    Если не прокатит, я ответ постараюсь продолжить.

    PS По поводу способов закрытия приложения. AFAIK остановка через "красный квадратик" в VS и закрытие окна консоли вызывает просто уничтожение процесса, и это не перехватывается (могу ошибаться, конечно). А вот закрытие по Ctrl+C отлично перехватывается ConsoleLifeTime (обычно испольуемая для консольных приложений реализация IHostLifetime), так что именно этот способ закрытия можно считать штатным. А вообще по жизни, 100% срабатывающих способов реакции на прекращение работы приложения нет - ибо прекращение может быть вызвано такой причной, как пропадание питания (особенно - если вследствие перерубания кабелей топором ;-) ).
    Ответ написан
    1 комментарий
  • Как написать свой кастомный EditorFor и получить значение свойства модели?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    У вас в поле _helper класса FormHelper<TModel> конструктор сохраняет ссылку на интерфейс IHtmlHelper<TModel>.
    Этот интерфейс содержит свойство ViewData типа ViewDataDictionary<TModel>. А в этом типе реализовано свойство Model (типа TModel), которое даст вам ссылку на экземпляр класса (типа TModel) модели для представления/страницы.
    Ну, а дальше, раз вы знаете имя нужного вам свойства объекта и имеете ссылку на экземпляр этого объекта, то можете получить значение этого свойства: либо через составление с последующей компиляцией и вычисление выражения, возвращающего его значение, либо через отражение.
    Как-то так.
    Ответ написан
    1 комментарий
  • Как удалить сессию из БД после истечения срока?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    В ASP.NET Core есть стандартный механизм сеансов (ISession): https://learn.microsoft.com/aspnet/core/fundamenta...
    Работает он на базе распределенного кэша (IDistributedCache), который штатными средствами можно базировать на MS SQL и на Redis (и, возможно, есть дополнительные сторонние пакеты для базирования на других СУБД,). Идентифкатор сессии хранится в куки (настраеваемой), по умолчанию - HTTP-only (но это настраивается).
    Механизм устаревания сеансов там есть, параметры тоже можно настроить.
    Кароче, если нет причин обязательно делать свой велосипед (типа, для учебной задачи) можно использовать этот стандартный механизм.
    Правда идея использовать этот механизм именно для авторизации у меня вызывает некоторые неясные опасения: он, вообще-то, не для того сделан. А именно для авторизации (плюс аутентифкация) в ASP.NET Core тоже есть штатное решение (Identity и политики авторизации). И вообще, аутентифкация/авторизация - это такое место, где легко накосячить, а потому лучше там обходиться без своих велосипедов.
    Ответ написан
    3 комментария
  • По какой причине появляеться SqliteException: SQLite Error 19: 'NOT NULL constraint failed?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Исправьте вот здесь:
    [Column("profilePicturePath")]
        public string? ProfilePicturePath { get; set; }

    Пояснение. В документации по EF Core написано:
    Если включены ссылочные типы, допускающие значение NULL, свойства будут настроены на основе nullability C# для типа .NET: string? будут настроены как необязательные, но string будут настроены по мере необходимости.

    В современных шаблонах проектов nullability включена по умолчанию, так что ваш тип свойства string EF понимает как то, что соответствующая колонка записи должна иметь ограничение NOT NULL. А потому вам следует использовать в качестве типа свойства string? .
    Ответ написан
    1 комментарий
  • Можно ли использовать excel файлы для генерации представлений в ASP проекте?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Идея генерировать содержимое HTML на основе внешних данных - она правильная. И шаблоны Razor (.cshtml) предоставляют для этого очень удобные средства, кстати. Так что хранить данные в самих шаблонах - это неправильная идея (вы в этом уже убедились, да).
    По поводу вашего предложения, как зранить данные, у меня есть совет: экспортируйте эти файлы из Excel в формат техта с разделителями (например, CSV, а я лично предпочитаю в качестве разделителей знаки табуляции). Это - обычные тектовые файлы, которые можно, при необходимости, править и без Excel (хоть в Блокноте), а для больших правок - импортировать в Excel. И всякие средства для контроля версий, типа Git - они с текстовыми файлами работают значительно лучше, чем с файлами excel.

    PS Как вам правильно написал в комментарии Sergey В. , для хранения данных лучше использоваь базу данных. Вы особенно это оцените, если функциональность проекта будет расширяться. А внешние файлы - хоть Excel, хоть текстовые - это эрзац-решение. Но если для вас это терпимо, а использовать БД в проекте вам сложно, то используйте файлы.
    Ответ написан
  • В каких случаях создавать новые контроллеры?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Одно из сображений, касающееся производительности. Контролллер - это класс, который создается каждый раз для обработки запроса.
    Поэтому имеет смысл смотреть на сервисы-зависимости в его конструкторе (параметры конструктора, которые извлекаются из контейнера сервисов): если действия этого контроллера используют разные зависимости, особенно - с временем жизни Scoped (DatabaseContext, к примеру) или Transient, то эти действия - хорошие кандидаты на перенос в отдельный(е) контроллер(ы).
    Ответ написан
    Комментировать
  • Почему некорректно работает параметр маршрутизатора Razor Pages?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Параметры маршрута привязываются к параметрам метода обработчика страницы по имени, т.е. их имена в директиве @page в шаблоне страницы и в списке параметров метода в коде должны совпадать.
    А у вас в @page используется id, а в списке параметров OnGet - pageId. Переименуйте что-нибудь, чтобы имена одинаковые были - и будет вам счастье.
    Ответ написан
  • Зачем нужен Service Locator?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Не легче ли внедрение делать через конструкторы?

    А вы не задумывались над тем, кто и как будет разрешать зависимости для конструкторов?
    В нынешнем ASP.NET Core этим занимются каркасы приложений ("фрейворки", от английского framework). И делают они это как раз через контейнер сервисов (либо напрямую через GetService, либо через ActvatorUtilities).
    Но есть области в ASP.NET Core , которые фремворками не закрываются - наример, в конвейере обработчиков запроса (middleware), там где требуется делегат типа RequestDelegate. И там без прямого обращения к контейнеру сервисов не обойтись.
    Ответ написан
    Комментировать
  • Как ограничить количество запросов на сервер при создании поисковика?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Не то, чтобы ответ, но идея ответа. И если чо непонятно - могу в каментах поуточнять.
    Со стороны тыла (на C#) можно ограничить число параллельных запросов в целом к сервису безо всяких там плагинов, а саморучно сделанным велосипедом: семафором (например, SemaphoreSlim, как самым модным в этом сезоне). Либо сделать его статическим, либо (чтобы веру в IoC свято блюсти и модульные тесты делать) - запихнув в Singleton-сервис с теми же свойствами/методами, что и у семафора используются, и получать этот сервис через конструктор (передавать через DI - в бою, напрямую - в тесте).
    Для семафора(сервиса) устанавливаете максимальное число параллельно выполняемых запросов в качестве начального значения. При входе в обработчик захватываете семафор (Wait/WaitAsync, таймаут - по вкусу), при выходе (лучше - в блоке finally того try, который начинается после захвата семафора) - освобождаете (Release).
    Таймаут выставляете в зависимости от поведения фронта: какие у него у самого таймауты на запрос (в том числе - на повторение) и как он реагирует на задержку ответа и на всякие разные коды статуса HTTP в ответе. В целом, стратегии тут две. Первая - пытаться захватывать семафор с коротким таймаутом и в случае неудачи - возвращать другой код статуса, кроме ОК, чтобы сказать фронту, что он не прав. Вторая - тормозить лишние запросы таймаутами. Кароче, без знания вашего фронтового плагина тут точно не скажешь, а я его знать не знаю и знать не хочу.
    Ответ написан
    Комментировать
  • Как должен быть путь для контроллера?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Если вы, как у вас написано, передаете название страны и города через путь в URL, то параметры метода Action для URL второго формата должен привязываться к переменной маршрутизации, примерно так:
    [HttpGet("{country}/{city}")]
    public IActionResult ActionMethod([FromRoute] String country, [FromRoute] String city) 
    {
      //... method code
    }

    [FromRoute] можно и не писать, если параметров запроса (то, что после '?' в URL) нет: привязка по умолчанию выберет следующим источником переменные маршрутизации.
    PS Можно обойтись одним методом действия (Action), если указать что city - параметр необязательный
    [HttpGet("{country}/{city?}")]
    public IActionResult ActionMethod([FromRoute] String country, [FromRoute] String? city) 
    {
     if(city==null) {
      //... method 1 code 
     }
     else {
      //... method 2 code 
     }
    }
    Ответ написан
    Комментировать
  • Почему прекращается работа BackgroundService?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Работа фонового сервиса у вас прекращается потому что у его метод ExecuteAsync дожидается инициализации статической переменной WebView (она содержит ChromiumWebBrowser, открытый на странице https://web.watsapp.com) а потом ничего не ждет, а просто завершается. А что там у вас происходит с браузером, код вашего фонового сервиса не контролирует.
    PS Я не вижу ваш остальной код, но могу предположить, что вам для работы программы этот фоновый сервис не нужен, т.к. вся работа с экземпляром CefSharp идет через упомянутую статическую переменную.
    Ответ написан
    3 комментария
  • ASP.NET Core 6 Как устранить ошибку при выполнении команды update-database?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    'MultipleActiveResults=True'

    Уберите эти одинарные кавычки.
    Ответ написан
    Комментировать
  • Почему сервер не разрешает [delete] метод (ошибка 405)?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Общий ответ - какой-то другой обработчик перехватывает запрос.

    Конкретный ответ зависит от того, как хостится приложение и от остального его кода. Если приложение работает через IIS(хостится на нем или опубликовано через него), то этим обработчиком часто является модуль WebDAV, и его надо отключить. Самый простой вариант, как это сделать - второй ответ этого вопроса: https://stackoverflow.com/questions/55736265/getti..., подробности, если интересно - https://learn.microsoft.com/ru-ru/aspnet/web-api/o...
    Ответ написан
  • Как адаптировать MapPost в метод ControllerBase?

    @mvv-rus
    Настоящий админ AD и ненастоящий программист
    Вынесено из комментариев (не туда запостил):

    Если бы я был контроллером API на MVC, я бы тоже так же ответил: "что за фигню вы мне прислали?" (в протоколе HTTP это обозначается кодом статуса 400).
    Контроллер API ждет, что вы ему пришлете, нечто, содержащее значение для параметра message вашего метода действия. Если вы посылаете в формате JSON, то - объект с полем message: тогда он привяжет значение этого поля входному параметру метода действия, который имеет то же имя message, как у вас.
    Потому в запросе POST надо слать объект с этим полем. Например - в уже сериализованном (строковом) формате:
    string content = "TestContent";
    Но можно в качестве content использовать и объект с полем message, например - анонимного типа:
    var content = new { message = "TestContent" };
    - PostAsJsonAsync сумеет превратить это в JSON.

    И да, к контроллеру на Minimal API из вашего первого комментария все это точно так же относится.
    Ответ написан
    Комментировать