How to call DI Confgure<T>(Config) with reflection? - c#

I have ASP.NET Core Web API.
I want to inject in DI container IOptions<MySetting> options. In Startup class it injected like this -
services.Configure<MySettings>(Configuration.GetSection("MySetting"));. Where MySettings is class with some fields and a section with the same name in appsetings.json.
Also i want inject it with other way. With reflection:
var configureMethod = typeof(OptionsConfigurationServiceCollectionExtensions).GetMethods()
.Single(m => m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(MySettings));
I sure configureMethod matches with services.Configure<MySettings>(Configuration.GetSection("MySetting"));.
But when I try to invoke this:
configureMethod.Invoke(null, new object?[] { services, configuration.GetSection("MySettings") });
I get error:
Unhandled exception. System.ArgumentException: Cannot instantiate implementation type 'System.ComponentModel.Design.DesignerOptionService' for service type 'System.ComponentModel.Design.IDesignerOptionService'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.Populate()
...
What am I doing wrong and how to fix it?

I tried add injecting with reflection for automatically registration {featureNmae}Setting classes in project, because there can be a lot of such classes in project.
In project i have many assemblies and i was getting them for suitable types with AppDomain.CurrentDomain.GetAssemblies() method.
Now i change it on Assembly.GetExecutingAssembly() and it works!

Related

FluentValidation: How to register all validators automatically from another assembly?

I'm using "FluentValidation.AspNetCore" library (Version="8.6.2") for a .Net Core project.
What I would like to do is to register all my Validators automatically in Startup.cs class, using something like this (which is what I'm using now):
services.AddControllers().AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<Startup>());
But the problem is that my validators are going to be moved to another assembly (required by client), so I can't use Startup as reference for the registration.
Is there a way I can do this without having to register the validators one by one?
For registering Validator, there are three ways.
Registering each validator in Service with AddTransient.
Registering using “RegisterValidatorsFromAssemblyContaining” method registers all
validators derived from AbstractValidator class within the assembly
containing the specified type.
Registering using “RegisterValidatorsFromAssembly” method.
source here
If you use option number 2 and replace Startup in your code with any class derived from AbstractValidator from another assembly, it will register all validators from that assembly
Example:
services.AddControllers().AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<AnotherValidationClass>());
You can use the official nuget package for that:
services.AddValidatorsFromAssemblyContaining<SomeValidator>();
or
services.AddValidatorsFromAssemblyContaining(typeof(SomeValidator));
or
services.AddValidatorsFromAssembly(typeof(SomeValidator).Assembly);
All possible DI options described here
AddFluentValidation() is deprecated.
source : https://github.com/FluentValidation/FluentValidation/issues/1965
old:
builder.Services.AddControllersWithViews().AddFluentValidation(fv => { });//or AddMvc(), AddMvcCore()
//or
builder.Services.AddFluentValidation(config =>
{
config.ConfigureClientsideValidation(enabled: false);
});
new :
builder.Services.AddValidatorsFromAssemblyContaining<AnyValidator>() // register validators
builder.Services.AddFluentValidationAutoValidation(); // the same old MVC pipeline behavior
builder.Services.AddFluentValidationClientsideAdapters(); // for client side

ComponentNotRegisteredException even with registered instance

I have an interface IMyInterface and I am registering a Moq object for unit testing like this
var myMockObject = new Mock<IMyInterface>();
myMockObject.Setup(a => a.MyMethod(It.IsAny<string>()))
.Returns(new MyResult()
{
Props1 = "Testing123",
}).Callback(() =>
{
});
builder.RegisterInstance(myMockObject).As<IMyInterface>();
var test = container.Resolve<IMyInterface>();
But I am always getting the following error:
Autofac.Core.Registration.ComponentNotRegisteredException: 'The
requested service 'IMyInterface' has not been registered. To avoid
this exception, either register a component to provide the service,
check for service registration using IsRegistered(), or use the
ResolveOptional() method to resolve an optional dependency.'
One thing to note is that I am accessing the AutoFac builder inside a static class. I have made sure that the autofac builder instance is only created once.
UPDATE it looks like I have to call builder.Build() after registering the instance. I guess all instances and types have to be registered and after that you call Build(). So it's not really possible to register more instances after that.
You are not building your Autofac container after registering.
builder.RegisterInstance(myMockObject).As<IMyInterface>();
// Create the DI container
var container = builder.Build();
var test = container.Resolve<IMyInterface>();

.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<>));

Simple Injector Container Fails To Register Web API Controller From Pluggedin External Library

