Home > Mobile >  .NET 6 set Newtonsoft.Json default options with Autofac
.NET 6 set Newtonsoft.Json default options with Autofac

Time:04-14

I am currently refatoring my .NET 6 WebAPI. While refactoring i switched to use Autofac as my DI-Framework.

To let my controllers not return null values I use newtonsoft json with the follwing options:

builder.Services.AddControllers().AddDefaultJsonOptions();
public static IMvcBuilder AddDefaultJsonOptions(this IMvcBuilder builder)
{
    return builder.AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
        options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
    });
}

With autofac I changed the registration of my controllers to:

builder.Host.ConfigureContainer<Autofac.ContainerBuilder>(builder =>
{
    ...

    var executingAssembly = Assembly.GetExecutingAssembly();
    builder.RegisterApiControllers(executingAssembly);

    ...
});

But how can I change the default json serialization options now?

CodePudding user response:

The awesomeness of ASP.NET Core being totally open source is you can hit the repo yourself and do some spelunking to figure out what these methods do and then track down how to solve issues like this.

In this case, let's see what AddControllers actually does:

public static IMvcBuilder AddControllers(this IServiceCollection services)
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }

    var builder = AddControllersCore(services);
    return new MvcBuilder(builder.Services, builder.PartManager);
}

Looks like there's a lot hidden in AddControllersCore, so we can scroll down to that:

private static IMvcCoreBuilder AddControllersCore(IServiceCollection services)
{
    // This method excludes all of the view-related services by default.
    var builder = services
        .AddMvcCore()
        .AddApiExplorer()
        .AddAuthorization()
        .AddCors()
        .AddDataAnnotations()
        .AddFormatterMappings();

    if (MetadataUpdater.IsSupported)
    {
        services.TryAddEnumerable(
            ServiceDescriptor.Singleton<IActionDescriptorChangeProvider, HotReloadService>());
    }

    return builder;
}

Interesting. So AddControllers doesn't actually add the literal controllers to the set of services - it adds the things needed to execute controllers. Why is that?

I'll fast-forward a bit - it's because controllers aren't resolved as services by default. Surprise! What actually happens is that the controller parameters are resolved, but the controller itself gets created via reflection, with the resolved parameters passed to the constructor. If you actually want controllers to be resolved (like, to support property injection or something), you need to use AddControllersAsServices (This is also in the Autofac documentation.)

In fact, that RegisterApiControllers method you're using isn't even for ASP.NET Core - it's for ASP.NET Web API, which isn't in .NET 6. You really need to be checking out ASP.NET Core integration and definitely not using any of the classic ASP.NET and Web API stuff in your refactoring. It's not mix-and-match. It's all .NET Core / ASP.NET Core.

So, to answer the question, how do you both register your controllers and get your JSON options?

Keep doing what you were doing.

builder.Services
  .AddControllers()
  .AddControllersAsServices()
  .AddDefaultJsonOptions();

If you really do want to manually register controllers with Autofac, you could re-register them (after you call AddControllersAsServices) but you'll register them as themselves.

builder.Host
  .UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Services
  .AddControllers()
  .AddControllersAsServices()
  .AddDefaultJsonOptions();
builder.Host
  .ConfigureContainer<ContainerBuilder>(b => {
    b.RegisterType<MyController>().PropertiesAutowired();
  });

But, again, unless you're specifically doing something to override how the controller is registered, like needing to set up a lambda or property injection or something, AddControllersAsServices will cover it.

You do need the UseServiceProviderFactory call if you want Autofac as your container. Otherwise you get the default Microsoft container, even if you call ConfigureContainer<T>.

  • Related