.NET 6, Web API - configuration driven DI and NuGet package registration - c#

I'm using .NET 6 Web API and would like to introduce some custom mocking NuGet package to our dev/test environments. I don't want this package/code to be present in further stages, so would like to do some DI registration in the appsettings.json instead of Program.cs. Would be nice to load NuGet package containing mock code as well from the appsettings.json. This would give the control of the environment via its configuration file. Is there a way to do this?

Regarding DI registration, you should be able to put part of configuration into configuration file. How exactly can it be done depends on the DI container. E.g. here is the doc for Autofac: https://autofac.readthedocs.io/en/latest/configuration/xml.html
But I don't think you can or should do similar trick for NuGet packages.

Related

Move di container into his own project in .NET Core

I'm building an API in .NET Core 3.1. I try to decouple this project in the typical 3 layers. Instead of having an UI layer, I have the API project with the controllers. I also have a class library project for the logic, and another class library project as the data access layer.
I'm trying to use dependency injection over all projects. My problem is, that I have, until now, registered my interfaces and classes as services in the ConfigureServices method in startup.cs, in my API project.
But that would mean, that the logic, and the data access layer would reference the API project. To solve this, I thought, it would be the best, if I move the DI container into his own "mapping" class library project and reference this in all other projects. Would that be a good practice?
If yes, how could such a project look like, how to setup the container and how will it be instantiated in the API's project ConfigureServices method in startup.cs?
But that would mean, that the logic, and the data access layer would
reference the API project
I don't think it should, just opposite, API project will be solution entry point, sole project which "knows" about all other dependencies and glue them together.
Other projects need to reference IServiceCollection Interface, which can be done by installing Microsoft.Extensions.DependencyInjection NuGet package.
Every project can introduce own "registration" method, which can be called by entry point.
// In Logic project
public IServiceCollection AddLogic()
{
services.AddSingleton<MyLogic>();
// Add other logic types
return services;
}
// In Data project
public IServiceCollection AddDataAccess(string connectionString)
{
services.AddTransient<IRepository, SqlRepository>();
services.AddDbContext<MyDbContext>(o => o.UseSqlServer(connectionString));
return services
}
// Startup
var connectionString = Configuration.GetConnectionString("MyDatabase");
services.AddLogic();
services.AddDataAccess(connectionString);
Only way where other projects need to reference API project is when you are using some types from API project. If this the case, then move them to the project where they are going to be used or introduce another project which both API and other project can reference.

NLog works in ASP.NET Core App but not in .NET Core xUnit Test Project

I've created a .NET Core class library that defines utility class interfaces and that implements each interface for things like database access via ADO.NET, etc.
Each implementation class utilizes NLog. This works great in an ASP.NET Core Web application that includes and references the .NET Core class library.
However, when I try to instantiate these same implementation classes in the context of a .NET Core xUnit Test project, I get
Unable to cast object of type 'NLog.Logger' to type 'Microsoft.Extensions.Logging.ILogger'
I've searched for similar issues but found none.
In each class I've declared a private readonly variable:
private readonly ILogger<DatabasePersistenceAdoDotNet> _logger = null;
And I'm instantiating this variable like this:
_logger = (ILogger<DatabasePersistenceAdoDotNet>)NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
This works in an ASP.NET Core Web application that includes and references the .NET Core class library but blows up in my .NET Core xUnit Test project.
I suspect that this has something to do with ASP.NET's dependency inject not being available in the .NET Core xUnit Test project, but I can't figure out how to make my utility package work correctly in both contexts.
When using NLog with ASP.NET Core, there are two styles:
The non-DI style, with usage of GetCurrentClassLogger()
The DI style, which needs the NLog.Extensions.Logging / NLog.Web.AspNetCore package to bridge between NLog and Microsoft.Extensions.Logging.
When using .GetCurrentClassLogger(), you wil get a NLog logger object (which implements NLog.ILogger, not Microsoft.Extensions.Logging.ILogger).
So if you need a Microsoft.Extensions.Logging.ILogger, you can't use the .GetCurrentClassLogger(). You need to:
Set up the whole DI system yourself and constructor inject the Microsoft.Extensions.Logging.ILogger, or
Use the classes of the NLog.Extensions.Logging package.
Option 2 is the easy way and I will show you how:
// Load NLog
NLog.Web.NLogBuilder.ConfigureNLog("nlog.config");
// Create provider to bridge Microsoft.Extensions.Logging
var provider = new NLog.Extensions.Logging.NLogLoggerProvider();
// Create logger
Microsoft.Extensions.Logging.ILogger logger = provider.CreateLogger(typeof(MyClass).FullName);
Please note, you can't create a Microsoft.Extensions.Logging.ILogger<MyClass> this way! Just a non-generic Microsoft.Extensions.Logging.ILogger. If you need the generic version, then you need the DI setup and that will handle the conversion between both.
NLog.config and test projects
Also good to know, finding a nlog.config in a test project is hard, as unit test frameworks will move the binaries etc. I would recommend to use the config API
e.g.
var configuration = new LoggingConfiguration();
configuration.AddRuleForAllLevels(new ConsoleTarget());
LogManager.Configuration = configuration;
OR
var configuration = XmlLoggingConfiguration.CreateFromXmlString("<nlog>....</nlog>"); //full nlog.config as string
LogManager.Configuration = configuration;

