C#: separă stocarea printr-o interfață repository
Separă regulile aplicației de stocare cu o interfață C#. Testează un serviciu folosind un repository în memorie înainte de conectarea la baza de date.
În acest material
Dacă fiecare buton deschide baza de date și decide și regulile bibliotecii, testarea devine greoaie. Introducem o interfață mică pentru întrebarea de care avem nevoie: câte exemplare sunt disponibile? Serviciul depinde de această întrebare, nu de un fișier Access sau de un control vizual. O implementare în memorie este suficientă pentru a verifica regula.
Cum lucrezi cu exemplul
Programul complet folosește C# 7.3 și API-uri disponibile în .NET Framework 4.8. Deschide Lesson.csproj din arhivă sau creează un proiect Console App (.NET Framework 4.8) și înlocuiește Program.cs. Datele de demonstrație sunt incluse în cod.
Descarcă sursele și instrucțiunile.
Ideea și pașii de implementare
IBookStock declară GetAvailable. MemoryStock păstrează numerele într-un Dictionary. BookingService primește interfața în constructor și o folosește în CanReserve. Această transmitere explicită a dependenței se numește injecție prin constructor; nu necesită un framework sau un container special.
Contractul trebuie să precizeze lipsa ID-ului. În exemplu, o carte necunoscută are disponibilitate zero. Aceasta este o alegere simplificată, utilă numai dacă aplicația tratează identic lipsa și epuizarea stocului. Într-un proiect mai mare poți întoarce un rezultat distinct. Interfața nu trebuie să expună controale sau conexiuni deschise.
Exemplu și rezultat așteptat
Cartea 11 are două exemplare, cartea 12 are zero, iar cartea 99 nu apare în date. CanReserve întoarce true, false, false, afișate ca DA, NU, NU. Nu există acces la rețea sau fișier. Aceleași teste vor putea fi aplicate unei implementări care citește din baza de date, folosind o bază de test separată.
BookingService → IBookStock → MemoryStock: ID 11 → 2 → CanReserve(11) → DA.Mărește schema ↗
Codul complet
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Globalization;
using System.Xml.Linq;
using System.Data;
interface IBookStock { int GetAvailable(int id); }
class MemoryStock : IBookStock {
readonly Dictionary<int,int> values = new Dictionary<int,int> { {11,2}, {12,0} };
public int GetAvailable(int id) { int count; return values.TryGetValue(id,out count) ? count : 0; }
}
class BookingService {
readonly IBookStock stock;
public BookingService(IBookStock stock) {
if (stock == null) throw new ArgumentNullException("stock");
this.stock = stock;
}
public bool CanReserve(int id) { return stock.GetAvailable(id) > 0; }
}
class Program {
static void Main() {
var service = new BookingService(new MemoryStock());
foreach (int id in new[] {11,12,99}) Console.WriteLine(service.CanReserve(id) ? "DA" : "NU");
}
}
Rezultatul programului:
DA
NU
NU
Greșeli și limite
Nu crea o interfață cu zeci de metode doar pentru a copia toate operațiile unui tabel. Începe cu nevoile serviciului. Nu păstra o singură conexiune deschisă pentru întreaga viață a aplicației doar fiindcă repository-ul este un obiect persistent. Responsabilitatea pentru deschiderea și închiderea conexiunii rămâne în implementarea operației. Nu confunda izolarea pentru teste cu dovada că providerul real funcționează.
Exersează
Scrie o implementare care întoarce întotdeauna un exemplar și folosește-o într-un test al serviciului. Apoi adaugă o implementare care numără apelurile: trei rezervări trebuie să consulte stocul de trei ori. Explică ce verifică aceste teste și ce nu verifică. Nu poți deduce din ele că un fișier Access are schema sau permisiunile corecte.
Continuă parcursul
Parcursul complet de 50 de lecții
Lecția precedentă: C#: modelează un împrumut cu reguli și identitate
Lecția următoare: C#: evenimente și notificarea schimbărilor din model.
Referință: documentația Microsoft.
Întrebări frecvente
Am nevoie de un container DI?
Nu. Pentru acest exemplu transmiți implementarea direct în constructor.
Un repository în memorie dovedește integrarea SQL?
Nu. Verifică regula și contractul, iar integrarea cu providerul real se testează separat.
Pot schimba implementarea fără să schimb serviciul?
Da, dacă noua implementare respectă același contract și aceleași semnificații.
Un pas mai departe
Autentifică-te pentru a păstra progresul în cont.