Ich möchte eine generische Repository-Struktur erstellen, die sowohl auf Datenbanken als auch auf Tabellen angewendet werden kann, sodass ich sowohl ein Repository pro Datenbank als auch ein Repository für eine bestimmte Tabelle haben kann. Daher habe ich ein Interface
IRepository erstellt.
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| public interface IRepository { bool SaveChangesAutomatically { get; set; } Task InsertAsync<TEntity>(TEntity entity) where TEntity : class; Task InsertRangeAsync<TEntity>([NotNull] params TEntity[] entities) where TEntity : class;
Task DeleteAsync<TEntity>(TEntity entity) where TEntity : class; Task DeleteRangeAsync<TEntity>([NotNull] params TEntity[] entities) where TEntity : class; Task DeleteWhereAsync<TEntity>(Func<TEntity, bool> condition) where TEntity : class;
Task UpdateAsync<TEntity>(TEntity entity) where TEntity : class; Task UpdateRangeAsync<TEntity>([NotNull] params TEntity[] entities) where TEntity : class;
TResult Query<TEntity, TResult>(Expression<Func<IQueryable<TEntity>, TResult>> query) where TEntity : class; Task SaveChangesAsync(); } |
Der Typparameter TEntity befindet sich nicht auf Klassenebene, da ein erbendes
DatabaseRepository nicht an einen bestimmten Entitätstyp gebunden ist, da es mehrere Tabellen mit unterschiedlichen Entitätstypen enthält.
Das Interface wird in der abstrakten Klasse
Repository implementiert.
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| public abstract class Repository<TDbContext> : IRepository where TDbContext : DbContext { protected TDbContext DbContext { get; } public virtual bool SaveChangesAutomatically { get; set; }
protected Repository(TDbContext dbContext) { this.DbContext = dbContext; }
protected abstract DbSet<TEntity> UseTable<TEntity>() where TEntity : class; } |
Diese Klasse führt den DB-Kontext ein, implementiert aber die Member unabhängig davon. Es erhält die zu verwendende Tabelle nicht aus dem DB-Kontext, sondern von einer anderen Methode namens
UseTable.
Diese Klasse soll dann sowohl von der
DatabaseRepository- als auch von der
TableRepository-Klasse geerbt werden. Das
DatabaseRepository implementiert die Methode
UseTable, indem es die erste Property des DB-Kontexts vom Typ
DbSet<TEntity> zurückgibt. Das
TableRepository soll eine bestimmte DbSet-Eigenschaft zurückgeben.
Aus diesem Grund ist ein generischer Typparameter in den Methoden eines
TableRepository nicht mehr erforderlich, da immer dieselbe Tabelle verwendet wird. Aus diesem Grund möchte ich den Typparameter so einschränken, dass er nur der Entitätstyp der Tabelle ist, damit die Methode tatsächlich das DbSet zurückgeben kann, was derzeit nicht möglich ist.
Am Ende sollte es ungefähr so aussehen:
C#-Quelltext
1: 2: 3: 4: 5: 6:
| public abstract class TableRepository<TDbContext, TEntity> : Repository<TDbContext> where TDbContext : DbContext where TEntity : class { public TableRepository(TDbContext dbContext) : base(dbContext) { } protected abstract override DbSet<TEntity> UseTable<TEntity>(); } |
Und dann die finale Klasse für eine bestimmte Tabelle:
C#-Quelltext
1: 2: 3: 4: 5: 6:
| public class UserTableRepository : TableRepository<ApplicationDbContext, IdentityUser> { public UserTableRepository(ApplicationDbContext dbContext) : base(dbContext) { } protected override DbSet<IdentityUser> UseTable() => this.DbContext.Users; } |
Kann man dieses Verhalten irgendwie erreichen? Umgekehrt wäre es auch in Ordnung, also Typeparameter
TEntity breits in
IRepository und
Repository eingeführt. DatabaseRepository bekommt jedoch nur TDbContext und erlaubt jede TEntity.
Ist dies irgendwie möglich?