Structuremap and ASP.NET core

I am using ASP.NET core and I'm trying to make use of structuremap for my IoC, but I seem to be having some issues. When I write a unit test that inits structuremap directly, everything works fine. When I print the configuration out to file, I can see that my setup is indeed registering everything correctly.
However, the populate seems to be giving me trouble.
I am trying to use StructureMap.Microsoft.DependencyInjection but I get an error when I build:
The dependency structuremap >= 4.4.0 could not be resolved.
I have StructureMap 4.4.1 installed in my project, including the project I installed the StructureMap.Microsoft.DependencyInjection library into (my Web API project).
So, I then took the files out of the project on github and loaded them into my solution, and removed the nuget package, but for some reason it is not working.
Here is a plunker with the relevant files
Ideally, I'd rather use the nuget package to do this, but I've never encountered a dependency issue when I have the actual dependency already installed.
EDIT: A few more details
When I write the results of container.WhatDoIHave() to a file, my classes are all shown correctly t0 be part of structuremap, however when I run container.AssertConfigurationIsValid(); is when I am getting errors about things correctly defined as reported by WhatDoIHave()
Here is what my configure method looks like
public IServiceProvider ConfigureIoC(IServiceCollection services)
{
var container = new Container();
container.Configure(config =>
{
// scan the webapi to register all the controllers
config.Scan(scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
//this is an IoC configuration in another library that
// ties together a bunch of class libraries so they
// don't all have to be in my web project
IoC.BootStrapper.Configure(config, "MembershipReboot");
});
System.IO.File.WriteAllText(#"C:\users\{me}\documents\structuremapTest.txt", container.WhatDoIHave());
container.AssertConfigurationIsValid();
//Populate the container using the service collection
container.Populate(services);
return container.GetInstance<IServiceProvider>();
}
Rename the "StructureMap" package to "structuremap", seems like some weird issue with NuGet casing.
Cheers :)
See this issue on the StructureMap.Microsoft.DependencyInjection issue tracker:
The dependency structuremap >= 4.3.0 could not be resolved
https://github.com/structuremap/StructureMap.Microsoft.DependencyInjection/issues/17

Best pattern for configuration management Asp.net

I always see that there are app.config's in dlls and then website has web.config.
At the run time mostly web.config passes data to all the portable libraries.
These configuration are passed as properties of portable library class. ConfigurationManager is used to get the values from config file.
Now the problem, In my current project they have configsectionhandler in webapp project/folder.
and my project folder structure is like
Main
WebApp
BusinessObject
ThirdPartyWebserviceCall
and as usual webapp project has reference to BusinessObject and BusinessObject has reference to ThirdPartyWebserviceCall.
Now if I want to use configsectionhandler in ThirdPartyWebserviceCall I will have add a circular reference to
webapp.
Does this design seem ok? It looks realy flawed to me.
Is there other standard pattern to access configuration values in the libraries.

Inject dependencies (NLog) into dynamically loaded types

I have sort of plugin architecture in my solution.
There is a well-known folder where to place plugins in it.
The plugins implement an interface which is shared in the host project.
Initially i load the plugin via Assembly.LoadFrom(fi.FullName).GetTypes()
and instanciate the needed type by (IPlugin)Activator.CreateInstance(type);.
So the host (main application) can execute appropriate code implemented by the plugin assembly. This works fine so far.
But recently i tried to implement application logging via NLog and configured NLog in the host project which worked great.
The problem is i would like to use (the already configured) logger in the plugin assembly.
If i just reference NLog and use it by LogManager.GetCurrentClassLogger(); there seems to be no configuration set. It does not log to the files i configured in the host project out of the plugin assembly.
I thought of trying to inject an NLogger instance (created in the host project) to a property of the plugin type.
Is this possible or is there a preferred way to accomplish things like that?
Thanks
this should work - NLog configuration should work also for the loaded plugin assemblies.
The problem is probably related to how your plugins are loaded. Maybe they are in a separate domain (I don't remember how it works) so NLog can't access your main applications's logging configuration.
In that case you could try adding separate nlog configuration files for your plugin assemblies (read nlog documentation about config file naming conventions).
I dont think IOC container will help you in case of dynamically loaded plugins - the container will not know about them so you will have to change the way plugins are loaded and configured. And imho using IOC for configuring nlog is not a great idea.
If the previous options don't work you could try changing the assembly probing path in app.config so your plugins are loaded into the default domain - then NLog should work for these plugins (at least works for me):
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Plugins" />
</assemblyBinding>
Have a look at Dependency Injection Containers like Unity or Lightcore.
They are some kind of "component registration stores"
there you can register components to interfaces. Then your consumers just need to request an interface.
The mapping of interface - component can be made in configuration files or source code.
So you can change the mapping without any pain.
For example when they are creating new instances and you are having complextypes as ctor parameters, they can make an automatic lookup if these components are registered and then inject them automatically.
Some Keywords that can help you are "ServiceLocator" "MicroKernel" "Depency Injection"
Components:
Lightcore
Unity
Also a more Plugin centric way is MEF

Categories

Resources