The issue was noticed with the call to the extension method container.RegisterWebApiControllers(GlobalConfiguration.Configuration) on the container that's supposed to register the web api controller with the container but didn't. Please note that the web api controllers are defined in a different class library project and plugged at application start up using a custom IAssembliesResolver type.
public static class SimpleInjectorWebApiInitializer
{
public static void Initialize()
{
var container = new Container();
GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver),
new ApiAssemblyResolver());
InitializeContainer(container);
container.RegisterWebApiControllers(GlobalConfiguration.Configuration);
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
}
I even tried to call to get the ControllerTypes manually to see what was going on using the code below but it never triggered the GetAssemblies method neither did it return any ControllerTypes.
var controllerTypes = httpConfigServicesContainer.GetHttpControllerTypeResolver()
.GetControllerTypes(
GlobalConfiguration.Configuration.Services.GetAssembliesResolver());
I am almost pulling out all my hair as I can't seem to see what I am doing wrong. Thanks for your help in advance.
It's hard to be very specific, but here's a list of things that might be causing your controllers to not be registered:
Your ApiAssemblyResolver does not return the assembly that holds the controllers.
That assembly is a dynamic assembly (meaning that Assembly.IsDynamic returns true). Web API will skip dynamic assemblies.
The controller types are internal. Web API only uses public types.
The controller types are not classes (but structs).
Their type name does not end with "Controller"
They don't implement IHttpController.
Your IAssembliesResolver isn't registerd correctly (perhaps you are missing a binding redirect, causing your application to reference two versions of Web API). Tip: Check what for type GlobalConfiguration.Configuration.Services.GetAssembliesResolver() actually resolves.
Also try the following:
var controllerTypes = httpConfigServicesContainer.GetHttpControllerTypeResolver()
.GetControllerTypes(new ApiAssemblyResolver());
Does this result in any controllers? In that case, there is probably something miss with the call to Replace(typeof(IAssembliesResolver).
The solution lay in the order in which configuration calls were made. I moved every call for configuration involving the IOC container into the Application_Start method of Global.asax file.
Before making the call to GlobalConfiguration.Configure(WebApiConfig.Register),
I had already called
GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver), new ApiAssemblyResolver())
to replace the default assemblies resolver. I finally placed the other container web api configuration settings after every other configuration call and it started working like a charm! I.e
var apiIOCContainer = new Container();
SimpleInjectorWebApiInitializer.InitializeContainer(apiIOCContainer);
apiIOCContainer.RegisterWebApiControllers(GlobalConfiguration.Configuration);
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(apiIOCContainer);

How to inject arguments when using lazy types?

I have a Translation class that takes in ITranslationService as its argument. How do I inject translation service when registering the Lazy<Translation> type? This is what I've done so far, but no luck.
public class Translation
{
public Translation(ITranslationService translationService)
{
// code here
}
}
container.RegisterType<ITranslationService, TranslationService>();
container.RegisterType<Lazy<Translation>>(new InjectionConstructor(typeof(ITranslationService)));
Error message when trying to resolve Lazy<Translation> type:
The lazily-initialized type does not have a public, parameterless
constructor
First of all, Unity 3 now supports resolving Lazy<T>(See What's New section), so you don't need to do anything special, just register ITranslationService and you will be able to resolve Lazy<Translation>.
So the following only applies to Unity 2.
You can install this nuget extension from Piotr Wlodek. You will then need to enable it using:
container.AddNewExtension<LazySupportExtension>();
You will then be able to resolve Lazy<T> objects:
var lazy = container.Resolve<Lazy<Translation>>();
And the actual Translation object will not be constructed until you call lazy.Value.
If neither getting Unity3 nor that extension is an option, you could still try to manually configure Unity2 and resolve those objects.
In your case, you need to use one of the constructors in Lazy<T> that receives a Func<T> as parameter. (That is a function with no parameters that returns an instance of T, or Translation in your case).
You can use an InjectionFactory when registering Lazy<Translation> in Unity, which is a factory method that constructs a Lazy<Translation> object. The lazy object will receive in its constructor an initialization function that uses Unity to resolve the Translation:
container.RegisterType<Lazy<Translation>>(
new InjectionFactory(c => new Lazy<Translation>(() => c.Resolve<Translation>()) ));
Unity supports Lazy<T>, but you have to configure it:
unityContainer.AddNewExtension<LazySupportExtension>();
then don't do
container.RegisterType<Lazy<Translation>(new InjectionConstructor(typeof(ITranslationService)));
but instead do:
container.RegisterType<Translation>();
And use it as follows:
unityContainer.Resolve<Lazy<Translation>>();
For any one using MVC or Web API projects, just install the relevant Unity Bootstrapper Nuget package i.e Unity.AspNet.WebApi for Web API, and Unity.Mvc for MVC.
And then register your types as you normally would, either in code or through a config file. The Lazy instances will be injected automatically
private Lazy<IMapService> _mapService;
public HomeController(Lazy<IMapService> mapService)
{
//Lazy instance is injected automatically.
_mapService = mapService
}

Categories

Resources