AsImplementedInterfaces() registering too much
I understand that AsImplementedInterfaces() registers all implemented interfaces, however this is sometimes too much. Take for instance, the FluentValidation library, you may scan with the following:
.AddClasses(c => c.AssignableTo(typeof(IValidator<>))).AsImplementedInterfaces()
This will pickup all the AbstractValidator<T> implementations you have, but this class implements the following interfaces:
IValidator<T>
IEnumerable
IEnumerable<IValidationRule>
IValidator
Which means you get entries for all of these, not just the desired IValidator<T> and I need to do the following to get rid of the superfluous entries:
services.RemoveAll<IValidator>();
services.RemoveAll<IEnumerable>();
services.RemoveAll<IEnumerable<IValidationRule>>();
Is there any way to do this without having to remove the extra entries afterwards?
Is there any way to do this without having to remove the extra entries afterwards?
Not really. You either have to filter them out using a custom implementation of AsImplementedInterfaces (it's pretty straight forward):
https://github.com/khellang/Scrutor/blob/4bb8ff4d3f936e02f11d9ebcabc68bd424728411/src/Scrutor/ServiceTypeSelector.cs#L50-L55
Or remove the extra entries like you're already doing.
I guess we could look at automatically filtering out IEnumerable interfaces as those doesn't make much sense to register (to me). The problem is that it would be a breaking change.
Can I ask why you care about the extra entries? Just for debugging purposes, or?
I'm experimenting with shifting DI containers and the background noise makes it impossible to know what is going on
I've noticed the same behavior with FluentValidation library, Scrutor adds a lot of unnecessary stuff to the container.
It would be great to have some way of fine-tunning AsImplementedInterfaces setup or even make it more extensible.
Another point is that there are good reflection extensions that could be exposed to be used inside AddClasses, like IsAssignableTo, for instance.
I think it makes sense to exclude IEnumerable (as that has built-in semantics in the container), but other than that, I think you'll have to pass your own "rules" in the form of a delegate passed to As(Func<Type, IEnumerable<Type>> selector). That should let you customize the types however you want.
Just checking in with this, is there a modern way to do this or is this thread still correct?
I came across this issue for a different reason: I am refactoring our code and moving some services to dotnet from Framework but I just get an exception: System.TypeLoadException HResult=0x80131522 Message=Could not load type 'System.Web.Hosting.IRegisteredObject' from assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
I do not actually need to register IRegisteredObject but it also seems like there is no way to tell Scrutor to ignore this interface. I can do it for classes but it does not seem as though I can do it for interfaces.
I do not actually need to register IRegisteredObject but it also seems like there is no way to tell Scrutor to ignore this interface.
@THammond9 There is. It's mentioned as a workaround in this issue.
I think a short-term non-breaking fix for this issue would be to introduce an overload of AsImplementedInterfaces that takes a filter predicate so people could filter out what they don't want.
If anyone wants to send a PR before I get to it, that would be cool 😎
I've added an overload to AsImplementedInterfaces that lets you filter the interfaces you'd like to register using a predicate and pushed it to NuGet as part of v4.1.0 😄 I'll leave the issue open to track the breaking change of filtering out some common types by default,
Is there a final example of how to register all of the validators for the FluentValidation library with Scrutor? Which interfaces need to be filtered and how to specify the filter?
For others looking at this, the following is not the most elegant solution but it works:
.AddClasses(classes => classes.AssignableTo(typeof(IValidator<>)))
.AsImplementedInterfaces(x => x != typeof(IEnumerable<IValidationRule>) && x != typeof(IValidator) && x != typeof(IEnumerable))
.WithScopedLifetime());
I've added a filter for IEnumerable and IEnumerable<T> for upcoming major version, as I can't imagine why people would want to register those service types (I'm not even sure if/how it would work).
I really don't want to get into the business of maintaining a list of types that should or shouldn't be registered in the container. The overload taking Func<Type, bool> lets people make these decisions themselves 😄