Using WebApiContrib.Core.Formatter.Csv in ASP.NET Core 3.0 Web API project - asp.net-core-3.0

I want to migrate the AddCsvSerializerFormatters configuration to .NET Core 3.0
Taken from the example code here
services.AddMvc(o =>
{
...
})
.AddCsvSerializerFormatters()
A .NET Core 3.0 web api project registers just the controllers, and registering all of Mvc seems overkill.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
...
}
References:
https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio#mvc-service-registration

The ServiceCollection.AddControllers() returns an IMvcBuilder type. Since this package adds an extension AddCsvSerializerFormatters() for IMvcBuilder, you can chain the method invocation by:
services.AddControllers().AddCsvSerializerFormatters();
See AddCsvSerializerFormatters():
public static IMvcBuilder AddCsvSerializerFormatters(this IMvcBuilder builder)

Related

ASP.NET Migration from WebHostBuilder (Pre-.NET 3.0) to IHostBuilder + ConfigureWebHostDefaults (Post-.NET 3.0) causes API returning 404 error

We have a project that started on .NET Core 2.2 and recently was migrated to .NET 6. In this project we used WebHostBuilder because we used combination of Rest API and Hosted Services and we decided to re-implement our hosts to new Generic hosts that were introduced in .NET Core 3.
But after reimplementation of our WebHost to Generic Host + WebHostDefaults method combo all of our API calls started to return 404 not found error message as if Controllers were not found/mapped correctly and my ideas of how to fix it ran out.
Our HostBuilder implementation:
var host = builder
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults((webBuilder) =>
webBuilder.UseContentRoot(AppDomain.CurrentDomain.BaseDirectory)
.UseWebHostConfiguration(ComponentName, commandLineArguments, out configuration)
.UseStartup(webHost => StartupClassFactory()))
.Build();
ConfigureServices implementation in Startup Class:
private void ConfigureServices(IServiceCollection services)
{
//Configure other non-API services
services
.AddMvc(options => options.EnableEndpointRouting = false)
.AddNewtonsoftJson(options => JsonSettings.SetSettings(options.SerializerSettings));
}
Configure implementation in Startup Class:
protected override void Configure(
IApplicationBuilder app,
ILoggerFactory loggerFactory,
IServiceProvider serviceProvider,
IHostApplicationLifetime applicationLifetime)
{
//Database Migration Stuff here
app.UseMiddleware<InquiryMetricLogHandlerMiddleware>();
app.UseMiddleware<ExceptionHandlerMiddleware>();
app.UseMiddleware<SeedingCheckMiddleware>();
if (QueryLocking)
{
QueryLockingMiddlewareApplicator(app);
}
app.UseMvc();
}
Controller snippet:
//Our Internal libraries
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace MyProject
{
[ApiController]
[Route(Routes.ApiRoute1 + "/{id:guid}/" + Routes.ApiRoute2)]
public class CustomController : SqliteInquiryController<MyModel>
{
public TokensController(
DI arg1,
DI arg2,
DI arg3)
: base(arg1, arg2, arg3)
{
//Constructor stuff
}
[Route("/" + Routes.GetAllApiRoute), HttpGet, PaginationValidation]
public override async Task<IActionResult> GetList()
{
//Build Database LINQ query
return Ok(response);
}
}
}
When debugging and trying /GetAllApiRoute API call I found out, that requests go through all Middlewares and stop on UseMvc where it throws just 404 Not Found response so it seems like problem with registering of Controller or routing request to Controller.
Methods ConfigurateServices and Configurate have the same implementation as they had on .NET 2.2 and everything worked fine. So I guess something must have changed in WebHost configuration along the way from .NET 2.2 until .NET 6 which makes this code disfunctional but I can't figure out what and I also didn't find anything on the web what would help me.
Here is what I've tried but didn't help:
Replacing AddMvc() for AddControllers() and UseMvc() for UseEndpoints(endpoints => endpoints.MapControllers())
Omit Startup class and call Configure and ConfigureServices directly from the builder
Any help would be highly appreciated :)
For me the problem was that my HostBuilder was in different assembly (Library Project) than Controllers as it was used in multiple different projects. And apparently the logic of loading Controllers must have changed and it was looking for Controllers only in Assembly where HostBuilder was located. So adding AddApplicationPart into ConfigureServices fixed my problem and everything works fine now.
Solution Code:
services
.AddControllers()
.AddNewtonsoftJson(options => JsonSettings.SetSettings(options.SerializerSettings))
.AddApplicationPart(Assembly.GetEntryAssembly()); //Adding this will look for Controllers in your Entry Point assembly where most likely your Controllers are

Method not found: System.MissingMethodException Microsoft.Extensions.DependencyInjection.IServiceCollection.AddAuthorization

