Baustein 4: Repositories verdrahten Teil B (Kaskaden & Batch, Kapitel 10)
Die heiklen Faelle, die nicht ueber die oeffentliche Save/Delete-Methode laufen, sondern mehrere Collections direkt anfassen: GroupRepository (Save + Delete-Kaskade), ExamRepository.Delete, ExamResultRepository (Save/SaveMany), DocumentationRepository (Save/Delete/HardDelete), ParticipationSessionRepository.Delete, ParticipationRepository (SaveMany/DeleteBySession), CompetencyDomainRepository (inkl. Replace- /DeleteBySubjectAndGrade). Regel: pro oeffentlichem Repository-Aufruf genau EIN Sync-Ereignis (z.B. GroupRepository.Delete -> ein Group/Delete-Ereignis, nicht 14), Batch-Methoden feuern ein Ereignis pro betroffener Entitaet. Dafuer ExamRepository.Delete/ParticipationSessionRepository.Delete/ DocumentationRepository.HardDelete auf die in Baustein 2 vorbereiteten LiteDbContext-Kaskadenhelfer umgestellt (CascadeDeleteExam, CascadeDeleteParticipationSession, CascadeHardDeleteDocumentation) - dieselbe Kaskade existiert dadurch nur an einer Stelle im Code, nicht doppelt (wichtig fuer Baustein 5, wo ein eingehendes Sync-Ereignis sie erneut braucht). Tests pruefen explizit die Ereignis-Anzahl bei Kaskaden/Batches. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -156,6 +156,32 @@ public class LiteDbContext : IDisposable
|
||||
});
|
||||
}
|
||||
|
||||
/// Führt dieselbe Kaskade wie <c>ExamRepository.Delete</c> aus (siehe <see cref="CascadeDeleteGroup"/>).
|
||||
internal void CascadeDeleteExam(Guid id)
|
||||
{
|
||||
foreach (var result in ExamResults.Find(r => r.ExamId == id).ToList())
|
||||
ExamResults.Delete(result.Id);
|
||||
Exams.Delete(id);
|
||||
}
|
||||
|
||||
/// Führt dieselbe Kaskade wie <c>ParticipationSessionRepository.Delete</c> aus (siehe
|
||||
/// <see cref="CascadeDeleteGroup"/>).
|
||||
internal void CascadeDeleteParticipationSession(Guid id)
|
||||
{
|
||||
ParticipationSessions.Delete(id);
|
||||
foreach (var e in ParticipationEntries.Find(e => e.SessionId == id).ToList())
|
||||
ParticipationEntries.Delete(e.Id);
|
||||
}
|
||||
|
||||
/// Führt dieselbe Kaskade wie <c>DocumentationRepository.HardDelete</c> aus (siehe
|
||||
/// <see cref="CascadeDeleteGroup"/>).
|
||||
internal void CascadeHardDeleteDocumentation(Guid id)
|
||||
{
|
||||
if (Documentation.FindById(id) is { } doc)
|
||||
foreach (var attachment in doc.Attachments) Attachments.Delete(attachment.StorageId);
|
||||
Documentation.Delete(id);
|
||||
}
|
||||
|
||||
internal void ExecuteInTransaction(Action action)
|
||||
{
|
||||
_db.BeginTrans();
|
||||
|
||||
@@ -79,11 +79,13 @@ public class GroupRepository(LiteDbContext db) : IGroupRepository
|
||||
throw new InvalidOperationException("Das gewählte Fach existiert nicht mehr.");
|
||||
g.UpdatedAt = DateTime.UtcNow;
|
||||
db.Groups.Upsert(g);
|
||||
db.OnChange?.Invoke(nameof(LearningGroup), g.Id.ToString(), "Save", g);
|
||||
}
|
||||
public void Delete(Guid id)
|
||||
{
|
||||
ArchivedGroupWriteGuard.EnsureActive(db, id);
|
||||
db.CascadeDeleteGroup(id);
|
||||
db.OnChange?.Invoke(nameof(LearningGroup), id.ToString(), "Delete", null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,14 +183,14 @@ public class ExamRepository(LiteDbContext db) : IExamRepository
|
||||
ArchivedGroupWriteGuard.EnsureActive(db, e.GroupId);
|
||||
e.UpdatedAt = DateTime.UtcNow;
|
||||
db.Exams.Upsert(e);
|
||||
db.OnChange?.Invoke(nameof(Exam), e.Id.ToString(), "Save", e);
|
||||
}
|
||||
public void Delete(Guid id)
|
||||
{
|
||||
if (db.Exams.FindById(id) is { } exam)
|
||||
ArchivedGroupWriteGuard.EnsureActive(db, exam.GroupId);
|
||||
foreach (var result in db.ExamResults.Find(r => r.ExamId == id).ToList())
|
||||
db.ExamResults.Delete(result.Id);
|
||||
db.Exams.Delete(id);
|
||||
db.CascadeDeleteExam(id);
|
||||
db.OnChange?.Invoke(nameof(Exam), id.ToString(), "Delete", null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +208,7 @@ public class ExamResultRepository(LiteDbContext db) : IExamResultRepository
|
||||
ArchivedGroupWriteGuard.EnsureActive(db, exam.GroupId);
|
||||
r.UpdatedAt = DateTime.UtcNow;
|
||||
db.ExamResults.Upsert(r);
|
||||
db.OnChange?.Invoke(nameof(ExamResult), r.Id.ToString(), "Save", r);
|
||||
}
|
||||
public void SaveMany(List<ExamResult> results)
|
||||
{
|
||||
@@ -215,6 +218,8 @@ public class ExamResultRepository(LiteDbContext db) : IExamResultRepository
|
||||
var now = DateTime.UtcNow;
|
||||
foreach (var r in results) r.UpdatedAt = now;
|
||||
db.ExamResults.Upsert(results);
|
||||
foreach (var r in results)
|
||||
db.OnChange?.Invoke(nameof(ExamResult), r.Id.ToString(), "Save", r);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +361,12 @@ public class DocumentationRepository(LiteDbContext db) : IDocumentationRepositor
|
||||
.OrderByDescending(d => d.Date).ToList();
|
||||
public List<Documentation> GetAll() =>
|
||||
db.Documentation.Find(d => !d.IsDeleted).OrderByDescending(d => d.Date).ToList();
|
||||
public void Save(Documentation d) { d.UpdatedAt = DateTime.UtcNow; db.Documentation.Upsert(d); }
|
||||
public void Save(Documentation d)
|
||||
{
|
||||
d.UpdatedAt = DateTime.UtcNow;
|
||||
db.Documentation.Upsert(d);
|
||||
db.OnChange?.Invoke(nameof(Documentation), d.Id.ToString(), "Save", d);
|
||||
}
|
||||
public void Delete(Guid id)
|
||||
{
|
||||
var doc = db.Documentation.FindById(id);
|
||||
@@ -364,13 +374,14 @@ public class DocumentationRepository(LiteDbContext db) : IDocumentationRepositor
|
||||
doc.IsDeleted = true;
|
||||
doc.DeletedAt = DateTime.UtcNow;
|
||||
db.Documentation.Update(doc);
|
||||
// Weiches Löschen ist inhaltlich eine Änderung, kein Entfernen -> "Save", damit ein
|
||||
// anwendendes Gerät den IsDeleted-Stand einfach übernimmt statt den Datensatz zu entfernen.
|
||||
db.OnChange?.Invoke(nameof(Documentation), id.ToString(), "Save", doc);
|
||||
}
|
||||
public void HardDelete(Guid id)
|
||||
{
|
||||
var doc = db.Documentation.FindById(id);
|
||||
if (doc is not null)
|
||||
foreach (var attachment in doc.Attachments) db.Attachments.Delete(attachment.StorageId);
|
||||
db.Documentation.Delete(id);
|
||||
db.CascadeHardDeleteDocumentation(id);
|
||||
db.OnChange?.Invoke(nameof(Documentation), id.ToString(), "Delete", null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,14 +434,14 @@ public class ParticipationSessionRepository(LiteDbContext db) : IParticipationSe
|
||||
ArchivedGroupWriteGuard.EnsureActive(db, s.GroupId);
|
||||
s.UpdatedAt = DateTime.UtcNow;
|
||||
db.ParticipationSessions.Upsert(s);
|
||||
db.OnChange?.Invoke(nameof(ParticipationSession), s.Id.ToString(), "Save", s);
|
||||
}
|
||||
public void Delete(Guid id)
|
||||
{
|
||||
if (db.ParticipationSessions.FindById(id) is { } session)
|
||||
ArchivedGroupWriteGuard.EnsureActive(db, session.GroupId);
|
||||
db.ParticipationSessions.Delete(id);
|
||||
foreach (var e in db.ParticipationEntries.Find(e => e.SessionId == id).ToList())
|
||||
db.ParticipationEntries.Delete(e.Id);
|
||||
db.CascadeDeleteParticipationSession(id);
|
||||
db.OnChange?.Invoke(nameof(ParticipationSession), id.ToString(), "Delete", null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,6 +459,7 @@ public class ParticipationRepository(LiteDbContext db) : IParticipationRepositor
|
||||
ArchivedGroupWriteGuard.EnsureActive(db, session.GroupId);
|
||||
e.UpdatedAt = DateTime.UtcNow;
|
||||
db.ParticipationEntries.Upsert(e);
|
||||
db.OnChange?.Invoke(nameof(ParticipationEntry), e.Id.ToString(), "Save", e);
|
||||
}
|
||||
public void SaveMany(List<ParticipationEntry> entries)
|
||||
{
|
||||
@@ -457,13 +469,18 @@ public class ParticipationRepository(LiteDbContext db) : IParticipationRepositor
|
||||
var now = DateTime.UtcNow;
|
||||
foreach (var e in entries) e.UpdatedAt = now;
|
||||
db.ParticipationEntries.Upsert(entries);
|
||||
foreach (var e in entries)
|
||||
db.OnChange?.Invoke(nameof(ParticipationEntry), e.Id.ToString(), "Save", e);
|
||||
}
|
||||
public void DeleteBySession(Guid sessionId)
|
||||
{
|
||||
if (db.ParticipationSessions.FindById(sessionId) is { } session)
|
||||
ArchivedGroupWriteGuard.EnsureActive(db, session.GroupId);
|
||||
foreach (var e in db.ParticipationEntries.Find(e => e.SessionId == sessionId).ToList())
|
||||
{
|
||||
db.ParticipationEntries.Delete(e.Id);
|
||||
db.OnChange?.Invoke(nameof(ParticipationEntry), e.Id.ToString(), "Delete", null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -682,14 +699,26 @@ public class CompetencyDomainRepository(LiteDbContext db) : ICompetencyDomainRep
|
||||
.OrderBy(d => d.SortOrder)
|
||||
.ToList();
|
||||
public CompetencyDomain? GetById(Guid id) => db.CompetencyDomains.FindById(id);
|
||||
public void Save(CompetencyDomain d) { d.UpdatedAt = DateTime.UtcNow; db.CompetencyDomains.Upsert(d); }
|
||||
public void Delete(Guid id) => db.CompetencyDomains.Delete(id);
|
||||
public void Save(CompetencyDomain d)
|
||||
{
|
||||
d.UpdatedAt = DateTime.UtcNow;
|
||||
db.CompetencyDomains.Upsert(d);
|
||||
db.OnChange?.Invoke(nameof(CompetencyDomain), d.Id.ToString(), "Save", d);
|
||||
}
|
||||
public void Delete(Guid id)
|
||||
{
|
||||
db.CompetencyDomains.Delete(id);
|
||||
db.OnChange?.Invoke(nameof(CompetencyDomain), id.ToString(), "Delete", null);
|
||||
}
|
||||
public void DeleteBySubjectAndGrade(Guid subjectId, int gradeLevel)
|
||||
{
|
||||
foreach (var d in db.CompetencyDomains
|
||||
.Find(d => d.SubjectId == subjectId && d.GradeLevel == gradeLevel)
|
||||
.ToList())
|
||||
{
|
||||
db.CompetencyDomains.Delete(d.Id);
|
||||
db.OnChange?.Invoke(nameof(CompetencyDomain), d.Id.ToString(), "Delete", null);
|
||||
}
|
||||
}
|
||||
public void ReplaceForSubjectAndGrade(Guid subjectId, int gradeLevel, List<CompetencyDomain> domains)
|
||||
{
|
||||
@@ -697,7 +726,10 @@ public class CompetencyDomainRepository(LiteDbContext db) : ICompetencyDomainRep
|
||||
{
|
||||
foreach (var domain in db.CompetencyDomains
|
||||
.Find(d => d.SubjectId == subjectId && d.GradeLevel == gradeLevel).ToList())
|
||||
{
|
||||
db.CompetencyDomains.Delete(domain.Id);
|
||||
db.OnChange?.Invoke(nameof(CompetencyDomain), domain.Id.ToString(), "Delete", null);
|
||||
}
|
||||
var now = DateTime.UtcNow;
|
||||
foreach (var domain in domains)
|
||||
{
|
||||
@@ -705,6 +737,7 @@ public class CompetencyDomainRepository(LiteDbContext db) : ICompetencyDomainRep
|
||||
domain.GradeLevel = gradeLevel;
|
||||
domain.UpdatedAt = now;
|
||||
db.CompetencyDomains.Upsert(domain);
|
||||
db.OnChange?.Invoke(nameof(CompetencyDomain), domain.Id.ToString(), "Save", domain);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user