Inietti il tipo come iniezione di dipendenza, quando sono richieste nuove istanze per ogni chiamata di metodo - Come evitare?

0

Ho una classe

public class CertificadoHandler : ICertHandler
{
    Type typeAfirmaValidate;

    public CertificadoHandler(){
        typeAfirmaValidate = typeof(AfirmaValidateCertificate);
    }

    //testing
    public void SetTypeAfirmaValidate(Type t)
    {
        typeAfirmaValidate = t;
    }

    public CredentialsData GetCredentialsData(X509Certificate cert)
    {
        return ((IValidateCertificate)Activator.CreateInstance(typeAfirmaValidate)).GetCredentialsData(cert);
    }
}

Il motivo di questo codice è che voglio dire a CertificadoHanlder quale classe deve istanziare molto tempo in cui GetCredentialsData viene chiamato. Come posso riprogettare questo meglio. Non mi piace il fatto che sto iniettando un tipo generico (non un'interfaccia) ma se inserisco un'interfaccia non sarò in grado di istanziarla. Come posso procedere?

Punti chiave

  1. Certificadohandler è istanziato all'interno di un singleton, quindi voglio che utilizzi nuove istanze di certificato Ivalidate per ogni chiamata al metodo. (L'app ha una concorrenza così voglio evitare problemi con la condivisione della stessa variabile tra i thread.

    1. Factory come parametro sembra essere un design migliore

3 Voglio testare l'unità e simulare il certificato di IValidate

    
posta Badulake 06.10.2017 - 10:43
fonte

3 risposte

1

Alcune opzioni. Ognuno ha punti di forza e punti deboli.

Inietti un'istanza transitoria

Se si sta utilizzando un contenitore IoC, è possibile registrare una dipendenza come transitoria. Ad esempio, in Ninject puoi usare registerTransient . Se stai utilizzando Unity, puoi utilizzare TransienLifetimeManager . Quindi iniettati nella tua classe per normale.

public void CompositionRoot(Container container)
{
    container.Register<IValidateCertificate, CertificateValidator>(new TransientLifetimeManager());
    container.RegisterType<ICerthandler, CertificadoHandler>(new ContainerControlledLifetimeManager());
}

public class CertificadoHandler : ICertHandler
{
    private readonly IValidateCertificate _validateCertificate;

    public CertificadoHandler(IValidateCertificate validateCertificate){
        _validateCertificate = validateCertificate;
    }

    public CredentialsData GetCredentialsData(X509Certificate cert)
    {
        return _validateCertificate.GetCredentialsData(cert);
    }
}

Inietta una fabbrica

Potresti iniettare una fabbrica singleton, che permetterà alla classe di istanziare tutto ciò di cui ha bisogno, ma permetterà comunque a un test unitario di sovrascrivere l'iniezione. Il vantaggio dell'utilizzo di una factory (invece di un'istanza transitoria) è che la classe può controllare quando avviene l'istanziazione e può persino fornire gli argomenti del costruttore se necessario.

A seconda del tuo framework IoC, potresti essere in grado di evitare di scrivere la factory e utilizzare invece un factory automatico .

public class ValidateCertificateFactory : IValidateCertificateFactory
{
    public IValidateCertificate GetInstance(string constructorArgument)
    {
        return new ValidateCertificate(constructorArgument);
    }
}

public void CompositionRoot(Container container)
{
    container.RegisterType<IValidateCertificateFactory, ValidateCertificateFactory>(new ContainerControlledLifetimeManager());
    container.RegisterType<ICerthandler, CertificadoHandler>(new ContainerControllerLifetimeManager());
}

public class CertificadoHandler : ICertHandler
{
    private readonly IValidateCertificateFactory _validateCertificateFactory;

    public CertificadoHandler(IValidateCertificateFactory validateCertificateFactory){
        _validateCertificateFactory = validateCertificateFactory;
    }

    public CredentialsData GetCredentialsData(X509Certificate cert)
    {
        return _validateCertificateFactory.Resolve("Foo").GetCredentialsData(cert);
    }
}

Inietti un delegato

Questo è un po 'come usare una fabbrica ma evita di dover scrivere una classe factory.

public void CompositionRoot(Container container)
{
    container.RegisterType<Func<string, IValidateCertificate>>((arg) => new ValidateCertificate(arg));
    container.RegisterType<ICerthandler, CertificadoHandler>();
}

public class CertificadoHandler : ICertHandler
{
    private readonly Func<string,IValidateCertificate> _createValidator;

    public CertificadoHandler(Func<string,IValidateCertificate> func){
        _createValidator= func;
    }

    public CredentialsData GetCredentialsData(X509Certificate cert)
    {
        return _createValidator("Foo").GetCredentialsData(cert);
    }
}
    
risposta data 07.10.2017 - 00:47
fonte
0

Ti piace questo?

    Func<IValidateCertificate> crearAfirmaValidate;

    public CertificadoHandler(){
        crearAfirmaValidate = () => new AfirmaValidateCertificate();
    }
    //testing
    public void SetTypeAfirmaValidate(Func<IValidateCertificate> factoria )
    {
        crearAfirmaValidate = factoria;
    }
    
risposta data 06.10.2017 - 11:17
fonte
0

Il tuo problema è che stai usando un'interfaccia e un'istanziazione di qualcosa di concreto, anche se stai usando un oggetto System.Type . Non hai idea di come sei arrivato a questo progetto, ma hai bisogno di un'integrazione delle dipendenze del costruttore qui:

public class CertificadoHandler : ICertHandler
{
    private IValidateCertificate validator;

    public CertificadoHandler(IValidateCertificate validator)
    {
        this.validator = validator ?? throw new ArgumentNullException(nameof(validator));
    }

    public CredentialsData GetCredentialsData(X509Certificate cert)
    {
        return validator.GetCredentialsData(cert);
    }
}

Se hai bisogno di un'istanza diversa ogni volta, hai bisogno di un'istanza diversa di CertificadoHandler ogni volta oppure passa l'oggetto IValidateCertificate come argomento. Data la logica molto semplice, mi fa dubitare della necessità di questa classe.

Se è effettivamente necessario convalidare un certificato su un numero di validatori, quindi iniettare una raccolta di essi, quindi eseguire il loop su di essi fino a ottenere una convalida adeguata per il certificato dato:

public class CertificadoHandler : ICertHandler
{
    private IEnumerable<IValidateCertificate> validators;

    public CertificadoHandler(IEnumerable<IValidateCertificate> validators)
    {
        this.validators = validators ?? throw new ArgumentNullException(nameof(validators));
    }

    public CredentialsData GetCredentialsData(X509Certificate cert)
    {
        foreach (var validator in validators)
        {
            var certValidation = validator.GetCredentialsData(cert);

            // Assuming GetCredentialsData returns null instead of throwing an exception
            // when the cert cannot be validated
            if (certValidation != null)
                return certValidation;
        }

        return null;
    }
}

E dal momento che hai a che fare con un'interfaccia, puoi facilmente prendere in giro questi oggetti durante i test unitari.

    
risposta data 06.10.2017 - 23:48
fonte