I am migrating from .net core web api from 2.2 to 3.1 I am getting below error at run time .
Soucrec :PII.Enterprise.AspNetCore.ApiStandards
'Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.AuthorizationServiceCollectionExtensions.AddAuthorization(Microsoft.Extensions.DependencyInjection.IServiceCollection,
System.Action`1<Microsoft.AspNetCore.Authorization.AuthorizationOptions>)'.
I have added settings here
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthenticationCore();
// this line calling third party DLL I am getting exception here
services.ConfigureAuthorizationHandler(Configuration);
}

Use existing IContainer in new instance of Autofac container

I have solution with .NET Core 3.1 console application, which using Autofac as IoC container. In this solution is approximately 50 projects as class libraries, which is referenced over Autofac as modules. Now I am creating new module, which will start .NET Core web API which will provide data from database and other running modules.
New web API module is declared as follows:
public class RESTModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(c =>
{
return Host.CreateDefaultBuilder(new string[] { })
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseConfiguration(c.Resolve<IConfiguration>());
webBuilder.UseStartup<Startup>();
})
.Build();
}).SingleInstance();
}
}
And my question is:
Is it possible to add existing Autofac instance with all modules, configurations and databases to instance of this created WEB API without adding this things manually again on start?
Thank you
Note:
.NET Core console application -> loading modules from configuration
loaded Module 1 (Database 1) as IDatabase1
loaded Module 2 (Database 2) as IDatabase2
loaded another 40 modules
and loaded new module RESTModule where i will use IDatabase1, IDatabase2, and another 10 running modules

Alternative code for services.BuildServiceProvider (in ConfigureServices) when migrating from ASP.NET Core 2.2 to 3.1

I have modular ASP.NET Core application. Back in the day of .NET Core 1.1 I build services (AddScoped, AddSingleton ...) in public void ConfigureServices(IServiceCollection services) method like this:
public void ConfigureServices(IServiceCollection services)
{
//...
var sp = services.BuildServiceProvider();
GlobalConfiguration.ModuleInitializers = sp.GetServices<IModuleInitializer>();
foreach (var moduleInitializer in GlobalConfiguration.ModuleInitializers)
moduleInitializer.ConfigureServices(services, this.Configuration, this.Environment);
}
each module (library) has own class which implements 'IModuleInitializer' and register services:
public class ModuleInitializer : IModuleInitializer
{
public void ConfigureServices(IServiceCollection serviceCollection, IConfiguration configuration, IHostingEnvironment environment)
{
serviceCollection.AddScoped<....>();
serviceCollection.AddScoped<...>();
...
With migration from .NET Core 2.2 to 3.1 I am receiving warning:
Calling 'BuildServiceProvider' from application code results in an
additional copy of singleton services being created. Consider
alternatives such as dependency injecting services as parameters to
'Configure'
I already educate myself what that means, where is the problem ..., but I am having problems how to properly configure services in a module design application.
I also need to get information about modules from internet. My question is if this approach is good, or is there any better/recommended?
public Startup(IConfiguration configuration)
{
Configuration = configuration; //From https://learn.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-3.1
}
public void ConfigureServices(IServiceCollection services)
{
...
var serverSett = this.Configuration.GetSection("Server").Get<ServerSettings>();
string[] allowedModules;
using (var httpClientHandler = new HttpClientHandler())
{
allowedModules = //Get allowed modules from web
}
if (allowedModules.Any(t => t == "Shopping");
services.AddShopping(); //Extension method in Shopping library which register own services.
if (allowedModules.Any(t => t == "Offer");
services.AddOffer(); //Extension method in Offer library which register own services.
}
Using the DI to setup the DI is a bit of a problem and could definitely be problematic.
Why not just use some reflection to get the IModuleInitializers.
var moduleInitializers = typeof(MyAssembly).GetTypeInfo().Assembly
.GetTypes()
.Where(type =>
!type.GetTypeInfo().IsAbstract &&
typeof(IModuleInitializer).IsAssignableFrom(type))
.Select(type => Activator.CreateInstance(type))
.Cast<IModuleInitializer>()
.ToArray();
foreach (var moduleInitializer in moduleInitializers)
moduleInitializer.ConfigureServices(services, this.Configuration, this.Environment);
As for your second question I cannot give you an advice. But I would question the need to dynamically setup the app unless you really really need that.

Apply [EnableQuery] attribute of OData Globally in ASP.NET Core

In ASP.NET I'm able to Enable Query support Globally using following lines of code.
public static void Register(HttpConfiguration config)
{
config.EnableQuerySupport();
}
OData recently reliesed their beta2 version of AspNetCore.OData. Using which we can enable query support at action level using [EnableQuery]
Do we have any similar code as in ASP.NET where I can enable globally in ASP.NET core OData
You can use this extension method :
public static IMvcBuilder EnableODataQuery(this IMvcBuilder builder)
{
builder.Services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new AspNet.OData.EnableQueryAttribute());
});
return builder;
}
And call it "in Startup.cs" like this :
services.AddControllersWithViews().EnableODataQuery();

Resources