mercredi 7 juillet 2021

Register ASP.NET Core JsonConverter implementations using assembly scan

I have lots of json converters, which derive from JsonConverter<T>. They are in various assemblies, not just the executing assembly.

I register them like this:

services
  .AddControllers()
  .AddJsonOptions(o => {
    o.JsonSerializerOptions.Converters.Add(new FooJsonConverter());
    o.JsonSerializerOptions.Converters.Add(new BarJsonConverter());
    o.JsonSerializerOptions.Converters.Add(new BazJsonConverter());
    o.JsonSerializerOptions.Converters.Add(new QuxJsonConverter());
    // lots more...
  });

I'd rather add them dynamically, using an assembly scan - the same way AutoMapper, FluentValidation, Autofac, etc., do it. I looked at their repos for the secret sauce but couldn't find it.

So I tried some basic reflection:

services
  .AddControllers()
  .AddJsonOptions(o => {

    var jsonConverters =
      AppDomain.CurrentDomain
      .GetAssemblies()
      .SelectMany(x => x.ExportedTypes)
      .Where(x => x.BaseType != null && x.IsAssignableFrom(typeof(JsonConverter<>)));

    foreach (var jsonConverter in jsonConverters)
      o.JsonSerializerOptions.Converters.Add(Activator.CreateInstance(jsonConverter) as JsonConverter);

  });

But that fails to start, and throws:

FTL | Microsoft.AspNetCore.Hosting.Diagnostics | Application startup exception
Autofac.Core.DependencyResolutionException: An exception was thrown while activating Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionEndpointDataSourceFactory -> Microsoft.AspNetCore .Mvc.Infrastructure.DefaultActionDescriptorCollectionProvider -> λ:Microsoft.AspNetCore.Mvc.Abstractions.IActionDescriptorProvider[] -> Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerA ctionDescriptorProvider -> Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModelFactory -> λ:Microsoft.AspNetCore.Mvc.ApplicationModels.IApplicationModelProvider[] -> Microsoft.AspNetC ore.Mvc.ApplicationModels.DefaultApplicationModelProvider -> Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider -> λ:Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.I CompositeMetadataDetailsProvider.
---> System.MissingMethodException: Cannot create an abstract class.
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean wrapExceptions, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& hasNoDefaultCtor )

Note the Cannot create an abstract class. The base class is abstract, but I'm trying to activate the subclasses.

What is the correct way to do this? Also, is it appropriate to refer to the AppDomain, as I remember reading somewhere that that behavior has changed in aspnet.





Aucun commentaire:

Enregistrer un commentaire