Da ciò che descrivi a livello base, sei:
-
Estrazione di dati da una memoria persistente
-
Analizzalo in una sorta di formato generico
-
Mapping su oggetti e proprietà
Il Pattern del repository risolve questo problema. Comprende tre tipi principali di classi:
-
La classe "Repository", che si occupa solo del modello a oggetti, ad es. le tue "classi di entità" o "modello di dominio" nella tua applicazione. Può fornire sia l'accesso in lettura che in scrittura per questi dati
-
La classe "Gateway", che è responsabile della lettura e scrittura dei dati in una memoria permanente, sia essa un database, un servizio web o un file. Come vedremo in seguito, è qui che dovrebbe esserci l'analisi dei file.
-
La classe "Factory", che crea istanze del tuo modello di dominio e mappa i dati dai dati generici, il gateway ritorna al tuo modello di dominio.
La chiave di questo modello è l'implementazione di interfacce per ogni tipo di classe. Esaminiamo come un blog potrebbe essere modellato e mantenuto in questo modo.
Modello e interfacce del dominio pattern di repository
Iniziamo con il modello di dominio:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Niente di troppo elegante. Successivamente, dobbiamo definire le nostre interfacce, prima definendo l'interfaccia per il repository del blog.
public interface IBlogRepository
{
Blog Find(int id);
void Create(Blog blog);
void Update(Blog blog);
void Delete(Blog blog);
}
L'interfaccia IBlogRepository
si concentra principalmente sul tuo modello di dominio senza menzionare il modo in cui la persistenza viene effettivamente raggiunta. Ciò fornisce uno strato di disaccoppiamento per il resto della tua applicazione.
Successivamente, l'interfaccia del gateway del blog consente al repository di preformare le operazioni CRUD specifiche (Crea, Leggi, Aggiorna, Elimina) nel tuo archivio dati.
public interface IBlogGateway
{
IList<IDictionary<string, object>> Load();
void Create(int id, string name, string description);
void Update(int id, string name, string description);
void Delete(int id);
}
È meglio mantenere il "gateway" privo di qualsiasi conoscenza sul modello di dominio, in quanto ciò garantirà la massima flessibilità per modificare il modello di dominio con effetti limitati sul "gateway" e per modificare il "gateway" con un impatto limitato sul tuo modello di dominio.
Il metodo Load
restituisce solo un elenco di oggetti del dizionario, un formato generico e piacevole per i dati.
L'ultima interfaccia è l'oggetto factory del blog responsabile della creazione di istanze del tuo modello di dominio e dell'associazione dei dati generici agli oggetti Blog
.
public interface IBlogFactory
{
Blog CreateInstance(IDictionary<string, object> data);
void UpdateInstance(IDictionary<string, object> data, Blog instance);
}
Classi concrete del modello di deposito
Ora che abbiamo definito le nostre interfacce, iniziamo creando una classe BlogRepository
che implementa l'interfaccia IBlogRepository
:
public class BlogRepository : IBlogRepository
{
private IBlogGateway gateway;
private IBlogFactory factory;
private IList<Blog> cache;
private IList<Blog> Cache
{
get
{
if (cache == null)
{
var data = gateway.Load();
cache = new List<Blog>();
foreach (var record in data)
{
cache.Add(factory.CreateInstance(record));
}
}
return cache;
}
}
public BlogRepository(IBlogGateway gateway = null, IBlogFactory factory = null)
{
this.gateway = gateway ?? new BlogFileGateway(@"C:\Path\To\Blogs.txt");
this.factory = factory ?? new BlogFactory();
}
public Blog Find(int id)
{
Cache.SingleOrDefault(b => b.Id == id);
}
public void Create(Blog blog)
{
Cache.Add(blog);
gateway.Create(blog.Id, blog.Name, blog.Description);
}
public void Update(Blog blog)
{
gateway.Update(blog.Id, blog.Name, blog.Description);
}
public void Delete(Blog blog)
{
Cache.RemoveAll(b => b.Id == blog.Id);
gateway.Delete(blog.Id);
}
}
Ora possiamo iniziare a vedere come ogni pezzo si adatta. Il costruttore di BlogRepository
ti consente di passare il tuo gateway e l'oggetto factory, ricadendo su un'implementazione predefinita di tua scelta.
Per trovare un singolo Blog
, il repository carica prima tutti i dati dal gateway e li associa agli oggetti usando la fabbrica. Il metodo Find
restituisce quindi il primo Blog che corrisponde all'Id. Certo, potresti voler creare un metodo sull'oggetto "gateway" per restituire un singolo record, ma il repository è anche un buon posto per implementare il caching.
Ora entriamo nella parte che ti interessa: analizzare il file.
public class BlogFileGateway : IBlogGateway
{
private string filePath;
public BlogFileGateway(string filePath)
{
this.filePath = filePath;
}
public IList<IDictionary<string, object>> Load()
{
// Read entire file and return the parsed data
}
public void Create(int id, string name, string description)
{
// Append the new data to the file
}
public void Update(int id, string name, string description)
{
// Update the correct row within the file
}
public void Delete(int id)
{
// Delete the correct row within the file
}
}
Il BlogFileGateway
implementa l'interfaccia IBlogGateway
. Qui puoi aprire, leggere e analizzare il file. Se disponi di un formato di file generico, le tue classi "gateway" potrebbero creare un'istanza di un'altra classe responsabile dell'analisi di ciascun record nel file.
E solo per buona misura, la classe blog factory:
public class BlogFactory : IBlogFactory
{
public Blog CreateInstance(IDictionary<string, object> data)
{
if (data == null)
return null;
Blog instance = new Blog();
UpdateInstance(data, instance);
return blog;
}
public void UpdateInstance(IDictionary<string, object> data, Blog instance)
{
instance.Id = int.Parse(data["id"].ToString());
instance.Name = data["name"].ToString();
instance.Description = data["description"].ToString();
}
}
Perché il modello del repository funziona meglio
Poiché disaccoppia l'accesso ai dati e la mappatura dal tuo modello di dominio, sei libero di iniziare con l'archiviazione dei file. Passare allo storage del database implicherebbe la scrittura di una nuova classe gateway per accedere al database e una nuova classe factory per mappare i dati dal database al modello di dominio Blog
. Se in seguito si decide di passare a un'architettura orientata ai servizi, è necessario solo scrivere un altro gateway per chiamare il servizio Web e un'altra fabbrica. È possibile modificare o cambiare radicalmente i meccanismi di archiviazione senza modificare le parti principali della propria applicazione.
@BartVanIngenSchenau ha detto nel suo risposta :
For other kinds of data access (data exchange with third parties, reading/writing files, etc.) those architectures have the concept of Services to perform the transportation of the data and to transform it between the external format and something that the business layer can work with.
(sottolineatura mia)
Il vantaggio dell'utilizzo del modello di repository è che il codice che chiama un servizio web non deve vivere nel livello generico e spesso abusato della tua applicazione chiamata "servizi". La chiamata al servizio web avviene nel gateway.
Esempio di utilizzo
Trova un blog, modifica un valore e aggiornalo:
IBlogRepository repository = new BlogRepository();
Blog blog = repository.Find(3);
blog.Description = "Black and white, and read all over";
repository.Update(blog);
In seguito, è necessario utilizzare un database MySQL per scrivere le classi BlogMySqlGateway
e BlogMySqlFactory
, quindi modificare una riga di codice:
IBlogRepository repository = new BlogRepository(new BlogMySqlGateway(), new BlogMySqlFactory());
Blog blog = repository.Find(3);
blog.Description = "Black and white, and read all over";
repository.Update(blog);
Niente altro deve cambiare. In seguito, decidi che tutto ciò ha bisogno di vivere in "The Cloud":
IBlogRepository repository = new BlogRepository(new BlogWebServiceGateway(), new BlogWebServiceFactory());
Blog blog = repository.Find(3);
blog.Description = "Black and white, and read all over";
repository.Update(blog);
Nient'altro deve cambiare.