Resolving dependency injected service as part of builder when specifying options - c#

I'm using Microsoft.Extensions.DependencyInjection in my ASP.NET Core project (targeting .NET 7.0).
I have a service that, provided a sizable number of injected other services, yields a string value that I need in order to populate an options method during my DI registrations. Typically, I'd simply have DI inject the service into any of the controllers in which I'd pull the various values needed, but here the situation is a bit different.
Here, I am using one of those many helpful extension methods of IServiceCollection that themselves register their own various types, but it also exposes an action that allows me to specify some settings. I need to resolve a string value from my aforementioned service that I can use in the option specification method later on.
This extension method isn't something I have written, but part of a third-party library and it itself is quite extensive (e.g. not something I want to write/maintain my own version of). The extension itself looks like the following:
builder.Services.AddMySpecialService().SpecifyOptions(opt => {
opt.Id = "<Insert DI string value here>";
});
Ideally, I need to inject the service in such a way so as to pass that string value into my settings, but short of creating a local instance (not really feasible given all its own dependencies), I'm at a loss of how I'd go about this, if it's even possible at all.
Has anyone successfully done this and if so, how? Thanks!

Without knowing what that third-party library is, you can use dependencies when configuring your options in a standard way.
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder
.Services
.AddSingleton<IStringProviderService, StringProviderService>()
.AddOptions<MyOptions>()
// For .Configure() method you can specify up to 5 dependencies.
.Configure<IStringProviderService>(
(myOptions, stringProviderService) =>
{
myOptions.Id = stringProviderService.GetString();
}
);
public class MyOptions
{
public required string Id { get; set; }
}
public interface IStringProviderService
{
string GetString();
}
public class StringProviderService : IStringProviderService
{
string IStringProviderService.GetString()
{
return Guid.NewGuid().ToString("N");
}
}

Related

Microsoft Dependency Injection Documentation

In the documentation for dependency injection I notice the following line.
The MVC framework will automatically look at the service provider to
register our dependency in the Controller.
They then provide a basic example with constructor injection, not their example but in essence this.
public class Example
{
private IFooFactory foo;
public Example(IFooFactory foo) => this.foo = foo;
public void SampleUse()
{
using(var context = foo.Create())
context.DoSomething();
}
}
If you have a console application, by default it will not look at the service provider to register your dependency with the concrete implementation. Is there a way to simulate that? Otherwise the console application will require you to do something along these lines:
public static Main(string[] args)
{
// Stuff to prepare the application and build service provider.
var service = serviceProvider.GetService<IFooFactory>();
using(var context = service.Create())
context.DoSomething();
// OR
var fooFactory = serviceProvider.GetService<IFooFactory>();
new Example(fooFactory).SampleUse();
}
Which creates the problem of having to pass IFooFactory or pulling things into the main that you may wanted separated for structure. How can I make the console application look at the provider when a new class is created with a defined interface?
You have to create everything manually as the framework is not there to automagically do it for you.
var services = new ServiceCollection();
services.AddTransient<IFooFactory, FooFactory>();
services.AddTransient<Example>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
Example example = serviceProvider.GetService<Example>();
example.SampleUse();
While not ideal, it is usually the way shown in most examples where DI is configured manually.
When you inspect the framework DI integration, behind the scenes it does the exact same thing during startup.
You could probably write your own code to inspect available types, but that is a very broad task to tackle on your own.
Reference Dependency injection in ASP.NET Core
Default service container replacement
The built-in service container is meant to serve the needs of the
framework and most consumer apps. We recommend using the built-in
container unless you need a specific feature that it doesn't support.
Some of the features supported in 3rd party containers not found in
the built-in container:
Property injection
Injection based on name
Child containers
Custom lifetime management
Func<T> support for lazy initialization

Why is an ASP.NET-Core app 'Configuration/AppSettings' POCO passed around as IOptions<T> instead of just T? [duplicate]

