CascadeDelete SQLite unter .NetCore 3.1

  • C#

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von MichaHo.

    CascadeDelete SQLite unter .NetCore 3.1

    Hallo,
    Folgende Umgebung:
    WPF .NetCore 3.1 Projekt
    Datenbank SQLite (local).
    Tabellen: Person, GraduationEntry, PresentEntry
    die Tabelle Person (representiert 1 Person):
    Spoiler anzeigen

    C#-Quellcode

    1. public class Person : ModelBase
    2. {
    3. [Required(AllowEmptyStrings = false, ErrorMessage = "Bitte geben sie einen Vornamen an")]
    4. [MinLength(3)]
    5. [MaxLength(150)]
    6. public virtual string FirstName { get; set; }
    7. [Required(AllowEmptyStrings = false, ErrorMessage = "Bitte geben sie einen Nachnamen an")]
    8. [MinLength(3)]
    9. [MaxLength(150)]
    10. public virtual string LastName { get; set; }
    11. public virtual LoginData AcessData { get; set; }
    12. public virtual string FullName => $"{LastName}, {FirstName}";
    13. public virtual DateTime EntryDate { get; set; } = DateTime.Today;
    14. public virtual GenderEnum Gender { get; set; } = GenderEnum.male;
    15. public virtual PersonTypeEnum PersonType { get; set; } = PersonTypeEnum.Shinzan;
    16. public virtual int MemberId { get; set; }
    17. public virtual ICollection<GraduationEntry> Graduations { get; set; }
    18. public virtual ICollection<PresentEntry> PresentEntries { get; set; }
    19. public virtual bool IsAdmin { get; set; } = false;
    20. public Person()
    21. {
    22. Graduations = new List<GraduationEntry>();
    23. PresentEntries = new List<PresentEntry>();
    24. }
    25. }
    26. public enum GenderEnum
    27. {
    28. male,
    29. female
    30. }
    31. public enum PersonTypeEnum
    32. {
    33. Shinzan,
    34. ShinzanKids
    35. }


    Tabelle GraduationEntry (representiert 1 Graduierung):
    Spoiler anzeigen

    C#-Quellcode

    1. public class GraduationEntry : ModelBase
    2. {
    3. public virtual int GraduationId { get; set; }
    4. public virtual DateTime? GraduationReceived { get; set; }
    5. public virtual DateTime? GraduationHold { get; set; }
    6. public virtual int PersonId { get; set; }
    7. public virtual Person Person { get; set; }
    8. }


    Tabelle PresentEntry (representiert 1 Anwesend Eintrag):
    Spoiler anzeigen

    C#-Quellcode

    1. public class PresentEntry : ModelBase
    2. {
    3. public virtual DateTime Date { get; set; } = DateTime.Today;
    4. public virtual PresentStateEnum PresentState { get; set; } = PresentStateEnum.None;
    5. public virtual int PersonId { get; set; }
    6. public virtual Person Person { get; set; }
    7. }
    8. public enum PresentStateEnum
    9. {
    10. None = 0,
    11. Present,
    12. Absent,
    13. Sick,
    14. Vacation
    15. }


    Der Context dazu:
    Spoiler anzeigen

    C#-Quellcode

    1. public class ShinzanDojoContext : DbContext
    2. {
    3. #region Private Member
    4. private readonly string _dbFile = @"sdDb.db";
    5. #endregion
    6. #region Constructor
    7. public ShinzanDojoContext()
    8. {
    9. }
    10. #endregion
    11. #region DbSets
    12. public virtual DbSet<Person> People { get; set; }
    13. public virtual DbSet<Graduation> Graduations { get; set; }
    14. #endregion
    15. #region Overrides
    16. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    17. {
    18. optionsBuilder.UseSqlite($"Data Source={_dbFile}");
    19. }
    20. #endregion
    21. }


    OK, soweit so gut. Die DB wird angelegt. Schaue ich nun in die DB Struktur, sehe ich auch, das EF Cascade Delete angelegt hat.
    Hier am Beispiel GraduationEntry:

    SQL-Abfrage

    1. CREATE TABLE "GraduationEntry" (
    2. "Id" INTEGER NOT NULL CONSTRAINT "PK_GraduationEntry" PRIMARY KEY AUTOINCREMENT,
    3. "CreatedBy" INTEGER NOT NULL,
    4. "CreationTimeStamp" TEXT NOT NULL,
    5. "LastModifiedBy" INTEGER NOT NULL,
    6. "LastModifiedTimeStamp" TEXT NOT NULL,
    7. "DeletedFlag" INTEGER NOT NULL,
    8. "GraduationId" INTEGER NOT NULL,
    9. "GraduationReceived" TEXT NULL,
    10. "GraduationHold" TEXT NULL,
    11. "PersonId" INTEGER NOT NULL,
    12. CONSTRAINT "FK_GraduationEntry_People_PersonId" FOREIGN KEY ("PersonId") REFERENCES "People" ("Id") ON DELETE CASCADE
    13. )


    Verstehe ich also so, das wenn ich bei der Person aus der Auflistung der GraduationEntry einen Eintrag gerausnehme (Auflistung.Remove), das dann automatisch in der GraduationEntry Tabelle der dazugehörige Eintrag auch gelsöcht wird.

    Nun, wird es aber leider nicht.

    Muss ich den Eintrag selbst löschen?

    Ich hab auch versucht, im Context der Tabelle GraduationEntry explizit zu sagen, das er Cascade löschen soll, bringt aber auch nichts.
    Fehlermeldung kommt keine, die Person wird richtig gespeichert (upgedatet) und beim Update ist der Eintrag auch wirklich weg, aber nach neuem Start der Anwendung ist der Eintrag wieder da.

    Wie lösche ich denn den Eintrag in der Tabelle wenn ich einen Eintrag aus der Auflistung entferne?
    "Hier könnte Ihre Werbung stehen..."

    MichaHo schrieb:

    aber nach neuem Start der Anwendung ist der Eintrag wieder da.

    Kann hier der Fehler liegen?
    Wenn du einen Eintrag updatest was gibt SaveChanges() da zurück. Kommt hier die richtige Anzahl an Datensätzen zurück?

    Hast du hier diesbezüglich noch etwas in OnModelCreating() konfiguriert??

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    in OnModelCreating hab ich nichts configuriert.
    SaveChanges hat auch tatsächlich nur noch 1 Graduierung drin, die, die ich gelöscht habe ist nicht mehr drin beim SaveChanges...
    auch wenn ich durchsteppe und finde dann irgendwo die GraduationEntry, ist da nur noch 1 Eintrag drin.

    EDIT:
    auch wenn ich OnModelCreating nutze:

    C#-Quellcode

    1. protected override void OnModelCreating(ModelBuilder modelBuilder)
    2. {
    3. modelBuilder.Entity<PresentEntry>().HasOne(p => p.Person).WithMany(e => e.PresentEntries).HasForeignKey(p => p.PersonId).OnDelete(DeleteBehavior.Cascade);
    4. modelBuilder.Entity<GraduationEntry>().HasOne(p => p.Person).WithMany(g => g.Graduations).HasForeignKey(p => p.PersonId).OnDelete(DeleteBehavior.Cascade);
    5. }


    wird es zwar schön eingteragen im Create Statement, aber gelöscht wird da nix.

    Muss ich die beiden Tabellen doch als DBSet anlegen und dann direkt in der Tabelle löschen?

    "Hier könnte Ihre Werbung stehen..."

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „MichaHo“ ()

    MichaHo schrieb:

    SaveChanges hat auch tatsächlich nur noch 1 Graduierung drin

    Langsam. SaveChanges hat nix drin. Ist ne Methode mit einem Integer Rückgabewert welche zurückgibt wieviele Datensätze (Entitäten) vom Speichern betroffen waren. Du meinst den Context.

    Gibt SaveChanges also 0 zurück ist was nicht in Ordnung. Stichwort Tracking.

    Das du beim Debuggen im Context nur den soeben gelöschten Datensatz nicht mehr siehst bedeutet nicht das dieser in der DB gelöscht wurde. Er ist lediglich von Context entfernt. Hat der ChangeTracker dies aber nicht "mitbekommen" kannst du Savechanges() aufrufen sooft du willst, es wird kein Delete an die DB gesendet.

    Meine Empfehlung: (Die gilt übrigens bei EF IMMER zur Entwicklungszeit)
    EF Log einschalten und kontrollieren welche Querys an die DB gehen, erst dann kannst du sicher sein das alles passt.

    Was gibt SaveChanges also zurück?

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    OK, Logging habe ich eingeschaltet (hab dazu deine DbSimpleLogger usw. aus dem WPF Projekt genutzt).
    Ich hab das Log direkt in ein File geschrieben.
    Man sieht darin (ziemlich weit unten) das die Tanelle GraduationEntry nur upgedatet wird, ein Delete Statement passiert leider nicht....
    Dateien
    • Log.txt

      (58,31 kB, 88 mal heruntergeladen, zuletzt: )
    "Hier könnte Ihre Werbung stehen..."
    Hallo

    Moment mal....
    Die von dir geposteten Klassen stimmen mal garnicht mit dem überein was hier im Log sichtbar wird.

    SQL-Abfrage

    1. UPDATE "GraduationEntry" SET "CreatedBy" = @p0, "CreationTimeStamp" = @p1, "DeletedFlag" = @p2, "GraduationHold" = @p3, "GraduationId" = @p4, "GraduationReceived" = @p5, "LastModifiedBy" = @p6, "LastModifiedTimeStamp" = @p7, "PersonId" = @p8
    2. WHERE "Id" = @p9;

    Hier hat GraduationEntry Spalten wie "DeletedFlag" und die TameStamp Spalten.

    Verwendest du die Interfaces aus meinem WPFNotes Projekt?

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Ich verwende die ModelBase, die ist bei jeder Modelklasse hinterlegt:
    einmal ein Interface:

    C#-Quellcode

    1. public interface IModel
    2. {
    3. bool IsValid { get; }
    4. IList<ValidationResult> Validate();
    5. }


    und dann die ModelBase:

    C#-Quellcode

    1. public abstract class ModelBase : IModel
    2. {
    3. [Key]
    4. public virtual int Id { get; set; }
    5. public virtual CreationUserEnum CreatedBy { get; set; } = CreationUserEnum.System;
    6. public virtual DateTime CreationTimeStamp { get; set; } = DateTime.Now;
    7. public virtual CreationUserEnum LastModifiedBy { get; set; } = CreationUserEnum.System;
    8. public virtual DateTime LastModifiedTimeStamp { get; set; } = DateTime.Now;
    9. public virtual bool DeletedFlag { get; set; }
    10. #region Validate Implementation
    11. public bool IsValid => Validate() != null && Validate().Count == 0;
    12. public IList<ValidationResult> Validate()
    13. {
    14. var validationResults = new List<ValidationResult>();
    15. var validationContext = new ValidationContext(this);
    16. Validator.TryValidateObject(this, validationContext, validationResults, true);
    17. return validationResults;
    18. }
    19. #endregion
    20. }
    21. public enum CreationUserEnum
    22. {
    23. System = -1,
    24. User = 1
    25. }
    "Hier könnte Ihre Werbung stehen..."
    OK, bitte Poste mal die Gesamte Context-Klasse.
    Ich vermute fast du überschreibst auch die SaveChanges-Methode wie ich in meinen Projekten. Kann das sein?
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Nee, ich überschreib da nix:

    Das ist die Gesamte Context Klasse:

    C#-Quellcode

    1. public class ShinzanDojoContext : DbContext
    2. {
    3. #region Private Member
    4. private readonly string _dbFile = @"sdDb.db";
    5. #endregion
    6. public static readonly LoggerFactory MyLoggerFactory = new LoggerFactory(new[] { new DbLoggerProvider(s => File.AppendAllText("Log.txt",s))});
    7. #region Constructor
    8. public ShinzanDojoContext()
    9. {
    10. }
    11. #endregion
    12. #region DbSets
    13. public virtual DbSet<Person> People { get; set; }
    14. public virtual DbSet<Graduation> Graduations { get; set; }
    15. #endregion
    16. #region Overrides
    17. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    18. {
    19. optionsBuilder.UseSqlite($"Data Source={_dbFile}");
    20. optionsBuilder.UseLoggerFactory(MyLoggerFactory);
    21. optionsBuilder.EnableSensitiveDataLogging();
    22. //optionsBuilder.UseSqlite($"Data Source={_dbFile};foreign keys=true");
    23. }
    24. protected override void OnModelCreating(ModelBuilder modelBuilder)
    25. {
    26. modelBuilder.Entity<PresentEntry>().HasOne(p => p.Person).WithMany(e => e.PresentEntries).HasForeignKey(p => p.PersonId).OnDelete(DeleteBehavior.Cascade);
    27. modelBuilder.Entity<GraduationEntry>().HasOne(p => p.Person).WithMany(g => g.Graduations).HasForeignKey(p => p.PersonId).OnDelete(DeleteBehavior.Cascade);
    28. }
    29. #endregion
    30. }


    ich nutze darin deine DbSimpleLogger, den DbLoggerProvider und die DbFacadeExtension
    "Hier könnte Ihre Werbung stehen..."
    OK? Dann sehe ich keinen Grund warum EF den Datensatz "nur" aktualisiert anstatt ihn zu löschen.
    Im gesamten Log sehe ich kein DELETE Statement.

    Jetzt bleibt nur noch eines, der Code vom ViewModel bzw. von der BL und/oder des DataAccess Layer (Ich kenne deine genaue Struktur nicht) damit ich zum einen sehe wie du die Daten holst und zum anderen wie du sie löscht.
    hier wäre eben auch wichtig wie du die Daten holst bezüglich des ChangeTrackers.
    Sollte das alles in einem ViewModel passieren dann am besten das gesamte ViewModel bitte.

    da kommen wir schon drauf was da nicht stimmt, ist hald ein wenig verzwickt. ;)

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    OK, dann schauen wir mal ob ich alles mitnehme....
    Also, es geht hier erstmal nur darum, eine eingetragene Graduierung wieder entfernen zu können.
    Ich hatte es zwischenzetlich mit dem setzen des DeletedFlag auf der selectierten Graduierung hinbekommen, das wäre PlanB.
    So, das entsprechende ViewModel:
    Spoiler anzeigen

    C#-Quellcode

    1. public class GraduationHistoryListViewModel : ViewModelBase
    2. {
    3. private readonly Person _personModel;
    4. #region Public Properties
    5. public string PersonName => _personModel.FullName;
    6. public ObservableCollection<GraduationHistoryListItemViewModel> GraduationHistory { get; set; }
    7. private GraduationHistoryListItemViewModel _selectedGraduation;
    8. public GraduationHistoryListItemViewModel SelectedGraduation { get => _selectedGraduation; set => SetValue(ref _selectedGraduation, value); }
    9. #endregion
    10. #region Constructor
    11. public GraduationHistoryListViewModel()
    12. {
    13. }
    14. public GraduationHistoryListViewModel(Person person)
    15. {
    16. _personModel = person;
    17. GetGraduationHistory();
    18. BackCommand = new RelayCommand(b => IoC.Application.GoToPage(ApplicationPageEnum.AdminMain, new AdminMainViewModel(IoC.Application.CurrentPerson)));
    19. DeleteGraduationCommand = new RelayCommand(d => Delete());
    20. }
    21. #endregion
    22. #region Commands
    23. public ICommand BackCommand { get; private set; }
    24. public ICommand DeleteGraduationCommand { get; private set; }
    25. #endregion
    26. #region Command Methods
    27. private void Delete()
    28. {
    29. if(GraduationHistory.Count > 1)
    30. {
    31. //Alternativ auf Deleted setzen
    32. //_personModel.Graduations.FirstOrDefault(g => g.GraduationId == SelectedGraduation.GraduationEntry.Id).DeletedFlag = true;
    33. _personModel.Graduations.Remove(SelectedGraduation.GraduationEntry);
    34. if(PeopleLogic.UpdatePerson(_personModel))
    35. GraduationHistory.Remove(SelectedGraduation);
    36. }else
    37. {
    38. IoC.UI.ShowMessage(new MessageBoxDialogViewModel
    39. {
    40. ButtonText = "OK, Danke",
    41. Message = String.Format("Sie können nicht alle Graduierungen löschen!{0}Im Zweifel, legen Sie bitte eine neue Graduierung{0}in den Teilnehmer Einstellungen an.{0}{0}", Environment.NewLine)
    42. });
    43. }
    44. }
    45. #endregion
    46. #region Helper Methods
    47. public ObservableCollection<GraduationHistoryListItemViewModel> GetGraduationHistory()
    48. {
    49. var graduationHistoryList = _personModel.Graduations.Where(g => !g.DeletedFlag).ToList();
    50. GraduationHistory = new ObservableCollection<GraduationHistoryListItemViewModel>();
    51. graduationHistoryList.ForEach(g => GraduationHistory.Add(new GraduationHistoryListItemViewModel(g)));
    52. return GraduationHistory;
    53. }
    54. #endregion
    55. }


    Vielleicht kurz zur Erklärung noch... Ich arbeite hier mit Pages. Die Pages werden über das ApplicationViewModel gesteuert. Das hat eine Methode GoToPage und verlangt eine PageEnum und ein Viewmodel.
    Navigiere ich nun zur Page, übergebe ich dem ViewModel eine Person, im Falle der Graduierung übergebe ich die Selected Person aus der vorherigen Page (das ist quasi eine Personenliste)

    OK, weiter:
    Zum obigen ViewModel gehört noch die ListItemViewModel:
    Spoiler anzeigen

    C#-Quellcode

    1. public class GraduationHistoryListItemViewModel : ViewModelBase
    2. {
    3. private readonly GraduationEntry _graduationModel;
    4. public DateTime? GraduationReceived => _graduationModel.GraduationReceived;
    5. public DateTime? GraduationHold => _graduationModel.GraduationHold;
    6. public GraduationEntry GraduationEntry => _graduationModel;
    7. public string GraduationName => IoC.Application.Graduations.FirstOrDefault(g => g.Id == _graduationModel.GraduationId).ToString();
    8. public GraduationHistoryListItemViewModel(GraduationEntry graduation)
    9. {
    10. _graduationModel = graduation;
    11. }
    12. }


    Das ApplicationViewModel hält eine Liste aller Graduierungen die es gibt in der Anwendung.

    Hier die Komplette PeopleLogic Klasse:
    Spoiler anzeigen

    C#-Quellcode

    1. public class PeopleLogic
    2. {
    3. #region Person Data Methods
    4. /// <summary>
    5. /// Legt eine übergebene Person in der Datenbank an
    6. /// </summary>
    7. /// <param name="person">die übergebene Person</param>
    8. /// <returns></returns>
    9. public static bool CreatePerson(Person person)
    10. {
    11. using (var prov = new PeopleProvider())
    12. {
    13. return prov.Insert(person);
    14. }
    15. }
    16. public static bool CreateOrUpdatePerson(Person person)
    17. {
    18. if (PersonExists(person))
    19. return UpdatePerson(person);
    20. return CreatePerson(person);
    21. }
    22. /// <summary>
    23. /// Aktualisiert die übergebene Person in der Datenbank
    24. /// </summary>
    25. /// <param name="person">die übergebene Person</param>
    26. /// <returns></returns>
    27. public static bool UpdatePerson(Person person)
    28. {
    29. using (var prov = new PeopleProvider())
    30. {
    31. return prov.Update(person);
    32. }
    33. }
    34. public static bool DeletePerson(Person person)
    35. {
    36. using (var prov = new PeopleProvider())
    37. {
    38. return prov.Delete(person);
    39. }
    40. }
    41. #endregion
    42. #region Person Get Methods
    43. /// <summary>
    44. /// gibt die ID der ersten Person aus der Datenbank zurück
    45. /// wenn keine Person in der Datenbank vorhanden ist
    46. /// wird -1 zurück gegeben
    47. /// </summary>
    48. /// <returns></returns>
    49. public static int GetLoggedInPersonId(string user, string pass)
    50. {
    51. using (var prov = new PeopleProvider())
    52. {
    53. var usr = prov.Get();
    54. if (usr.AcessData.UserName == user && usr.AcessData.EncryptedPassword == pass.GetHash())
    55. return usr.Id;
    56. return -1;
    57. }
    58. }
    59. public static int GetPresentDays(int personId)
    60. {
    61. using (var prov = new PeopleProvider())
    62. {
    63. return prov.Get(personId).PresentEntries.Count(e => !e.DeletedFlag);
    64. }
    65. }
    66. public static List<Person> GetPresentPersons()
    67. {
    68. using (var prov = new PeopleProvider())
    69. {
    70. var entryList = new List<PresentEntry>();
    71. var personList = new List<Person>();
    72. foreach (var person in prov.GetList())
    73. {
    74. foreach (var item in person.PresentEntries)
    75. {
    76. if (item.Date != DateTime.Today && item.PresentState == PresentStateEnum.Present)
    77. {
    78. person.PresentEntries.FirstOrDefault(p => p.Id == item.Id).PresentState = PresentStateEnum.Absent;
    79. UpdatePerson(person);
    80. }
    81. if (item.PresentState == PresentStateEnum.Present)
    82. personList.Add(person);
    83. }
    84. }
    85. return personList;
    86. }
    87. }
    88. /// <summary>
    89. /// gibt die erste Person aus der Datenbank zurück
    90. /// </summary>
    91. /// <returns></returns>
    92. public static Person GetPerson(int id)
    93. {
    94. using (var prov = new PeopleProvider())
    95. {
    96. return prov.Get(id);
    97. }
    98. }
    99. public static Person GetPersonByMemberId(int memberId)
    100. {
    101. using (var prov = new PeopleProvider())
    102. {
    103. return prov.GetByMemberId(memberId);
    104. }
    105. }
    106. public static int GetLastMemberId()
    107. {
    108. using (var prov = new PeopleProvider())
    109. {
    110. return prov.GetLast().MemberId;
    111. }
    112. }
    113. public static List<Person> GetPersons(PersonTypeEnum type, bool isDeactivated)
    114. {
    115. using (var prov = new PeopleProvider())
    116. {
    117. return prov.GetList().Where(p => !p.IsAdmin && p.PersonType == type && p.DeletedFlag == isDeactivated).ToList();
    118. }
    119. }
    120. #endregion
    121. #region Helper Methods
    122. public static IEnumerable<PersonTypeEnum> GetPersonTypes()
    123. {
    124. return Enum.GetValues(typeof(PersonTypeEnum)).OfType<PersonTypeEnum>();
    125. }
    126. /// <summary>
    127. /// gibt zurück ob die Tabelle People Einträge hat
    128. /// </summary>
    129. /// <returns></returns>
    130. public static bool HasEntries()
    131. {
    132. using (var prov = new PeopleProvider())
    133. {
    134. return prov.HasEntries();
    135. }
    136. }
    137. public static bool PersonExists(Person person)
    138. {
    139. return GetPerson(person.Id) != null;
    140. }
    141. #endregion
    142. }


    und der Provider dazu:
    Spoiler anzeigen

    C#-Quellcode

    1. public class PeopleProvider : ShinzanDojoProvider, IGenericProvider<Person>
    2. {
    3. public bool Insert(Person item)
    4. {
    5. Context.People.Add(item);
    6. return Context.SaveChanges() > 0;
    7. }
    8. public bool Update(Person item)
    9. {
    10. Context.People.Update(item);
    11. return Context.SaveChanges() > 0;
    12. }
    13. public bool Delete(Person item)
    14. {
    15. item.DeletedFlag = true;
    16. Context.People.Update(item);
    17. return Context.SaveChanges() > 0;
    18. }
    19. public Person Get(int id)
    20. {
    21. return Context.People.Where(p => p.Id == id).Include(l => l.AcessData).Include(g => g.Graduations).Include(pe => pe.PresentEntries).FirstOrDefault();
    22. }
    23. public Person GetByMemberId(int id)
    24. {
    25. return Context.People.Where(p => p.MemberId == id && !p.DeletedFlag).Include(g => g.Graduations).Include(pe => pe.PresentEntries).FirstOrDefault();
    26. }
    27. public Person Get()
    28. {
    29. return Context.People.Include(l => l.AcessData).Include(g => g.Graduations).Include(pe => pe.PresentEntries).FirstOrDefault();
    30. }
    31. public Person GetLast()
    32. {
    33. return HasEntries() ? Context.People.Where(p => !p.DeletedFlag).Include(g => g.Graduations).Include(pe => pe.PresentEntries).ToList().LastOrDefault() : new Person();
    34. }
    35. public List<Person> GetList(int id)
    36. {
    37. return HasEntries() ? Context.People.Include(g => g.Graduations).Include(pe => pe.PresentEntries).ToList() : new List<Person>();
    38. }
    39. public List<Person> GetList()
    40. {
    41. return HasEntries() ? Context.People.Include(g => g.Graduations).Include(pe => pe.PresentEntries).ToList() : new List<Person>();
    42. }
    43. public bool HasEntries()
    44. {
    45. return Context.People.Any();
    46. }
    47. }


    Dann noch der ProviderBase:
    Spoiler anzeigen

    C#-Quellcode

    1. public class ShinzanDojoProvider : ProviderBase<ShinzanDojoContext>, IDisposable
    2. {
    3. public void Dispose()
    4. {
    5. if (_disposeContext) Context.Dispose();
    6. }
    7. protected override void CreateContext(ShinzanDojoContext context)
    8. {
    9. Context = context;
    10. Context.ChangeTracker.LazyLoadingEnabled = false;
    11. Context.ChangeTracker.AutoDetectChangesEnabled = false;
    12. Context.Database.EnsureCreated();
    13. }
    14. }


    und der IGenericProvider:
    Spoiler anzeigen

    C#-Quellcode

    1. public interface IGenericProvider<T>
    2. {
    3. bool Insert(T item);
    4. bool Update(T item);
    5. bool Delete(T item);
    6. bool HasEntries();
    7. T Get(int id);
    8. List<T> GetList(int id);
    9. }


    ich geb noch die Provider Basisklasse mit:
    Spoiler anzeigen

    C#-Quellcode

    1. public abstract class ProviderBase<T> where T : new()
    2. {
    3. protected bool _disposeContext;
    4. protected T Context { get; set; }
    5. public ProviderBase()
    6. {
    7. CreateContext(new T());
    8. _disposeContext = true;
    9. }
    10. public ProviderBase(T context)
    11. {
    12. CreateContext(context);
    13. _disposeContext = false;
    14. }
    15. protected abstract void CreateContext(T context);
    16. }


    In der ViewModelklasse ist in Zeile 45 der entsprechende Remove Eintrag
    "Hier könnte Ihre Werbung stehen..."
    Hallo

    Irgendwie vermisse ich die Methode "GetGradiationhistory".
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    MichaHo schrieb:

    #region Helper Methods
    public ObservableCollection<GraduationHistoryListItemViewModel> GetGraduationHistory()
    {
    var graduationHistoryList = _personModel.Graduations.Where(g => !g.DeletedFlag).ToList();
    GraduationHistory = new ObservableCollection<GraduationHistoryListItemViewModel>();
    graduationHistoryList.ForEach(g => GraduationHistory.Add(new GraduationHistoryListItemViewModel(g)));
    return GraduationHistory;
    }
    #endregion


    ist im ViewModel ganz unten.
    Aufgerufen wird die Methode im Konstructor des ViewModel
    "Hier könnte Ihre Werbung stehen..."
    Oh, sorry. Übersehen. Ohne F12 Funktion etwas mühsam, und die C# Syntax macht mir Kopfweh ;)

    OK, also so wie ich das sehe rufst du per Default ja ohne Tracking ab, was ja auch gut ist.

    VB.NET-Quellcode

    1. Context.ChangeTracker.AutoDetectChangesEnabled = false;


    Aber dann bekommt der Context keine Änderungen mit. Du kannst in solch einem Fall mit .AsTracking() im Query dafür sorgen das die Entitäten getrackt werden.
    Das könnte ein Grund sein.

    Schau dir bitte beim ChangeTracker auch an welche Änderungen er mitbekommen hat. In meinem WPFNotes Projekt ist eine Methode im GenericRepository die dir das ausgibt.
    Ansonsten melde dich bitte nochmals. Bei sowas komplexen wir einem Repository mit EF Core ist sowas schwer nachzuvollziehen.

    Wenn ich am Abend daheim bin habe ich etwas mehr Zeit mir das genauer anzusehen, aber so finde ich jetzt ansonsten keinen Fehler auf die schnelle.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hi Sascha, Danke Dir für Deine Mühe...

    Ich hab nun deine SetTracking eingebaut im PeopleProvider:

    C#-Quellcode

    1. public virtual IQueryable<Person> SetTracking(bool tracking, IQueryable<Person> query)
    2. {
    3. Debug.WriteLine($"SetTracking for Query of Type '{typeof(Person).ToString()}' to '{tracking.ToString()}' and Return modified Query");
    4. if (tracking)
    5. return query.AsTracking();
    6. else
    7. return query.AsNoTracking();
    8. }


    und dann in der Update Mezhode des Provider:

    C#-Quellcode

    1. public bool Update(Person item)
    2. {
    3. SetTracking(true, Context.People.Include(g => g.Graduations).Include(p => p.PresentEntries));
    4. Context.People.Update(item);
    5. return Context.SaveChanges() > 0;
    6. }


    Ich nutze es vermutlich falsch :( SetTracking gibt mir ja ein IQueryable zurück, aber wie Update ich denn nu?
    "Hier könnte Ihre Werbung stehen..."