3
votes

Résoudre les Microsoft.Extensions.Logging.ILogger génériques avec Unity - obtenir InvalidCastException

J'essaye d'enregistrer un ILogger générique (de Microsoft.Extensions.Logging, pas de Serilog) dans Unity (version 4).

J'ai la classe suivante:

// Arrange
var serviceCollection = new Microsoft.Extensions.DependencyInjection.ServiceCollection()
    .AddLogging(builder => builder.AddSerilog())
    .AddTransient<MyClass, MyClass>();

var serviceProvider = serviceCollection.BuildServiceProvider();

// Assert
serviceProvider.GetService<MyClass>();


9 commentaires

container.RegisterType (); ne devrait pas être quelque chose comme container.RegisterType () ;? Les injections de dépendances sont normalement effectuées en demandant une interface, pas une classe.


container.RegisterType () est pour la démo, le problème est la résolution de ILogger .


L'avez-vous essayé avec une interface enregistrée dans une classe ? La résolution pose peut-être un problème, car elle n'a pas d'interface.


Essayez à la place private readonly Microsoft.Extensions.Logging.ILogger _logger; et public MyClass (Microsoft.Extensions.Logging.ILogger logger) . Vous créez un logger non spécialisé dans votre configuration d'injection, donc cela ne sera bien sûr pas assignable à un logger spécialisé.


@kara oui, le même problème est là avec une interface (IMyClass par exemple)


@ErikFunkenbusch le but est ici de créer le Logger générique. Non générique fonctionne (mais nécessite un autre enregistrement). Veuillez noter que les enregistreurs génériques sont conseillés par MS. Voir docs.microsoft.com/en-us/aspnet/core/ principes de base / journalisation (notez que ce cas n'est pas ASP.NET Core)


CreateLogger ne crée pas de logger générique cependant ...


@ErikFunkenbusch vrai, mais la chose amusante est que cela fonctionne un peu avec ASP.NET Core (en utilisant loggingProvider et loggingFactory). Voir usage / source github.com/serilog/serilog-extensions-logging . Il existe des ILoggers génériques résolus avec le même SerilogLoggerProvider


Non, pas pareil. Ils créent un fournisseur de serilog en utilisant le type de logger, vous créez un fournisseur de logger sans type.


3 Réponses :


2
votes

J'ai résolu ce problème à l'aide de MakeGenericMethod et d'une méthode d'assistance:

// Arrange
IUnityContainer container = new UnityContainer();

var logfactory = new LoggerFactory();

var provider = new Serilog.Extensions.Logging.SerilogLoggerProvider();
logfactory.AddProvider(provider);

container.RegisterType(typeof(Microsoft.Extensions.Logging.ILogger<>),
    new InjectionFactory((ctr, type, name) =>
    {
        var loggerType = type.GetGenericArguments()[0];

        var myType = this.GetType();

        var createLoggerMethod1 = myType.GetMethod(nameof(CreateLogger),BindingFlags.Static | BindingFlags.NonPublic);

        var createLoggerMethod = createLoggerMethod1.MakeGenericMethod(loggerType);

        var logger = createLoggerMethod.Invoke(this, new object[] {logfactory});

        return logger;
    }));

container.RegisterType<MyClass, MyClass>();

// Assert
container.Resolve<MyClass>();

Et le code final (sans mise en cache de réflexion, etc.):

XXX


0 commentaires

3
votes

Le moyen le plus simple que j'ai trouvé est d'enregistrer une fabrique et de refléter sa méthode d'extension pour le faire et obtenir le bon type de réponse. Cet exemple suppose également que vous avez enregistré une ILoggerFactory.

_container = new UnityContainer();
_container.RegisterInstance<ILoggerFactory>(new LoggerFactory().AddLog4Net(), new ContainerControlledLifetimeManager());
_container.RegisterFactory(typeof(ILogger<>), null, (c, t, n) =>
        {
            var factory = c.Resolve<ILoggerFactory>();
            var genericType = t.GetGenericArguments().First();
            var mi = typeof(Microsoft.Extensions.Logging.LoggerFactoryExtensions).GetMethods().Single(m => m.Name == "CreateLogger" && m.IsGenericMethodDefinition);
            var gi = mi.MakeGenericMethod(t.GetGenericArguments().First());
            return gi.Invoke(null, new[] { factory });
        });


0 commentaires

1
votes

Comme il existe déjà une implémentation de Logger qui prend une ILoggerFactory comme dépendance, vous pouvez simplement faire

container.RegisterType<ILoggerFactory, MyLoggerFactory>();
container.RegisterType(typeof(ILogger<>), typeof(Logger<>));
container.Resolve<ILogger<MyClass>>();


0 commentaires