It seems to me that it's a bad idea to have a domain service require an instance of IOptions<T> to pass it configuration. Now I've got to pull additional (unnecessary?) dependencies into the library. I've seen lots of examples of injecting IOptions all over the web, but I fail to see the added benefit of it.
Why not just inject that actual POCO into the service?
services.AddTransient<IConnectionResolver>(x =>
{
var appSettings = x.GetService<IOptions<AppSettings>>();
return new ConnectionResolver(appSettings.Value);
});
Or even use this mechanism:
AppSettings appSettings = new AppSettings();
Configuration.GetSection("AppSettings").Bind(appSettings);
services.AddTransient<IConnectionResolver>(x =>
{
return new ConnectionResolver(appSettings.SomeValue);
});
Usage of the settings:
public class MyConnectionResolver
{
// Why this?
public MyConnectionResolver(IOptions<AppSettings> appSettings)
{
...
}
// Why not this?
public MyConnectionResolver(AppSettings appSettings)
{
...
}
// Or this
public MyConnectionResolver(IAppSettings appSettings)
{
...
}
}
Why the additional dependencies? What does IOptions buy me instead of the old school way of injecting stuff?
Technically nothing prevents you from registering your POCO classes with ASP.NET Core's Dependency Injection or create a wrapper class and return the IOption<T>.Value from it.
But you will lose the advanced features of the Options package, namely to get them updated automatically when the source changes as you can see in the source here.
As you can see in that code example, if you register your options via services.Configure<AppSettings>(Configuration.GetSection("AppSettings")); it will read and bind the settings from appsettings.json into the model and additionally track it for changes. When appsettings.json is edited, and will rebind the model with the new values as seen here.
Of course you need to decide for yourself, if you want to leak a bit of infrastructure into your domain or pass on the extra features offered by the Microsoft.Extensions.Options package. It's a pretty small package which is not tied to ASP.NET Core, so it can be used independent of it.
The Microsoft.Extensions.Options package is small enough that it only contains abstractions and the concrete services.Configure overload which for IConfiguration (which is closer tied to how the configuration is obtained, command line, json, environment, azure key vault, etc.) is a separate package.
So all in all, its dependencies on "infrastructure" is pretty limited.
In order to avoid constructors pollution of IOptions<>:
With this two simple lines in startup.cs inside ConfigureServices you can inject the IOptions value like:
public void ConfigureServices(IServiceCollection services)
{
//...
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddScoped(cfg => cfg.GetService<IOptions<AppSettings>>().Value);
}
And then use with:
public MyService(AppSettings appSettings)
{
...
}
credit
While using IOption is the official way of doing things, I just can't seem to move past the fact that our external libraries shouldn't need to know anything about the DI container or the way it is implemented. IOption seems to violate this concept since we are now telling our class library something about the way the DI container will be injecting settings - we should just be injecting a POCO or interface defined by that class.
This annoyed me badly enough that I've written a utility to inject a POCO into my class library populated with values from an appSettings.json section. Add the following class to your application project:
public static class ConfigurationHelper
{
public static T GetObjectFromConfigSection<T>(
this IConfigurationRoot configurationRoot,
string configSection) where T : new()
{
var result = new T();
foreach (var propInfo in typeof(T).GetProperties())
{
var propertyType = propInfo.PropertyType;
if (propInfo?.CanWrite ?? false)
{
var value = Convert.ChangeType(configurationRoot.GetValue<string>($"{configSection}:{propInfo.Name}"), propInfo.PropertyType);
propInfo.SetValue(result, value, null);
}
}
return result;
}
}
There's probably some enhancements that could be made, but it worked well when I tested it with simple string and integer values. Here's an example of where I used this in the application project's Startup.cs -> ConfigureServices method for a settings class named DataStoreConfiguration and an appSettings.json section by the same name:
services.AddSingleton<DataStoreConfiguration>((_) =>
Configuration.GetObjectFromConfigSection<DataStoreConfiguration>("DataStoreConfiguration"));
The appSettings.json config looked something like the following:
{
"DataStoreConfiguration": {
"ConnectionString": "Server=Server-goes-here;Database=My-database-name;Trusted_Connection=True;MultipleActiveResultSets=true",
"MeaningOfLifeInt" : "42"
},
"AnotherSection" : {
"Prop1" : "etc."
}
}
The DataStoreConfiguration class was defined in my library project and looked like the following:
namespace MyLibrary.DataAccessors
{
public class DataStoreConfiguration
{
public string ConnectionString { get; set; }
public int MeaningOfLifeInt { get; set; }
}
}
With this application and libraries configuration, I was able to inject a concrete instance of DataStoreConfiguration directly into my library using constructor injection without the IOption wrapper:
using System.Data.SqlClient;
namespace MyLibrary.DataAccessors
{
public class DatabaseConnectionFactory : IDatabaseConnectionFactory
{
private readonly DataStoreConfiguration dataStoreConfiguration;
public DatabaseConnectionFactory(
DataStoreConfiguration dataStoreConfiguration)
{
// Here we inject a concrete instance of DataStoreConfiguration
// without the `IOption` wrapper.
this.dataStoreConfiguration = dataStoreConfiguration;
}
public SqlConnection NewConnection()
{
return new SqlConnection(dataStoreConfiguration.ConnectionString);
}
}
}
Decoupling is an important consideration for DI, so I'm not sure why Microsoft have funnelled users into coupling their class libraries to an external dependency like IOptions, no matter how trivial it seems or what benefits it supposedly provides. I would also suggest that some of the benefits of IOptions seem like over-engineering. For example, it allows me to dynamically change configuration and have the changes tracked - I've used three other DI containers which included this feature and I've never used it once... Meanwhile, I can virtually guarantee you that teams will want to inject POCO classes or interfaces into libraries for their settings to replace ConfigurationManager, and seasoned developers will not be happy about an extraneous wrapper interface. I hope a utility similar to what I have described here is included in future versions of ASP.NET Core OR that someone provides me with a convincing argument for why I'm wrong.
I can't stand the IOptions recommendation either. It's a crappy design to force this on developers. IOptions should be clearly documented as optional, oh the irony.
This is what I do for my configuraition values
var mySettings = new MySettings();
Configuration.GetSection("Key").Bind(mySettings);
services.AddTransient(p => new MyService(mySettings));
You retain strong typing and don't need need to use IOptions in your services/libraries.
You can do something like this:
services.AddTransient(
o => ConfigurationBinder.Get<AppSettings>(Configuration.GetSection("AppSettings")
);
Using Net.Core v.2.2, it's worked for me.
Or then, use IOption<T>.Value
It would look something like this
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
I would recommend avoiding it wherever possible. I used to really like IOptions back when I was working primarily with core but as soon as you're in a hybrid framework scenario it's enough to drive you spare.
I found a similar issue with ILogger - Code that should work across frameworks won't because I just can't get it to bind properly as the code is too dependent on the DI framework.

.net-core Dependency Injection

I have a Generic repository which I want to register for DI, it implements an interface IRepository.
Normally I would create an instance of it like this:
IRepository repo = new Repository<Order>();
However I am trying to get up to speed in .net 5 ahead of release and want to get this working with DI, I have resorted to the following :
services.AddTransient<DAL.IRepository<Models.Order>, DAL.Repository<Models.Order>>();
But this feels wrong, I don't want 50+ lines in there one for each of the classes in my model...
I cannot find anything online about this, I know its possible with other ioc containers.. but as this is a learning project I dont want to use another container, Im aiming to do it all with .net5s native container.
You should be able to register the open generic with
services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
After some back and forwards in the comments to other answers I have a working solution, It might not be the best way but it works. Ill update again if I find a better way to implement this.
The two issues I had were : Needed to register a generic interface, the issue here was a lapse in concentration on my part.. I had the syntax wrong for registering a generic type which of course is :
services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
The second issue was that I have an assembly which contains 50+ different models which I wanted registered, The way that I addressed this was to write a method that I can pass a list of assemblies to along with the Namespace that I want to register and it iterates over any types that match the criteria and registers them in the DI container.
public void RegisterModels(IServiceCollection services, string[] Assemblies, string #NameSpace)
{
foreach (var a in Assemblies)
{
Assembly loadedAss = Assembly.Load(a);
var q = from t in loadedAss.GetTypes()
where t.IsClass && !t.Name.Contains("<") && t.Namespace.EndsWith(#NameSpace)
select t;
foreach (var t in q.ToList())
{
Type.GetType(t.Name);
services.AddTransient(Type.GetType(t.FullName), Type.GetType(t.FullName));
}
}
}
This is then called from the startup.cs method ConfigureServices :
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<TestContext>(options =>
options.UseSqlServer(#"Server=LOCALHOST\SQLEXPRESS;Database=Test;Trusted_Connection=True;"));
services.AddMvc();
RegisterModels(services, new string[] { "UI" }, "UI.Models");
services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
}
There may be a better way to do this, there definitely is using different DI containers, if anyone has improvements to offer please let me know.
You could use a convention based registration library like Scrutor.
Scrutor is a small open source library that provides a fluent API to register services in your Microsoft.Extensions.DependencyInjection container based on conventions (Similar to Autofac's RegisterAssemblyTypes method, StructureMap's Scan method and Ninject's Conventions package).
This will allow you to do something like this:
services.Scan(scan => scan
.FromAssemblies(<<TYPE>>.GetTypeInfo().Assembly)
.AddClasses(classes => classes.Where(x => {
var allInterfaces = x.GetInterfaces();
return
allInterfaces.Any(y => y.GetTypeInfo().IsGenericType && y.GetTypeInfo().GetGenericTypeDefinition() == typeof(IRepository<>)));
}))
.AsSelf()
.WithTransientLifetime()
);
What you can do is create an extension method to encapsulate all those individual items that need to be registered.
That is the same technique Microsoft is using, for example you only put this in startup:
services.AddMvc();
but that is an extension method and behind the scenes you can bet it is registering a bunch of stuff it needs.
so you can create your own extension method like this:
using Microsoft.Extensions.DependencyInjection;
public static IServiceCollection AddMyFoo(this IServiceCollection services)
{
services.AddTransient<DAL.IRepository<Models.Order>, DAL.Repository<Models.Order>>();
//....
return services;
}
and by making the method return the IServiceCollection you make it fluent so you can do
services.AddMyFoo().AddSomeOtherFoo();
Updated based on comment
the other technique to reduce registrations is when your dependency doesn't itself have dependencies you can make the constructor have a default of null so you still have decoupling and could pass a different one in later but the DI won't throw an error and you can just instantiate what you need if it is not passed in.
public class MyFoo(IFooItemDependency myItem = null)
{
private IFooItemDependency internalItem;
public MyFoo(IFooItemDependency myItem = null)
{
internalItem = myItem ?? new FooItemItem();
}
}
I'm not 100% sure on what your question is I assume you don't want to have
services.AddTransient<DAL.IRepository<Models.Order>, DAL.Repository<Models.Order>>();
services.AddTransient<DAL.IRepository<Models.Person>, DAL.Repository<Models.Person>>();
services.AddTransient<DAL.IRepository<Models.Invoice>, DAL.Repository<Models.Invoice>>();
etc
I have done this before (with ninject)
Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InRequestScope();
I imagine for Unity you can do something similar like
services.AddTransient<DAL.IRepository<>, typeof(Repository<>)();
And then to use it in a service
public OrderService(IRepository<Models.Order> orderRepository)
{
this.orderRepository = orderRepository;
}
EDIT
As pointed out by OP the correct syntax is:
services.AddTransient(typeof(IRepository<>), typeof(Repository<>));

Logging as a cross-cutting concern in WCF

I would like to log to file every time my Web service is called recording the name of the method called as well as the calling data.
To write in this file, I use Nlog but I would like not to make the logging call in each of my service operations. I'd prefer to automate this logging call somehow as a cross cutting concern.
Is it possible?
You can use a DI container supporting interceptors to log any WCF method call with input values. Here is an example for Ninject and old weird ASMX service (for WCF, idea is the same):
Let's consider the following client class:
public partial class LegacyClient : System.ServiceModel.ClientBase<Legacy>, Legacy
Because this class is partial, we may easily add to class the interface ILegacyClient containing methods we want to call. ReSharper extract the interface in a second. So place the partial class declaration in separate file:
public partial class LegacyClient : ILegacyClient {}
In DI config, add the interceptor:
Kernel.Bind(typeof(ILegacyClient))
.To<LegacyClient>()
.InRequestScope() //carefully choose a scope
.Intercept()
.With<LogRequestInterceptor>();
LogRequestInterceptor is the most interesting part:
using Ninject;
using Ninject.Extensions.Interception;
public class LogRequestInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
MethodInfo method = invocation.Request.Method;
var parameters = method.GetParameters();
var builder = new StringBuilder();
for (int index = 0; index < parameters.Length; index++)
{
object argument = invocation.Request.Arguments[index];
ParameterInfo parameterInfo = parameters[index];
if (!parameterInfo.IsOut)
{
//use any serialization you like
string text = $"{parameterInfo.Name} = {argument.ToJson()}, ";
builder.Append(text);
}
}
string joinedParameters = builder.ToString();
YourLogging(method.Name, joinedParameters);
//LegacyClient method call, don't forget this line
invocation.Proceed();
}
}
Last part to make things work. Create proxy object via DI and use it:
ILegacyClient client = Kernel.GetService<ILegacyClient>();
client.AnyMethodCall(...);
Direct creation is only an example, of course you may use constuctor or property injection instead and replace client interface with factory if you need.
Hope it helps!
You can try to use either Aspect Oriented programming (AOP) using Windsor Castle, PostSharp or other 3rd party Libraries offering AOP. Also, you may use interceptors from these Libraries.
WCF Extension might be another place to do this kind of logging.
Using WCF tracing. You can view the Service calls, message level details etc by enabling WCF tracing by Configuration.
The answer from Agalo is correct but too general, so I would just add a link to how to implement #2. I guess this is exactly what question author wants to have.
In a nutshell, you can implement your own parameter inspector (IParameterInspector) and inject it using service behavior (IServiceBehavior). Here you can find complete example how to do that for logging.

Injecting runtime value into Unity dependency resolver

I am working on a webapi project and using Unity as our IOC container. I have a set of layered dependencies something like the following:
unityContainer.RegisterType<BaseProvider, CaseProvider>(new HierarchicalLifetimeManager());
unityContainer.RegisterType<IRulesEngine, RulesEngine>();
unityContainer.RegisterType<IQuestionController, QuestionController>();
unityContainer.RegisterType<IAPIThing, WebAPIThing>();
Now the constructor for BaseProvider accepts an int as a parameter which is the Case identifier. WebAPIThing takes a BaseProvider in its constructor. Normally in a non web scenario I would inject the case id using something like:
public static IAPIThing GetIAPIThing(int caseId)
{
return CreateUnityContainer().Resolve<IAPIThing >(new ParameterOverride("caseId", caseId).OnType<CaseProvider>());
}
But that only works when I explicitly call that method. In a Web API scenario I am using a
config.DependencyResolver = new UnityDependencyResolver(unityContainer); to resolve my api controllers.
I would guess I will still need to influence how the DependencyResolver resolves that BaseProvider object at runtime.
Anyone had to do something similar?
EDIT 1
I have tried using the following which appears to work:
unityContainer.RegisterType<BaseProvider>(
new HierarchicalLifetimeManager()
, new InjectionFactory(x =>
new CaseProvider(SessionManager.GetCaseID())));
You are trying to inject a runtime value (the case id) into the object graph, which means you are complicating configuration, building, and verification of the object graph.
What you should do is promote that primitive value to its own abstraction. This might sound silly at first, but such abstraction will do a much better job in describing its functionality. In your case for instance, the abstraction should probably be named ICaseContext:
public interface ICaseContext
{
int CurrentCaseId { get; }
}
By hiding the int behind this abstraction we effectively:
Made the role of this int very explicit.
Removed any redundancy with any other values of type int that your application might need.
Delayed the resolving of this int till after the object graph has been built.
You can define this ICaseContext in a core layer of your application and everybody can depend on it. In your Web API project you can define a Web API-specific implementation of this ICaseContext abstraction. For instance:
public class WebApiCaseContext : ICaseContext
{
public int CurrentCaseId
{
get { return (int)HttpContext.Current.Session["CaseId"];
}
}
This implementation can be registered as follows:
unityContainer.RegisterType<ICaseContext, WebApiCaseContext>();
UPDATE
Do note that your own new CaseProvider(SessionManager.GetCaseID()) configuration does not solve all problems, because this means that there must be a session available when verifying the object graph, which will neither be the case during application startup and inside a unit/integration test.

Categories

Resources