Это видео интересно не для полных новичков, а для тех, кто уже умеет писать WebApi, но хочет научиться делать это, используя чистую архитектуру и best practices, которые можно встретить на реальных больших проектах. Лично для меня, работавшего 2 года с легаси и в глаза не видевшего clean architecture, этот плейлист действительно полезен и понятен. Такого контента не так уж много на ютубе. Автору большой респект
А можете сделать урок по созданию API на обычной трехуровневой архитектуре. Ато этот пример очень конечно крутой, затрагивает много технологий но для новичка вроде меня очень сложен в понимании.
Курс отличный. На данном этапе мы реализовали контролер, и необходимые методы: добавление, изменение, удаление, получение всего списка. Вот вопрос, а блоки try catch почти нигде не писали. Это нормально? вдруг будет ошибка например при добавлении записи, ошибка в базе данных, вот возникнет в классе class DeleteNoteCommandHandler. Получается лажет приложение? Я интересуюсь, потому что хочу знать, где в реальных приложения правильно использовать отлов ошибок. Абсолютно везде использовать блоки try catch не вариант. Использовать на самых высоких уровнях? например в контролерах???? Хотелось бы услышать ваш совет.
Зачем делать маппинг из класса для Request (CreateNoteCommand) в CreateNoteDTO? В API будет прилетать как раз DTO объект (или объект его содержащий), который можно записывать в свойство класса Request'a, а в Handle уже производить маппинг. Поправьте, если ошибаюсь, но вообще маппинг используется между DTO и оригинальным объектом.
Подскажите пожалуйста, на какой уровень, согласно данной модели, необходимо выносить "сложную" логику запросов? Например, для выполнения запроса, мне необходимо сперва получить 2 разные сущности (для этого я делаю 2 разных query?), затем проверить их данные, и, при выполнении условий, создать сущность третьего типа (запрос command). На уровне Application или WebApi? Особенно, если подобные действия могут вызываться в разных контроллерах?
Это уже седьмое видео по счету и только сейчас автор запускает первый раз Веб-Апи. И только сейчас у меня выясняется что база данных не создается и есть еще другие ошибки. И где я ошибся придется долго искать. Не лучше было бы каждый шаг проверять, а не на седьмом видео. А еще лучше было бы автору выложить весь проект, чтобы можно было легче найти ошибку у себя. В любом случае спасибо автору за труд.
получить IMediatr и присвоить, если _mediatr равен null подробнее тут: learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator
Спасибо за серию уроков, многие вещи для меня теперь стали более понятными, но при запуске проекта выдает ошибку HTTP Error 502.5 - Process Failure. Можете подсказать, в какую сторону копать, чтобы найти причину?
В готовом шаблоне VS Asp.Net.Core Web Api уже есть готовая архитектура работи с контроллерами, тестами и всеми настройками проекта включая DI и Swager.... Не понимаю зачем изобретать квадратное колесо?
Подскажите пожалуйста, при попытке сделать запрос в постмане, в таске getall() nullreference, медиатор = null. В чем может быть ошибка? Использую asp.net 6.
Добрый день. Попробуйте следующее, в Notes.Application-> class DependencyInjection -> добавить: services.AddMediatR(Assembly.GetExecutingAssembly()); return services;
Подскажите, пожалуйста, при запросе api/note выдает ошибку в строке var vm = await Mediator.Send(query) - System.NullReferenceException: "Object reference not set to an instance of an object.", с чем это может быть связанно?
Не знаю где я и что пропустил, но пересмотрел все на 5 раз, перепроверил также. Выдает ошибку в Postman: Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: Notes'. Как я понимаю не создается таблица, база данных есть, а таблицы внутри нет. Если сможете, подскажите пожалуйста в каком направлении копать.. Спасибо.
Лучше поздно, чем никогда. Мне помогло следующее: Я на .NET 7 делал, уже минимал апи, нету файла Startup.cs В программе в try блоке строчка var context = serviceProvider.GetRequiredService(); вызывалась раньше, чем инжектился сервис дб контекста в метода расширение AddPersistence поэтому надо сначала делать builder.Services.AddPersistence, а только потом трай блок с получением сервиса контекста и db initialize
@@kpssnik9570 Ты меня спас от пары часов утомительного дебага, спасибо) UPD: а, нет, похоже не спас, в EnsureCreated программа заходит, но не похоже, чтобы где-то создавался файл бд, и ошибка остается, может есть мысли по поводу?) UPD2: разобрался, напишу решение, вдруг кому поможет: //получаем конфигурацию в файле program.cs ConfigurationManager configuration = builder.Configuration; //явно указываем путь к файлам builder.Environment.ContentRootPath = Assembly.GetEntryAssembly().Location; Проблема заключалась в том, что DependencyInjection.cs не мог распарсить appsettings.json, потому что не мог его найти, соответственно мы получали пустой connection string и база данных не создавалась, хотя ensure created возвращал true.
Больше спасибо за этот замечательный урок! Но осталось лёгкое непонимание при работе с контроллером, в частности с getall. Мы отправляем команду в хандлер, который берет данные из дбконтекста. Почему же тогда мы не получаем ошибку от сервера, что ни таблица notes ни сама база данных не создана... Хотя запрос успешно выполняется (пусть и пустой список, ведь пустой список это все равно результат работы БАЗЫ ДАННЫХ)
Добрый день. У вас в методе Task Create(CreateNoteDto createNoteDto) - который создаёт запись, возвращается Ok(noteId). Соответственно StatusCodes будет 200. Но это же не совсем правильно. Мы же должны возвращать 201 через Created(). Тем более, что вы когда описание для сваггера делали, так и написали [ProducesResponseType(StatusCodes.Status201Created)]. Как исправить метод?
@@PlatinumTechTalks created требует два параметра. первый - урл. И вот какой урл подставить я не знаю. нам же вроде не надо никуда переходить... (извините за глупые вопросы)
В первый параметр можно передать просто ссылку на созданный объект. Например так “www.example.com/api/controller/id” где id это айди только что созданного объекта. Вероятно можно просто айди строкой без ссылки отдавать. Это то что клиенту вернется в заголовке ответа. Во втором параметре можно передать что угодно, можно айдишник, можно сам объект. В зависимости от нужд. Клиенту это вернется в теле ответа. (глупых вопросов не бывает)
У меня очень странная ситуация: IMediator.Send(new GetNoteListQuery) возвращает Task, а IMediator.Send(new GetNoteDetailsQuery) возвращает Task (void). Cигнатуры хэндлеров следующие public async Task Handle(GetNoteDetailsQuery request, CancellationToken cancellationToken); public async Task Handle(GetNoteListQuery request, CancellationToken cancellationToken); и как это понимать? Почему он считает что при GetNoteDetailsQuery должен возвращаться void?? пишу на .net 6
Получаю ошибку: Ошибка CS1061 "DbContextOptionsBuilder" не содержит определения "UseSqlite", и не удалось найти доступный метод расширения "UseSqlite", принимающий тип "DbContextOptionsBuilder" в качестве первого аргумента (возможно, пропущена директива using или ссылка на сборку). Пакет переустанавливал, для уверенности скопировал с гитхаба все.
Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: Notes'. at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db) в каком участке кода ошибка?
Там плюс минус тоже самое. Идеи о том как можно организовать код в файле Program.cs можно подчерпнуть из последнего видео по Minimal APIs (на данный момент последнее на канале)
@@PlatinumTechTalks поправьте, если ошибаюсь, новичок, потратил почти день, чтобы разобраться. Мой Program.cs в .net 6 выглядит следующим образом: using Notes.Application; using Notes.Application.Common.Mappings; using Notes.Application.Interfaces; using Notes.Persistence; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddApplication(); builder.Services.AddControllers(); builder.Services.AddPersistence(builder.Configuration); builder.Services.AddAutoMapper(config => { config.AddProfile(new AssemblyMappingProfile(typeof(Program).Assembly)); config.AddProfile(new AssemblyMappingProfile(typeof(INotesDbContext).Assembly)); }); builder.Services.AddCors(options => { options.AddPolicy("AllowAll", policy => { policy.AllowAnyHeader(); policy.AllowAnyMethod(); policy.AllowAnyOrigin(); }); }); // Learn more about configuring Swagger/OpenAPI at aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } using (var scope = app.Services.CreateScope()) { try { var context = scope.ServiceProvider.GetRequiredService(); DbInitializer.Initialize(context); } catch (Exception) { } } app.UseHttpsRedirection(); app.UseAuthorization(); app.UseCors("AllowAll"); app.MapControllers(); app.Run();
@@winstochurgle9133 Может лучше сделать так: var scope = app.Services.CreateScope(); , чтобы не появлялось напоминание ASP0000, что создается второй контейнер?
using Microsoft.EntityFrameworkCore; using Notes.Application.Interfaces; using Notes.Domain; using Notes.Persistence.EntityTypeConfigurations; namespace Notes.Persistence { public class NotesDbContext : DbContext, INotesDbContext { public DbSet Notes { get; set; } public NotesDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { builder.ApplyConfiguration(new NoteConfiguration()); base.OnModelCreating(builder); } } } UsersDbContext.cs файл. Написал вот так код из репозитория и все пошло.
Считаю что для профессионального программирования перехватывать исключения и ничего с ним не делать это лютое зло. Ты просто можешь забыть об этом, а приложение будет просто молчаливо проглатывать команды/данные и никому ничего об этом не говорить, и еще хуже если это будет происходить изредка. Такое гавно придется долго искать. Хотя бы WriteLine в консоль.
С полной ответственностью могу заверить, что ни один комментарий не был удален на канале. А вот ваши одинаковые под каждым видео комментарии настораживают. Удачи вам)
System.InvalidOperationException: The entity type 'List' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'. Как быть с таким исключением?
Это видео интересно не для полных новичков, а для тех, кто уже умеет писать WebApi, но хочет научиться делать это, используя чистую архитектуру и best practices, которые можно встретить на реальных больших проектах. Лично для меня, работавшего 2 года с легаси и в глаза не видевшего clean architecture, этот плейлист действительно полезен и понятен. Такого контента не так уж много на ютубе. Автору большой респект
спасибо большое за такой отзыв!)
А можете сделать урок по созданию API на обычной трехуровневой архитектуре. Ато этот пример очень конечно крутой, затрагивает много технологий но для новичка вроде меня очень сложен в понимании.
Спасибо большое за курс
Курс отличный. На данном этапе мы реализовали контролер, и необходимые методы: добавление, изменение, удаление, получение всего списка. Вот вопрос, а блоки try catch почти нигде не писали. Это нормально? вдруг будет ошибка например при добавлении записи, ошибка в базе данных, вот возникнет в классе class DeleteNoteCommandHandler. Получается лажет приложение? Я интересуюсь, потому что хочу знать, где в реальных приложения правильно использовать отлов ошибок. Абсолютно везде использовать блоки try catch не вариант. Использовать на самых высоких уровнях? например в контролерах???? Хотелось бы услышать ваш совет.
обожаю оверинженеринг) не вижу смысла в коммандах, запросах, если сервис прекрасно справляется
13:18 минуте видео у нас неожиданно появляется база данных Platinum.
Объясните как ее ввести
У меня кроме матов по этому поводу больше никаких эмоций нету, и так сложно, дак еще и файлы откуда-то появляются.
@@ВикторБелянкин-м1щ Надо миграцию сделать, чтобы база появилась. А в видео этот момент почему-то опущен.
Спасибо🙏
Вам спасибо!!
Зачем делать маппинг из класса для Request (CreateNoteCommand) в CreateNoteDTO?
В API будет прилетать как раз DTO объект (или объект его содержащий), который можно записывать в свойство класса Request'a, а в Handle уже производить маппинг.
Поправьте, если ошибаюсь, но вообще маппинг используется между DTO и оригинальным объектом.
Подскажите пожалуйста, на какой уровень, согласно данной модели, необходимо выносить "сложную" логику запросов?
Например, для выполнения запроса, мне необходимо сперва получить 2 разные сущности (для этого я делаю 2 разных query?), затем проверить их данные, и, при выполнении условий, создать сущность третьего типа (запрос command).
На уровне Application или WebApi? Особенно, если подобные действия могут вызываться в разных контроллерах?
тут напрашивается прослойка в виде сервиса на уровне Application. Поправьте, если ошибаюсь
Подскажите, пожалуйста, а зачем мы прописываем роутинг контроллеру, если мы уже в base controller писали этот атрибут?
Это уже седьмое видео по счету и только сейчас автор запускает первый раз Веб-Апи. И только сейчас у меня выясняется что база данных не создается и есть еще другие ошибки. И где я ошибся придется долго искать. Не лучше было бы каждый шаг проверять, а не на седьмом видео. А еще лучше было бы автору выложить весь проект, чтобы можно было легче найти ошибку у себя.
В любом случае спасибо автору за труд.
под каждым видео в описании есть ссылка на коммит с тем состоянием кода, в котором он был к концу урока, это может помочь, спасибо за фидбек
А что значит "??=" на 8:50 ?
Подскажите пожалуйста.
получить IMediatr и присвоить, если _mediatr равен null
подробнее тут: learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator
Спасибо
Спасибо за серию уроков, многие вещи для меня теперь стали более понятными, но при запуске проекта выдает ошибку HTTP Error 502.5 - Process Failure. Можете подсказать, в какую сторону копать, чтобы найти причину?
Проблема в Program.cs или Startup.cs. От версии asp net core зависит стоит ли использовать эти Startup
хм, получается нам не нужны ef dotnet tools?
В готовом шаблоне VS Asp.Net.Core Web Api уже есть готовая архитектура работи с контроллерами, тестами и всеми настройками проекта включая DI и Swager.... Не понимаю зачем изобретать квадратное колесо?
Подскажите пожалуйста, при попытке сделать запрос в постмане, в таске getall() nullreference, медиатор = null. В чем может быть ошибка? Использую asp.net 6.
Он у вас инициализируется? Посмотрите BaseController и DependencyInjection в Application, поставьте точки останова и тд
Добрый день. Попробуйте следующее, в Notes.Application-> class DependencyInjection -> добавить: services.AddMediatR(Assembly.GetExecutingAssembly());
return services;
@@clutchmeisteryo9831 Спасибо!
@@clutchmeisteryo9831 лучший брат
Подскажите, пожалуйста, при запросе api/note
выдает ошибку в строке var vm = await Mediator.Send(query) - System.NullReferenceException: "Object reference not set to an instance of an object.", с чем это может быть связанно?
У меня такая же ошибка, ты как-то её исправил?
Подскажите пожалуйста, а почему CreateNoteDto определяется в Web.Api проекте а не в Application как NoteLookupDto?
Одно для внутреннего использования, другое - торчит наружу
Не знаю где я и что пропустил, но пересмотрел все на 5 раз, перепроверил также. Выдает ошибку в Postman: Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: Notes'.
Как я понимаю не создается таблица, база данных есть, а таблицы внутри нет. Если сможете, подскажите пожалуйста в каком направлении копать.. Спасибо.
В DbInitializer метод Initialize.
Или NotesDbContext, метод OnModelCreating
@@Кирилл-ж3м2н Спасибо за направление.
@@ValentinAntinicotine Нужно не забыть вызвать .Initialize(context) в Program.cs в блоке try :-)
Лучше поздно, чем никогда. Мне помогло следующее:
Я на .NET 7 делал, уже минимал апи, нету файла Startup.cs
В программе в try блоке строчка
var context = serviceProvider.GetRequiredService();
вызывалась раньше, чем инжектился сервис дб контекста в метода расширение AddPersistence
поэтому надо сначала делать builder.Services.AddPersistence, а только потом трай блок с получением сервиса контекста и db initialize
@@kpssnik9570 Ты меня спас от пары часов утомительного дебага, спасибо)
UPD: а, нет, похоже не спас, в EnsureCreated программа заходит, но не похоже, чтобы где-то создавался файл бд, и ошибка остается, может есть мысли по поводу?)
UPD2: разобрался, напишу решение, вдруг кому поможет:
//получаем конфигурацию в файле program.cs
ConfigurationManager configuration = builder.Configuration;
//явно указываем путь к файлам
builder.Environment.ContentRootPath = Assembly.GetEntryAssembly().Location;
Проблема заключалась в том, что DependencyInjection.cs не мог распарсить appsettings.json, потому что не мог его найти, соответственно мы получали пустой connection string и база данных не создавалась, хотя ensure created возвращал true.
Больше спасибо за этот замечательный урок! Но осталось лёгкое непонимание при работе с контроллером, в частности с getall. Мы отправляем команду в хандлер, который берет данные из дбконтекста. Почему же тогда мы не получаем ошибку от сервера, что ни таблица notes ни сама база данных не создана... Хотя запрос успешно выполняется (пусть и пустой список, ведь пустой список это все равно результат работы БАЗЫ ДАННЫХ)
спасибо! База создается при старте приложения когда вызывается EnsureCreated
Добрый день ! Подскажите, какие еще есть инструменты , кроме ПостМана, для проверки API?
Здравствуйте! Insomnia, Fiddler, Hoppscotch (aka PostWoman), консольная утилита httprepl.
@@PlatinumTechTalks спасибо)
Добрый день. У вас в методе Task Create(CreateNoteDto createNoteDto) - который создаёт запись, возвращается Ok(noteId). Соответственно StatusCodes будет 200. Но это же не совсем правильно. Мы же должны возвращать 201 через Created(). Тем более, что вы когда описание для сваггера делали, так и написали [ProducesResponseType(StatusCodes.Status201Created)]. Как исправить метод?
Здравствуйте. Видимо, опечатка. Нужно тогда return Created(noteId);
@@PlatinumTechTalks created требует два параметра. первый - урл. И вот какой урл подставить я не знаю. нам же вроде не надо никуда переходить... (извините за глупые вопросы)
В первый параметр можно передать просто ссылку на созданный объект. Например так “www.example.com/api/controller/id” где id это айди только что созданного объекта. Вероятно можно просто айди строкой без ссылки отдавать. Это то что клиенту вернется в заголовке ответа. Во втором параметре можно передать что угодно, можно айдишник, можно сам объект. В зависимости от нужд. Клиенту это вернется в теле ответа. (глупых вопросов не бывает)
У меня очень странная ситуация: IMediator.Send(new GetNoteListQuery) возвращает Task, а IMediator.Send(new GetNoteDetailsQuery) возвращает Task (void). Cигнатуры хэндлеров следующие public async Task Handle(GetNoteDetailsQuery request, CancellationToken cancellationToken);
public async Task Handle(GetNoteListQuery request, CancellationToken cancellationToken);
и как это понимать? Почему он считает что при GetNoteDetailsQuery должен возвращаться void??
пишу на .net 6
Разобрался, унаследовал хэндлер сразу от IRequest и IRequest
Получаю ошибку:
Ошибка CS1061 "DbContextOptionsBuilder" не содержит определения "UseSqlite", и не удалось найти доступный метод расширения "UseSqlite", принимающий тип "DbContextOptionsBuilder" в качестве первого аргумента (возможно, пропущена директива using или ссылка на сборку).
Пакет переустанавливал, для уверенности скопировал с гитхаба все.
Я бы проверил версию пакета и то что он есть в csproj файле для начала.и то что название пакета совпадает с нужным
Валидация данных где планируется?
Валидация данных будет в проекте Application. В следующем видео
Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 1: 'no such table: Notes'.
at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
в каком участке кода ошибка?
А как быть в ASP.NET 6? Здесь же убрали файл Startup.cs, теперь все в файле Program.cs, как по уму всё сделать?
Там плюс минус тоже самое. Идеи о том как можно организовать код в файле Program.cs можно подчерпнуть из последнего видео по Minimal APIs (на данный момент последнее на канале)
@@PlatinumTechTalks поправьте, если ошибаюсь, новичок, потратил почти день, чтобы разобраться. Мой Program.cs в .net 6 выглядит следующим образом:
using Notes.Application;
using Notes.Application.Common.Mappings;
using Notes.Application.Interfaces;
using Notes.Persistence;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddApplication();
builder.Services.AddControllers();
builder.Services.AddPersistence(builder.Configuration);
builder.Services.AddAutoMapper(config =>
{
config.AddProfile(new AssemblyMappingProfile(typeof(Program).Assembly));
config.AddProfile(new AssemblyMappingProfile(typeof(INotesDbContext).Assembly));
});
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
{
policy.AllowAnyHeader();
policy.AllowAnyMethod();
policy.AllowAnyOrigin();
});
});
// Learn more about configuring Swagger/OpenAPI at aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
using (var scope = app.Services.CreateScope())
{
try
{
var context = scope.ServiceProvider.GetRequiredService();
DbInitializer.Initialize(context);
}
catch (Exception)
{
}
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseCors("AllowAll");
app.MapControllers();
app.Run();
@@МаксимСпорт-ф3х var scope = builder.Services.BuildServiceProvider().CreateScope() :)
@@МаксимСпорт-ф3х спасибо :)
@@winstochurgle9133 Может лучше сделать так: var scope = app.Services.CreateScope(); , чтобы не появлялось напоминание ASP0000, что создается второй контейнер?
Будет ссылка на репозиторий?
Будет. В ближайшее время добавим для каждого видео код из него
под каждым видео теперь добавлены ссылки на код из видео
@@PlatinumTechTalks Спасибо
Aes4e do s MiddleWare dobavliaütsia filtry))))
Весьма странное решение мапить дто в команду...
Почему?
Я все перепроверил, но у меня база данных не создается
Если с гитхаба взять репозиторий, то тоже не создается?
@@PlatinumTechTalks я скопировал все, что было, не создает, ошибок никаких нет
Если продебажить то вызывается ли метод EnsureCreated?
@@PlatinumTechTalks нет, он не вызывался, я полностью пересоздал проект, все заработало.
using Microsoft.EntityFrameworkCore;
using Notes.Application.Interfaces;
using Notes.Domain;
using Notes.Persistence.EntityTypeConfigurations;
namespace Notes.Persistence
{
public class NotesDbContext : DbContext, INotesDbContext
{
public DbSet Notes { get; set; }
public NotesDbContext(DbContextOptions options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfiguration(new NoteConfiguration());
base.OnModelCreating(builder);
}
}
}
UsersDbContext.cs файл. Написал вот так код из репозитория и все пошло.
ПО? Это что? точно не middleware! Переводчик!
Считаю что для профессионального программирования перехватывать исключения и ничего с ним не делать это лютое зло. Ты просто можешь забыть об этом, а приложение будет просто молчаливо проглатывать команды/данные и никому ничего об этом не говорить, и еще хуже если это будет происходить изредка. Такое гавно придется долго искать. Хотя бы WriteLine в консоль.
Автор клоун,вместо того чтобы просто помочь устранить проблему,удаляет комменты)
С полной ответственностью могу заверить, что ни один комментарий не был удален на канале. А вот ваши одинаковые под каждым видео комментарии настораживают. Удачи вам)
@@PlatinumTechTalks комменты и вправду пропадают. Это ютуб чудит. Последнее время с ним это часто случается.
System.InvalidOperationException: The entity type 'List' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'.
Как быть с таким исключением?
Проверьте конфигурацию для энтити