2013-01-16 13 views
12

Sto tentando di utilizzare il seguente interfaccia Repository generico per l'iniezione DI e costruttore:Come utilizzare l'interfaccia del repository che utilizza Generics with Dependency Injection?

public interface IRepository<TEntity> : IDisposable where TEntity : class 

Il problema è per definire un'istanza dell'interfaccia, devo fornire il tipo di classe come questo:

private IRepository<Person> _personRepository; 

Il problema con questo è se sto usando DI (e sto usando Unity per il framework IoC), quindi devo definire più istanze nel mio costruttore per ottenere tutte le interfacce del repository di cui ho bisogno per lavorare in questo modo:

public MyClass(IRepository<Person> personRepository, 
       IRepository<Orders> ordersRepository, 
       IRepository<Items> itemsRepository, 
       IRepository<Locations> locationsRepository) 
{ 
    _personRepository = personRepository; 
    _OrdersRepository = ordersRepository; 
    _itemsRepository = itemsRepository; 
    _locationsRepository = locationsRepository; 
} 

Domande:

  1. È questo il posto?
  2. Se non dovessi perdere questo concetto?
  3. Anche se questo è corretto, qual è il punto di Unity per registrare l'interfaccia in concreto? L'ho già fatto perché il repository generico mi ha costretto alla dichiarazione.

Si prega di chiarire questo per me, e apprezzo tutto il vostro aiuto!

risposta

2

È OK?

Sicuro. C'è una preferenza personale sul fatto di utilizzare l'iniezione del costruttore come hai fatto tu o l'iniezione di proprietà. L'iniezione del costruttore è più pulita poiché non devi avere molti parametri per il tuo costruttore, ma è anche più sicuro.

qual è il punto di unità per registrare interfaccia di tipo concreto

Uno dei motivi è così che si può unit test MyClass senza dover utilizzare il repository reale che colpisce un database. È possibile "falsificare" il repository per restituire valori hard-coded su cui eseguire il test.

+0

Quello che vorrei poter fare è avere 'IRepository repository' come il parametro del metodo, ma so che non funzionerà. Puoi spiegare perché non lo farà? Sembra sbagliato passare tutti quelli dentro, ma forse è corretto e sto pensando troppo. – atconway

+1

Poiché ciascun repository è di un tipo diverso e MyClass (apparentemente) richiede 4 diversi repository. Devi fornire le lezioni concrete separatamente. Non è sbagliato (o raro) avere diverse dipendenze iniettate come fai tu. –

+0

Ma con quale situazione avrei bisogno di un metodo aggiuntivo? – krypru

2

Si potrebbe considerare l'utilizzo di un Aggregate Service che mette insieme diversi altri servizi insieme, ma si dovrebbe anche guardare con attenzione per vedere se il MyClass è trying to do too much; avere un numero molto elevato di dipendenze può essere un'indicazione di ciò.

+1

