DataProvider mit unterschiedlichen Context, Best Practice

  • WPF

Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von florian03.

    DataProvider mit unterschiedlichen Context, Best Practice

    Hi,
    ich stehe gerade etwas auf dem Schlauch.
    Ich möchte dem User anbieten ausuchen zu können, zu welcher DB er connecten möchte. (SQLite, SQLServer, MySQL)
    Dazu habe ich für jede Möglichkeit einen Context erstellt, der von einem Basis Context ableitet und dann entsprechend den optionsBuilder configuriert.
    Soweit ist das denke ich OK (oder gibts eine bessere Möglichkeit?)

    Nun habe ich aber das Problem, das ich im DataProvider ja explicit sagen muss, mit welchem Context der Provider arbeiten soll.
    Da hänge ich gerade.

    Hier etwas Code:

    Interface Context

    C#-Quellcode

    1. public interface IDbContext : IDisposable
    2. {
    3. DbSet<Absence> Absences { get;}
    4. DbSet<Company> Companies { get;}
    5. DbSet<LoginData> Logins { get;}
    6. DbSet<Person> People { get;}
    7. DbSet<TimeEntry> TimeEntries { get;}
    8. EntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class;
    9. int SaveChanges();
    10. Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken));
    11. }



    Context Base

    C#-Quellcode

    1. public abstract class DbContextBase : DbContext, IDbContext
    2. {
    3. protected readonly string _connectionString = null;
    4. public DbContextBase(string connectionString)
    5. {
    6. _connectionString = connectionString;
    7. }
    8. #region DbSets
    9. public virtual DbSet<Absence> Absences { get; set; }
    10. public virtual DbSet<Company> Companies { get; set; }
    11. public virtual DbSet<LoginData> Logins { get; set; }
    12. public virtual DbSet<Person> People { get; set; }
    13. public virtual DbSet<TimeEntry> TimeEntries { get; set; }
    14. #endregion
    15. }



    Interface Provider

    C#-Quellcode

    1. public interface IDataProvider<T> : IDisposable
    2. {
    3. #region Get Methods
    4. Task<T> GetByIdAsync(int id);
    5. Task<IList<T>> GetListAsync();
    6. Task<IList<T>> GetListAsync(DataRequest<T> request);
    7. Task<IList<T>> GetListAsync(int skip, int take, DataRequest<T> request);
    8. Task<IList<T>> GetKeysAsync();
    9. Task<IList<T>> GetKeysAsync(DataRequest<T> request);
    10. Task<IList<T>> GetKeysAsync(int skip, int take, DataRequest<T> request);
    11. Task<int> GetListCountAsync();
    12. Task<int> GetListCountAsync(DataRequest<T> request);
    13. #endregion
    14. #region Update Methods
    15. Task<int> UpdateAsync(T item);
    16. Task<int> DeleteAsync(params T[] items);
    17. #endregion


    Basis Provider

    C#-Quellcode

    1. public abstract class DataProviderBase : IDisposable
    2. {
    3. private bool _disposeContext;
    4. internal DbContextBase Context { get; set; }
    5. public DataProviderBase()
    6. {
    7. CreateContext(/*hier neuen Context erstelles, also entweder SQLContext, oder SQLite Context usw.*/);
    8. _disposeContext = true;
    9. }
    10. internal DataProviderBase(DbContextBase context)
    11. {
    12. CreateContext(context);
    13. _disposeContext = false;
    14. }
    15. private void CreateContext(DbContextBase context)
    16. {
    17. Context = context;
    18. Context.ChangeTracker.LazyLoadingEnabled = false;
    19. Context.ChangeTracker.AutoDetectChangesEnabled = false;
    20. Context.Database.EnsureCreated();
    21. }
    22. public void Dispose()
    23. {
    24. if (_disposeContext) Context.Dispose();
    25. }
    26. }


    Beispiel People Provider

    C#-Quellcode

    1. public class PeopleProvider : DataProviderBase, IDataProvider<Person>
    2. {
    3. private readonly DbContextBase _context = null;
    4. public PeopleProvider(DbContextBase dbContext) : base(dbContext)
    5. {
    6. _context = dbContext;
    7. }
    8. public Task<int> DeleteAsync(params Person[] items)
    9. {
    10. throw new NotImplementedException();
    11. }
    12. public async Task<Person> GetByIdAsync(int id)
    13. {
    14. return await _context.People.Where(p => p.Id == id).FirstOrDefaultAsync();
    15. }
    16. public Task<IList<Person>> GetKeysAsync()
    17. {
    18. throw new NotImplementedException();
    19. }
    20. public Task<IList<Person>> GetKeysAsync(DataRequest<Person> request)
    21. {
    22. throw new NotImplementedException();
    23. }
    24. public Task<IList<Person>> GetKeysAsync(int skip, int take, DataRequest<Person> request)
    25. {
    26. throw new NotImplementedException();
    27. }
    28. public Task<IList<Person>> GetListAsync()
    29. {
    30. throw new NotImplementedException();
    31. }
    32. public Task<IList<Person>> GetListAsync(DataRequest<Person> request)
    33. {
    34. throw new NotImplementedException();
    35. }
    36. public Task<IList<Person>> GetListAsync(int skip, int take, DataRequest<Person> request)
    37. {
    38. throw new NotImplementedException();
    39. }
    40. public Task<int> GetListCountAsync()
    41. {
    42. throw new NotImplementedException();
    43. }
    44. public Task<int> GetListCountAsync(DataRequest<Person> request)
    45. {
    46. throw new NotImplementedException();
    47. }
    48. public Task<int> UpdateAsync(Person item)
    49. {
    50. throw new NotImplementedException();
    51. }
    52. }


    Wie bekomme ich es nun hin, im DataProviderBase nicht explicit einen bestimmten Context einzubauen?
    Die Auswahl, welche DB benutzt werden soll, geschieht ja im View Projekt, dort liegen auch die AppSettings da manche Settings auf View interne Sachen zugreifen müssen.
    Ich habe also im Provider Projekt keinen Zugriff auf die AppSettings

    Danke Euch für nen Denkanstoß.
    Grüße Michael
    "Hier könnte Ihre Werbung stehen..."
    Hi,

    ich beschäftige mich auch gerade mit EntityFramework.
    Ich glaube, du musst es dir gar nicht so kompliziert machen.

    Als Beispiel nehme ich hier mal die ​OnConfiguring Methode aus @Nofear23ms WPFNotes Projekt.
    Dort unterscheidet er ja auch zwischen einer InMemory Datenbank für die UnitTests und der ganz normalen.

    Hier mal der Code:

    C#-Quellcode

    1. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    2. {
    3. connString = Instance.CurrentConnectionString;
    4. if (!optionsBuilder.IsConfigured)
    5. {
    6. Debug.WriteLine($"Configured connectionstring: {connString}");
    7. if (connString.Contains("InMemory"))
    8. {
    9. optionsBuilder.UseInMemoryDatabase(connString);
    10. }
    11. else
    12. {
    13. optionsBuilder.UseSqlite(connString);
    14. }
    15. }
    16. }


    Du könntest doch einfach in deinem InstanceHolder oder in der Context Klasse selbst ein Enum mit der verwendeten Datenbank speichern.
    Dann gehst du einfach mit Select Case in der Methode die einzelnen verfügbaren Datenbanken durch und erstellst je nach Fall die entsprechende Datenbankverbindung.

    So würde ich das lösen.

    Viele Grüße
    Florian
    ----

    WebApps mit C#: Blazor