5

Ho un repository generico che voglio registrare per DI, implementa un'interfaccia IRository.. Iniezione delle dipendenze net-core

Normalmente avrei creare un'istanza di esso in questo modo:

IRepository repo = new Repository<Order>(); 

Comunque sto cercando di arrivare fino a velocità in .net 5 prima del rilascio e voglio ottenere questo lavoro con DI, ho fatto ricorso al seguente:

services.AddTransient<DAL.IRepository<Models.Order>, DAL.Repository<Models.Order>>(); 

Ma questo si sente male, non voglio 50+ linee in là uno per ciascuna delle classi nel mio modello ...

non riesco a trovare qualsiasi cosa online su t lui, so che è possibile con altri contenitori di ioc .. ma dato che questo è un progetto di apprendimento, non voglio usare un altro contenitore, sto cercando di fare tutto con il contenitore nativo di .net5s.

risposta

4

Dopo un po 'indietro e in avanti in i commenti ad altre risposte Ho una soluzione funzionante, potrebbe non essere il modo migliore ma funziona. Aggiornamento malato di nuovo se trovo un modo migliore per implementare questo.

I due problemi che ho avuto sono stati: necessarie per registrare un'interfaccia generica, il problema qui è stato un calo di concentrazione da parte mia .. Ho avuto la sintassi sbagliata per la registrazione di un tipo generico che naturalmente è:

services.AddTransient(typeof(IRepository<>), typeof(Repository<>)); 

Il secondo problema era che ho un assembly che contiene oltre 50 diversi modelli che volevo registrare, Il modo in cui l'ho indirizzato è stato scrivere un metodo per passare un elenco di assembly insieme al namespace che voglio registrarsi e itera su qualsiasi tipo che soddisfa i criteri e li registra nel contenitore DI.

public void RegisterModels(IServiceCollection services, string[] Assemblies, string @NameSpace) 
    { 
     foreach (var a in Assemblies) 
     { 
      Assembly loadedAss = Assembly.Load(a); 

      var q = from t in loadedAss.GetTypes() 
        where t.IsClass && !t.Name.Contains("<") && t.Namespace.EndsWith(@NameSpace) 
        select t; 

      foreach (var t in q.ToList()) 
      { 
       Type.GetType(t.Name); 
       services.AddTransient(Type.GetType(t.FullName), Type.GetType(t.FullName)); 
      } 
     } 
    } 

Questa è poi chiamata dalle ConfigureServices metodo startup.cs:

public void ConfigureServices(IServiceCollection services) 
    { 
     // Add framework services. 
     services.AddEntityFramework() 
      .AddSqlServer() 
      .AddDbContext<TestContext>(options => 
       options.UseSqlServer(@"Server=LOCALHOST\SQLEXPRESS;Database=Test;Trusted_Connection=True;")); 

     services.AddMvc(); 

     RegisterModels(services, new string[] { "UI" }, "UI.Models"); 

     services.AddTransient(typeof(IRepository<>), typeof(Repository<>)); 
    } 

Ci può essere un modo migliore per fare questo, v'è sicuramente utilizzando diversi contenitori DI, se qualcuno ha miglioramenti offerta Per favore mi faccia sapere.

+0

A meno che non manchi qualcosa. Perché hai bisogno di registrare i tuoi modellini di vista usando DI? – testydonkey

+0

Hai perfettamente ragione, il problema originale era che non ero in grado di registrare il repository generico senza specificare un tipo, il che mi ha portato giù un po 'di un percorso in cui è stato suggerito che questo non è possibile nel .net 5 DI container - così ho iniziato a cercare modi per minimizzare il lavoro per registrare tutti i diversi tipi di repository generico di cui avrei bisogno ... Comunque sei corretto, il metodo RegisterModels non è necessario - Ive lo ha lasciato in quanto potrebbe essere utile ad altri che cercano di registrare un numero di classi all'interno di un namespace, Ill edit per renderlo più chiaro. – D3vy

+0

più 1 per 'loadedAss' –

0

Quello che puoi fare è creare un metodo di estensione per incapsulare tutti i singoli elementi che devono essere registrati.

Questa è la stessa tecnica Microsoft sta usando, ad esempio, si mette solo questo in avvio:

services.AddMvc(); 

ma che è un metodo di estensione e dietro le quinte potete scommettere che sta registrando un mucchio di roba che esigenze.

in modo da poter creare il proprio metodo di estensione in questo modo:

using Microsoft.Extensions.DependencyInjection; 
public static IServiceCollection AddMyFoo(this IServiceCollection services) 
{ 
    services.AddTransient<DAL.IRepository<Models.Order>, DAL.Repository<Models.Order>>(); 
    //.... 

    return services; 
} 

