#c# #entity-framework-core #ef-code-first
#c# #entity-framework-core #ef-code-first
Вопрос:
Я создаю очень простое CRUD-приложение для изучения C # и EF. Для простоты я создал 3 модели домена: Player, Tournament и TournamentEnrollment.
public class Player
{
[Key]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
public ICollection<TournamentEnrollment> TournamentEnrollments { get; set; }
}
public class Tournament
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public ICollection<TournamentEnrollment> Enrollments { get; set; }
}
public class TournamentEnrollment
{
[key]
public int Id { get; set; }
public Tournament Tournament { get; set; }
public Player Player { get; set; }
}
Для каждой модели домена у меня также есть DbSet в моем DbContext.
Под слоем DAL я создал репозиторий для нескольких основных операций CRUD
public class Repository : IRepository
{
private readonly TmDbContext _context;
public Repository()
{
_context = new TmDbContext();
}
public Repository(TmDbContext context)
{
_context = context;
}
public Player ReadPlayer(int id)
{
return _context.Players.Find(id);
}
public Tournament ReadTournament(int id)
{
return _context.Tournaments.Find(id);
}
public IEnumerable<Tournament> ReadAllTournamentsWithParticipants()
{
return _context.Tournaments
.Include(t => t.Enrollments)
.ThenInclude(te => te.Player);
}
public TournamentEnrollment ReadTournamentEnrollment(int id)
{
return _context.TournamentEnrollments.Find(id);
}
public IEnumerable<TournamentEnrollment> ReadAllTournamentEnrollments()
{
return _context.TournamentEnrollments
.Include(te => te.Player)
.Include(te => te.Tournament);
}
public void DeleteTournamentEnrollment(int id)
{
TournamentEnrollment tournamentEnrollment = ReadTournamentEnrollment(id);
_context.TournamentEnrollments.Remove(tournamentEnrollment);
_context.SaveChanges();
}
}
Ниже я покажу вам мой Program.cs из моего проекта консольного приложения
class Program
{
private static IRepository _repo = new Repository();
private static bool _exit;
static void Main(string[] args)
{
while (!_exit)
{
try
{
ShowMenu();
MakeChoice();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
private static void ShowMenu()
{
Console.WriteLine("1) Show all tournaments");
Console.WriteLine("2) Remove tournament enrollment");
Console.WriteLine("3) Show all tournament enrollments");
Console.WriteLine("0) Exit");
}
private static void MakeChoice()
{
Console.Write("=>: ");
if (!Int32.TryParse(Console.ReadLine(), out int choice))
{
Console.WriteLine("nPlease enter a numeric value!n");
}
else
{
switch (choice)
{
case 1:
ShowAllTournaments();
break;
case 2:
RemoveTournamentEnrollment();
break;
case 3:
ShowAllTournamentEnrollments();
break;
case 0:
_exit = true;
break;
}
}
}
private static void ShowAllTournaments()
{
Console.WriteLine();
IEnumerable<Tournament> allTournaments = _repo.ReadAllTournamentsWithParticipants().ToList();
foreach (var t in allTournaments)
{
Console.WriteLine($"[{t.Id}] {t.Name}");
foreach (var te in t.Enrollments)
{
Console.WriteLine($"t- [{te.Player.Id}] {te.Player.FullName}");
}
}
Console.WriteLine();
}
private static void ShowAllTournamentEnrollments()
{
Console.WriteLine();
foreach (var te in _repo.ReadAllTournamentEnrollments())
{
Console.WriteLine($"[{te.Id}] Tournament = {te.Tournament.Name} | Player = {te.Player.FullName}");
}
Console.WriteLine();
}
private static void RemoveTournamentEnrollment()
{
Console.Write("TournamentEnrollment ID: ");
int id = Int32.Parse(Console.ReadLine());
Console.WriteLine();
_repo.DeleteTournamentEnrollment(id);
}
}
Вариант 1 показывает все турниры с их участниками. Теперь, если я удалю запись о регистрации в турнире (используя опцию 2 в приложении), а затем я запрошу все турниры с их участниками (вариант 1), как и ожидалось, в определенном турнире на одного участника меньше.
Теперь у меня также есть инструмент SQLite desktop для открытия файла базы данных SQLite. Если я удаляю запись в настольном инструменте (и, конечно, выполняю фиксацию) во время запуска консольного приложения, а затем нажимаю опцию 1, удаленная запись все еще отображается в консольном приложении. Странно то, что когда я нажимаю опцию 3, эта запись не отображается. Поэтому я думаю, что это как-то связано с запросом или кэшированием из EF. Сначала я подумал, что, возможно, запрос для варианта 1 выполняется только один раз, а затем всегда в памяти, но при отладке я вижу, что выполняется запрос, поэтому я действительно не понимаю, как это возможно.
У кого-нибудь есть идея, что я делаю не так?
Большое вам спасибо!
Комментарии:
1. Это потому, что результат запроса к базе данных консолидируется с кэшированными (отслеживаемыми) экземплярами объектов в контексте. «Неправильным» является использование статического (следовательно, одноэлементного) долговременного репозитория (таким образом, контекста БД). Создайте новый репозиторий (контекст) для каждого вызова, используйте его для выполнения желаемой операции / запроса и удалите его впоследствии, и проблема исчезнет. Посмотрите время жизни DbContext .
2. Спасибо за ваш комментарий! Но почему один запрос (метод в репозитории = ReadAllTournamentsWithParticipants()) затронут, а другой (метод в репозитории = ReadAllTournamentEnrollments()) нет?