Sto usando un modello oggetto query (simile a questo ) per gestire le query disparate evitando facciate / repository bloadati.
Un oggetto query accetta un numero di parametri del costruttore, che rappresentano gli argomenti di query. La query viene quindi passata a un IQueryHandler
dal chiamante, nel quale viene iniettato IDataContext
. Il IDataContext
viene quindi passato a un metodo Execute
di IQuery
.
La cosa che non mi piace è questa:
public interface IQuery<TResult>
{
public TResult Execute(IDataContext context);
}
Poiché IDataContext
è passato al metodo e quindi dichiarato esplicitamente nell'interfaccia, non c'è alcuna opzione per avere una query che recuperi le cose tramite un meccanismo diverso - IQuery
è accoppiato a qualsiasi cosa definisca l'interfaccia IDataContext
. Supponiamo, per esempio, di passare da un archivio DB SQL a un DB del documento.
Ho provato un paio di alternative, ma nessuno dei due riesce a farmi dove voglio essere.
Il primo consiste nell'iniettare il contesto dei dati nel costruttore della query e utilizzare una factory per generare gli oggetti query. Questo disaccoppia correttamente le interfacce, ma ora lavorare con le query è più complicato. Invece di usare l'inizializzatore costruttore / oggetto per impostare la query, i chiamanti devono fare qualcosa del genere:
var query = _queryFactory.Create<PersonQuery, Person>(); // Second type argument can't be inferred from first due to limitations in generic type inference.
query.Name = "Bob";
query.Age = 32;
var result = query.Execute();
Il secondo è di astrarre l'esecuzione della query - e quindi la dipendenza da IDataContext
- in una classe handler, quindi avere un'altra classe che risolve un gestore per ogni query, che di nuovo astrae la dipendenza da qualsiasi interfaccia:
public class ISomeQueryHandler : IQueryHandler<SomeQuery, SomeQueryResult>
{
private readonly IDataContext _context = ...;
public SomeQueryResult Execute(SomeQuery query) { ... }
}
Non mi piace l'opzione questa perché significa che ogni nuova query implica la scrittura di due classi, che, di nuovo, sono ingombranti e aumentano il potenziale per una query di esistere senza alcun modo di gestirla ( coinvolge anche un'interfaccia marker vuota per le query poiché il metodo Execute
viene spostato sul gestore, che si sente sempre disattivato). Implica anche una risoluzione delle dipendenze basata su una convenzione piuttosto funky per ottenere il gestore di destra per ogni query.
La creazione di un'astrazione su IDataContext
non è realmente fattibile a causa della sua complessità.
C'è un mezzo felice che non ho visto?