Основной код Entity Framework сначала все еще показывает удаленную запись sqlite

#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()) нет?