Repository Pattern στη C# με SOLID αρχές

Το Repository Pattern είναι ένα από τα πιο δημοφιλή (design patterns) για τον διαχωρισμό της επιχειρηματικής λογικής από την πρόσβαση στη βάση δεδομένων. Επιτρέπει στον κώδικα να είναι καθαρός, ευέλικτος και επεκτάσιμος. - Γιατί να χρησιμοποιούμε το Repository Pattern; Πλεονεκτήματα ✔ Διαχωρισμός Επιπέδων – Το Repository απομονώνει τη βάση δεδομένων από το business logic. ✔ Εύκολη αντικατάσταση – Αν θέλουμε να αλλάξουμε από Entity Framework σε Dapper ή άλλη ORM, μπορούμε να το κάνουμε χωρίς να αλλάξουμε όλη την εφαρμογή. ✔ Mocking για Unit Tests – Μπορούμε εύκολα να κάνουμε mock τα repositories, βελτιώνοντας τη δυνατότητα δοκιμών. ✔ Καλύτερη διαχείριση δεδομένων – Παρέχει μία κεντρική προσέγγιση για πρόσβαση στα δεδομένα. - SOLID Αρχές και Repository Pattern Όταν εφαρμόζουμε το Repository Pattern, είναι σημαντικό να ακολουθούμε τις αρχές SOLID για να διατηρούμε τον κώδικα καθαρό και επεκτάσιμο. 1️⃣ Single Responsibility Principle (SRP) – Κάθε repository πρέπει να έχει μία ευθύνη: την πρόσβαση στα δεδομένα ενός συγκεκριμένου entity. 2️⃣ Open-Closed Principle (OCP) – Πρέπει να μπορούμε να προσθέτουμε νέα repositories ή queries χωρίς να αλλάζουμε τον υπάρχοντα κώδικα. 3️⃣ Liskov Substitution Principle (LSP) – Κάθε repository μπορεί να αντικατασταθεί από ένα άλλο χωρίς να προκαλέσει προβλήματα. 4️⃣ Interface Segregation Principle (ISP) – Αντί για ένα repository με πολλές μεθόδους, είναι καλύτερο να έχουμε εξειδικευμένα interfaces. 5️⃣ Dependency Inversion Principle (DIP) – Το repository δεν πρέπει να εξαρτάται άμεσα από την ORM (π.χ. Entity Framework), αλλά από ένα abstraction. Παράδειγμα Repository στη C# με SOLID Αρχές 1️⃣ Οντότητα (Entity) Ας υποθέσουμε ότι έχουμε μια εφαρμογή με Πελάτες (Customers). public class Customer { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } } 2️⃣ Δημιουργία του Γενικού Repository (Generic Repository) Ένα γενικό repository (Generic Repository) μας επιτρέπει να έχουμε επανχρησιμοποιήσιμο κώδικα. public interface IRepository where T : class { Task GetByIdAsync(int id); Task GetAllAsync(); Task AddAsync(T entity); Task UpdateAsync(T entity); Task DeleteAsync(int id); } 3️⃣ Υλοποίηση του Generic Repository με το Entity Framework Το EF Core θα χρησιμοποιηθεί για την πρόσβαση στη βάση δεδομένων. public class Repository : IRepository where T : class { protected readonly AppDbContext _context; protected readonly DbSet _dbSet; public Repository(AppDbContext context) { _context = context; _dbSet = context.Set(); } public async Task GetByIdAsync(int id) => await _dbSet.FindAsync(id); public async Task GetAllAsync() => await _dbSet.ToListAsync(); public async Task AddAsync(T entity) { await _dbSet.AddAsync(entity); await _context.SaveChangesAsync(); } public async Task UpdateAsync(T entity) { _dbSet.Update(entity); await _context.SaveChangesAsync(); } public async Task DeleteAsync(int id) { var entity = await _dbSet.FindAsync(id); if (entity != null) { _dbSet.Remove(entity); await _context.SaveChangesAsync(); } } } 4️⃣ Δημιουργία Εξειδικευμένου Repository για Πελάτες (Customer Repository) Αντί να προσθέτουμε όλες τις μεθόδους σε ένα repository, δημιουργούμε εξειδικευμένα repositories. 4.1 Ορίζουμε ένα εξειδικευμένο interface public interface ICustomerRepository : IRepository { Task GetByEmailAsync(string email); } 4.2 Υλοποίηση του Customer Repository public class CustomerRepository : Repository, ICustomerRepository { public CustomerRepository(AppDbContext context) : base(context) { } public async Task GetByEmailAsync(string email) { return await _context.Customers.FirstOrDefaultAsync(c => c.Email == email); } } Είναι καλό να έχουμε πολλά Repositories ή ένα μεγάλο με πολλές μεθόδους;

Feb 15, 2025 - 23:53
 0
Repository Pattern στη C# με SOLID αρχές