e rendendo il metodo restituisce l'IServiceCollection si rendono fluente in modo da poter fare

services.AddMyFoo().AddSomeOtherFoo(); 

aggiornato in base al commento

l'altra tecnica per ridurre le registrazioni è quando la dipendenza non ha di per sé dipendenze è possibile fare in modo che il costruttore abbia un valore di default null hai ancora il disaccoppiamento e potresti passare un altro in seguito, ma il DI non genera un errore e puoi semplicemente creare un'istanza di ciò che ti serve se non viene passato.

public class MyFoo(IFooItemDependency myItem = null) 
{ 
    private IFooItemDependency internalItem; 

    public MyFoo(IFooItemDependency myItem = null) 
    { 
     internalItem = myItem ?? new FooItemItem(); 
    } 
} 
+0

Questo mi richiede ancora di registrare 50 + repository, il suo solo spostando il codice a farlo da qualche altra parte ... Se ho intenzione di farlo, allora potrei anche sbarazzarmi del repository generico. Per chiarire, voglio avere (se possibile) una riga per registrare il repository, indipendentemente dal tipo di implementazione concreta. – D3vy

+0

ho appena aggiornato la mia risposta con qualcosa che può essere d'aiuto, ma alla fine non si può passare nulla a meno che non sia registrato da qualche parte, non c'è auto discovery per riflessione. Si noti che non è necessario utilizzare il DI integrato, quindi se qualche altro DI compatibile supporta la registrazione automatica, è possibile utilizzarlo, ma questa non è una funzionalità del DI –

+0

integrato. È quello che stavo pensando .. Grazie per la conferma. Per quanto non volessi usare nient'altro, userò un altro container IOC. – D3vy

0

Io non sono sicuro al 100% su quello che la tua domanda è presumo che non vuole avere

services.AddTransient<DAL.IRepository<Models.Order>, DAL.Repository<Models.Order>>(); 
services.AddTransient<DAL.IRepository<Models.Person>, DAL.Repository<Models.Person>>(); 
services.AddTransient<DAL.IRepository<Models.Invoice>, DAL.Repository<Models.Invoice>>(); 

ecc

ho fatto prima (con Ninject)

Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InRequestScope(); 

immagino per l'Unità che si può fare qualcosa di simile come

services.AddTransient<DAL.IRepository<>, typeof(Repository<>)(); 

e quindi di utilizzare in un servizio

public OrderService(IRepository<Models.Order> orderRepository) 
{ 
    this.orderRepository = orderRepository; 
} 

EDIT

Come sottolineato dalla OP la sintassi corretta è:

services.AddTransient(typeof(IRepository<>), typeof(Repository<>)); 
+0

purtroppo 'services.AddTransient , typeof (Repository <>)();' non è un codice valido e la domanda non riguarda Unità. – qujck

+0

Il mio brutto proposito di menzionare Unity, ma non è comunque il codice di unità. Ci sono errori? Runtime? Costruisci il tempo? – testydonkey

+0

Non l'ho digitato in VS ma posso vedere 3 problemi da qui che non riusciranno a compilare: 1. ci sono più '<''s than '>' s. 2. non puoi mettere 'typeof (Repository <>)' tra '<' and '>' e 3. non puoi inserire un tipo generico aperto come 'DAL.IRepository <>' tra '<' and '>' (avresti bisogno di qualcosa come ' AddTransient (typeof (DAL.IRepository <>), typeof (Repository <>) ') – qujck

10

Si dovrebbe essere in grado di registrare il generico aperto con

services.AddTransient(typeof(IRepository<>), typeof(Repository<>)); 
1

Si potrebbe utilizzare una libreria di registrazione convenzione basata come Scrutor.

Scrutor è una piccola libreria open source che fornisce un'API fluente per registrare i servizi nel contenitore Microsoft.Extensions.DependencyInjection basato su convenzioni (simile al metodo RegisterAssemblyTypes di Autofac, al metodo Scan di StructureMap e al pacchetto Convenzioni di Ninject).

Questo vi permetterà di fare qualcosa di simile:

services.Scan(scan => scan 
      .FromAssemblies(<<TYPE>>.GetTypeInfo().Assembly) 
       .AddClasses(classes => classes.Where(x => { 
        var allInterfaces = x.GetInterfaces(); 
        return 
         allInterfaces.Any(y => y.GetTypeInfo().IsGenericType && y.GetTypeInfo().GetGenericTypeDefinition() == typeof(IRepository<>))); 
       })) 
       .AsSelf() 
       .WithTransientLifetime() 
     ); 
Problemi correlati