Il servizio aggregato in questo caso speciale è denominato [Unità di lavoro] (http://martinfowler.com/eaaCatalog/unitOfWork.html). – Steven

2

Questo sembra ok tranne un punto mancante; è necessario il pattern UnitOfWork per abilitare le transazioni. Senza il pattern UnitOfWork applicato, tutti i repository tentano di eseguire il commit delle operazioni db in diversi contesti.

+0

Niente nella sua domanda indica verso di lui che richiede UoW. Come ha sottolineato qualcun altro, forse la sua classe sta facendo troppo, ma non ha mai detto nulla sul tentativo di commettere modifiche su tutti i repository contemporaneamente. Inoltre, tecnicamente, diversi repository non usano necessariamente un contesto diverso. – Tipx

9

Come notato da D Stanley, una dipendenza deve essere un'interfaccia concreta. Altrimenti, dove dichiarerai T? La tua classe dipendente potrebbe essere generica, ma devi ancora dire "T è una persona" ad un certo punto.

Detto questo, Unity gestisce la registrazione di tipi generici piuttosto bene.

Diciamo che si implementa IRepository<T> con una classe generica Repository<T> che avvolge una DbSet<T> (o qualsiasi altra cosa).

Il seguente iscrizione e risolve potranno poi lavorare (che comprende l'iniezione in alcun costruttore):

container.RegisterType(typeof(IRepository<>), typeof(Repository<>)); 

// no specific registration needed for the specific type resolves 
container.Resolve(<IRepository<Person>); 
container.Resolve(<IRepository<Order>); 

Se avete bisogno di una sostituzione specifica di un tipo (dice il repository Elementi è speciale per qualsiasi ragione quindi ha un pienamente attuato ItemRepository classe), basta registrarsi che specifica implementazione dopo il generico uno:

container.RegisterType<IRepository<Item>, ItemRepository>(); 

Risoluzione IRespository<Item> sarà ora ottenere la vostra specifica implementazione.

Per la cronaca, penso che questo può essere fatto solo nel codice e non nei file di configurazione. Qualcuno si sente libero di correggere quell'ipotesi.

+1

È possibile registrare le classi generiche nei file di configurazione di Unity. L'ho fatto. '' è un esempio. Amami un po 'di unità. –

0

Avevo bisogno di fare questo utilizzando il file di configurazione. In realtà è piuttosto facile, ma mi ci è voluto un po 'per capirlo.

In questo esempio abbiamo un'interfaccia IRepository<T> ed ha 2 implementazioni:

  1. OneRepository
  2. TwoRepository

Abbiamo poi una classe denominata Worker che dipende IRepository<One> e IRepository<Two>. Chiediamo l'unità per creare un'istanza di Worker per noi e capire le dipendenze dal file di configurazione.

interfaccia e implementazione

Tutti questi sono nello spazio dei nomi ConsoleApplication1 in questo esempio.

public class Worker 
{ 
    private readonly IRepository<One> one; 
    private readonly IRepository<Two> two; 

    public Worker(IRepository<One> one, IRepository<Two> two) 
    { 
     this.one = one; 
     this.two = two; 
    } 

    public string DoOne() 
    { 
     return this.one.Add(new One()); 
    } 

    public string DoTwo() 
    { 
     return this.two.Add(new Two()); 
    } 
} 

public interface IRepository<T> 
{ 
    string Add(T t); 
} 

public class OneRepository : IRepository<One> 
{ 
    public string Add(One t) 
    { 
     return "One"; 
    } 
} 

public class TwoRepository : IRepository<Two> 
{ 
    public string Add(Two t) 
    { 
     return "Two"; 
    } 
} 

public class One { } 
public class Two { } 

configurazione di Unity

Si prega di notare insegniamo unità e diciamo che il nome del gruppo. Quindi registriamo le 2 implementazioni.

<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
    <configSections> 
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" /> 
    </configSections> 
    <unity xmlns="http://schemas.microsoft.com/practices/2010/unity"> 
    <assembly name="ConsoleApplication1" /> 
    <container> 
     <register type="ConsoleApplication1.IRepository[[ConsoleApplication1.One]]" mapTo="ConsoleApplication1.OneRepository" /> 
     <register type="ConsoleApplication1.IRepository[[ConsoleApplication1.Two]]" mapTo="ConsoleApplication1.TwoRepository" /> 
    </container> 
    </unity> 
</configuration> 

Applicazione

Questa è la Composition Root.

public class Program 
{ 
    static void Main() 
    { 
     UnityContainer container = new UnityContainer(); 
     var res = container.LoadConfiguration(); 
     Worker worker = res.Resolve<Worker>(); 
     Console.WriteLine(worker.DoOne()); 
     Console.WriteLine(worker.DoTwo()); 
     Console.Read(); 
    } 
} 

L'uscita come previsto è:

One 
Two 
Problemi correlati