Το Repository Pattern είναι ένα από τα πιο δημοφιλή (design patterns) για τον διαχωρισμό της επιχειρηματικής λογικής από την πρόσβαση στη βάση δεδομένων. Επιτρέπει στον κώδικα να είναι καθαρός, ευέλικτος και επεκτάσιμος.

- Γιατί να χρησιμοποιούμε το Repository Pattern;

Πλεονεκτήματα

Διαχωρισμός Επιπέδων – Το Repository απομονώνει τη βάση δεδομένων από το business logic.
Εύκολη αντικατάσταση – Αν θέλουμε να αλλάξουμε από Entity Framework σε Dapper ή άλλη ORM, μπορούμε να το κάνουμε χωρίς να αλλάξουμε όλη την εφαρμογή.
Mocking για Unit Tests – Μπορούμε εύκολα να κάνουμε mock τα repositories, βελτιώνοντας τη δυνατότητα δοκιμών.
Καλύτερη διαχείριση δεδομένων – Παρέχει μία κεντρική προσέγγιση για πρόσβαση στα δεδομένα.

- SOLID Αρχές και Repository Pattern

Όταν εφαρμόζουμε το Repository Pattern, είναι σημαντικό να ακολουθούμε τις αρχές SOLID για να διατηρούμε τον κώδικα καθαρό και επεκτάσιμο.

1️⃣ Single Responsibility Principle (SRP) – Κάθε repository πρέπει να έχει μία ευθύνη: την πρόσβαση στα δεδομένα ενός συγκεκριμένου entity.
2️⃣ Open-Closed Principle (OCP) – Πρέπει να μπορούμε να προσθέτουμε νέα repositories ή queries χωρίς να αλλάζουμε τον υπάρχοντα κώδικα.
3️⃣ Liskov Substitution Principle (LSP) – Κάθε repository μπορεί να αντικατασταθεί από ένα άλλο χωρίς να προκαλέσει προβλήματα.
4️⃣ Interface Segregation Principle (ISP) – Αντί για ένα repository με πολλές μεθόδους, είναι καλύτερο να έχουμε εξειδικευμένα interfaces.
5️⃣ Dependency Inversion Principle (DIP) – Το repository δεν πρέπει να εξαρτάται άμεσα από την ORM (π.χ. Entity Framework), αλλά από ένα abstraction.

Παράδειγμα Repository στη C# με SOLID Αρχές

1️⃣ Οντότητα (Entity)

Ας υποθέσουμε ότι έχουμε μια εφαρμογή με Πελάτες (Customers).

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

2️⃣ Δημιουργία του Γενικού Repository (Generic Repository)

Ένα γενικό repository (Generic Repository) μας επιτρέπει να έχουμε επανχρησιμοποιήσιμο κώδικα.

public interface IRepository where T : class
{
    Task GetByIdAsync(int id);
    Task> GetAllAsync();
    Task AddAsync(T entity);
    Task UpdateAsync(T entity);
    Task DeleteAsync(int id);
}

3️⃣ Υλοποίηση του Generic Repository με το Entity Framework

Το EF Core θα χρησιμοποιηθεί για την πρόσβαση στη βάση δεδομένων.

public class Repository : IRepository where T : class
{
    protected readonly AppDbContext _context;
    protected readonly DbSet _dbSet;

    public Repository(AppDbContext context)
    {
        _context = context;
        _dbSet = context.Set();
    }

    public async Task GetByIdAsync(int id) => await _dbSet.FindAsync(id);

    public async Task> GetAllAsync() => await _dbSet.ToListAsync();

    public async Task AddAsync(T entity)
    {
        await _dbSet.AddAsync(entity);
        await _context.SaveChangesAsync();
    }

    public async Task UpdateAsync(T entity)
    {
        _dbSet.Update(entity);
        await _context.SaveChangesAsync();
    }

    public async Task DeleteAsync(int id)
    {
        var entity = await _dbSet.FindAsync(id);
        if (entity != null)
        {
            _dbSet.Remove(entity);
            await _context.SaveChangesAsync();
        }
    }
}

4️⃣ Δημιουργία Εξειδικευμένου Repository για Πελάτες (Customer Repository)

Αντί να προσθέτουμε όλες τις μεθόδους σε ένα repository, δημιουργούμε εξειδικευμένα repositories.

4.1 Ορίζουμε ένα εξειδικευμένο interface

public interface ICustomerRepository : IRepository
{
    Task GetByEmailAsync(string email);
}

4.2 Υλοποίηση του Customer Repository

public class CustomerRepository : Repository, ICustomerRepository
{
    public CustomerRepository(AppDbContext context) : base(context) { }

    public async Task GetByEmailAsync(string email)
    {
        return await _context.Customers.FirstOrDefaultAsync(c => c.Email == email);
    }
}

Είναι καλό να έχουμε πολλά Repositories ή ένα μεγάλο με πολλές μεθόδους;