Uso di ninject in una libreria di classi

1

In cerca di aiuto per orientarmi su ninject e DI.

Usando i semplici esempi che ho trovato online tutto funziona bene ma provare a fare qualcosa di più complesso sta causando mal di testa.

Ho una libreria di classi che riceve i file, li analizza e memorizza i risultati in un database. Questa libreria può essere chiamata da varie parti di un'azienda; servizio Windows, sito Web, applicazione Windows, ecc.

Vedi questo codice di esempio:

class FileProcessor : IFileProcessor
{
    public string Process(string fileName)
    {
        using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
        {
            BinaryReader binaryReader = new BinaryReader(fileStream);
            string hexString = BinaryFunctions.BytesToHexString(binaryReader.ReadBytes(2));
            string result = "";

            if(hexString == "123")
            {
                // I would like to inject the required BLLs into this constructor
                FileType1Processor p = new FileType1Processor(fileStream);
                result = p.Process();
            }
            else if(hexString == "456")
            {
                // I would like to inject the required BLLs into this constructor
                FileType2Processor p = new FileType2Processor(fileStream);
                result = p.Process();
            }

            return result;
        }
    }
}

Sono bloccato su come ottenere le dipendenze dai processori FileType indipendentemente dal tipo di applicazione in cui viene utilizzato.

Resto vivo il kernel in tutte le applicazioni e lo lancio in biblioteca?
Devo impostare un localizzatore di servizi e usarlo?
Creo un nuovo kernel nella radice della libreria?
Sto sbagliando tutto questo?

Qualsiasi aiuto sarebbe molto apprezzato.

    
posta Flick 16.05.2016 - 14:16
fonte

1 risposta

1

Stai facendo parecchio in una singola classe, detto questo vediamo cosa possiamo fare.

Prima di tutto, modifica il codice in modo da poter supportare meglio OCP . Lo faremo introducendo un dizionario che conterrà i processori e un'interfaccia correlata IFileTypeProcessor .

interface IFileTypeProcessor
{
    // not sure what string you intended to return
    string Process(FileStream fileStream);
}

class FileProcessor : IFileProcessor
{
    private readonly IDictionary<string, IFileTypeProcessor> processors;
    public FileProcessor(IDictionary<string, IFileTypeProcessor> processors)
    {
            this.processors = processors
    }

    public string Process(string fileName)
    {
        using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
        {
            BinaryReader binaryReader = new BinaryReader(fileStream);
            string hexString = BinaryFunctions.BytesToHexString(binaryReader.ReadBytes(2));
            string result = "";

            if(!this.processors.ContainsKey(hexString)) 
            {
                throw new YourApplicationSpecificException();
            }
            return this.processors[hexString].Process(fileStream);
        }
    }
}

Quindi introduciamo una nuova classe chiamata FirstTwoByteHexKeyProvider che implementa una nuova interfaccia IKeyREader che diventerà quindi una dipendenza aggiuntiva dal CTOR. Includeremo anche alcune clausole di salvaguardia per aiutare i nostri utenti perché ci piacciono.

interface IKeyReader
{
    string GetKeyAsHexString(FileStream filestream)
}

class FirstTwoByteHexKeyProvider : IKeyReader
{
    public string GetKeyAsHexString(FileStream fileStream)
    {
        if(fileStream)
        { 
            throw new ArgumentNullException(nameof(fileStream));
        }

        BinaryReader binaryReader = new BinaryReader(fileStream);
        return BinaryFunctions.BytesToHexString(binaryReader.ReadBytes(2));
    }
}

class FileProcessor : IFileProcessor
    {
        private readonly IDictionary<string, IFileTypeProcessor> processors;
        private readonly IKeyReader keyReader;
        public FileProcessor(IDictionary<string, IFileTypeProcessor> processors, IKeyReader keyReader)
        {
                if(processors == null)
                { 
                    throw new ArgumentNullException(nameof(fileStream));
                }

                if(keyReader == null)
                { 
                    throw new ArgumentNullException(nameof(keyReader));
                }

                this.processors = processors
                this.keyReader = keyReader
        }

        public string Process(string fileName)
        {
            using (FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                string hexString = this.keyReader.GetKeyAsHexString(fileStream);

                if(!this.processors.ContainsKey(hexString)) 
                {
                    throw new YourApplicationSpecificException();
                }
                return this.processors[hexString].Process(fileStream);
            }
        }
    }

Ora alle dipendenze, devi supportare solo Ninject? se così fosse potrebbe essere semplice come usare NinjectModules:

public class FileProcessorNinjectModule : NinjectModule
{
    public override void Load() 
    {
        Bind<IFileProcessor>().To<FileProcessor>();
        Bind<IKeyReader>().To<FirstTwoByteHexKeyProvider>();
        Bind<IDictionary<string, IFileTypeProcessor>>().ToMethod(context => new Dictionary<string,IFileTypeProcessor>
        {
            ["123"] = new FileType1Processor(),
            ["456"] = new FileType2Processor(),
        });
    }
}

Una volta creato il modulo, consenti ai tuoi clienti di utilizzare qualche forma di Caricamento del modulo dinamico e Bob è tuo zio.

L'ho scritto velocemente qui e non l'ho testato tramite il compilatore, ma sono sicuro che funzionerà con un po 'di correzione da parte tua, ma dovrebbe essere sufficiente per farti andare.

Se devi supportare più di un modulo di DI allora ti consiglio di esporre un'interfaccia che consenta al cliente di fornire il proprio contenitore DI e il tuo pacchetto lo userà per risolvere le dipendenze allo stesso modo in cui ASP.NET Core lo fa oggi.

Infine potresti considerare di estrarre l'azione di leggere effettivamente il file in un'altra classe e di rendere il risultato binario di questo "file letto" un argomento che viene passato al tuo metodo di processo anziché al fileName dato che così com'è essere un po 'un dolore da testare.

Se non vuoi farlo, puoi perlomeno estrarre il "nuovo FileStream (..." in una funzione virtuale interna che ti darà la possibilità di MOQ attorno a questa chiamata di sistema durante il test dell'unità.

Spero che aiuti ...

    
risposta data 17.05.2016 - 23:20
fonte

Leggi altre domande sui tag