aspnet / DependencyInjection

[Archived] Contains common DI abstractions that ASP.NET Core and Entity Framework Core use. Project moved to https://github.com/aspnet/Extensions
Apache License 2.0
874 stars 319 forks source link

Add an IServiceCollection.AddAssembly extensions method #76

Closed lodejard closed 7 years ago

lodejard commented 10 years ago

to assist in clean startup, usage optional by application of course

AddAssembly(assembly) extension method scans for public types

If the public type is creatable (class, non abstract) and a [ServiceDescriptorAttribute], add it to collection

ServiceDescriptorAttribute has a Lifecycle property, default is transient

ServiceDescriptorAttribute has a ServiceType property, default is null, null means add each interfaces.

davidfowl commented 10 years ago

Interesting

david-driscoll commented 10 years ago

We have something like this at work as well.

I'm currently doing something similar for vNext, but I'm also playing around with a preprocess compiler, that emits the appropriate Add method call at compile time, so that at run time, you don't have to iterate over all the types of an assembly.

In addition this allows Diagnostics to be created that can inform the developer that they have added an incorrect implementations, again at compile instead of app startup. In my case I'm using an attribute named ImplementationOfAttribute.

eg...

public interface IService1 {}
public interface IService2 {}

[ImplementationOf(typeof(IService1))] // Flags this as a compile time error, the interface is not implemented
class Service2 : IService2 {}
david-driscoll commented 9 years ago

Any interest in a PR? I have a working example at https://github.com/david-driscoll/DependencyInjection.Annotations

lodejard commented 9 years ago

That's genius! You know, what that actually makes me think is that we probably shouldn't bake any kind of service discovery directly into the base DI library. That seems like a perfect example of an additional package we can tell people to use if this feature is asked for.

We're also working more on preprocessing to enable an application can take a dependency on a package that has ICompileModule implementations in it. So someone wouldn't need to have code directly in their app's subfolder.

david-driscoll commented 9 years ago

There is one more case that I need to support, that's Open Generic registrations, I think I'll end up doing that sometime today.

With preprocessing that's my understanding after talking with @davidfowl on Jabbr a few days ago. I wanted to experiment with Roslyn and "meta progamming" to see what the limits are... they're pretty limitless.

npehrsson commented 9 years ago

Why annotate the class, why not just add it as other DI containers do. For example Unity can resolve types even if has not been registered or having a annotation on it. This is something I miss.

david-driscoll commented 9 years ago

You mean register the class as an implementation of the same (or base) class?

In the case of vNext anyway there are a few classes that are registered as the class (generally an abstract class, but I don't think this is always the case). HttpContext or MvcMarkerService for example.

npehrsson commented 9 years ago

I mean that classes that are not abstract and are having public constructors should be resolved automatically without the need of register them first. They should be resolved as Transient. If one want another behaviour one should register it. This will be convention over configuration.

Antaris commented 8 years ago

I took a different approach to this. As detailed in here http://stackoverflow.com/questions/31119284/getting-interface-implementations-in-referenced-assemblies-with-roslyn.

I wanted to avoid assembly scanning and reflection so I used a compile module to search simply for an instance of an module interface and then generated a type implementation at compile time with references to those modules. Each module returns service descriptors so they own their own service registrations. An arbitrary order can be applied as part of the interface contract.

It can pick up modules defined in compilations and references and the only requirement is to drop on a compile module into the host project.

Let me know your thoughts I can push a more concrete example to Github if need be

taspeotis commented 8 years ago

I'd like this too, please. MEF does this well with new DirectoryCatalog("MyProject.*.dll") which then looks for MEF attributes. I know DNX/CoreCLR has stripped down reflection capabilities (e.g. restrictions on assembly loading ... no Assembly.GetReferencedAssemblies) so I have rolled-my-own in a slightly different way.

(I prefer some sort of control over what is added to the container. If you scan for every type that's trivially constructible through a parameter-less constructor then I think you will pollute the container with a whole bunch of MyProject.Entities.A ... B ... C because entities are frequently POCOs.)

There is one interface and three attributes

Usage is thus:

using System;
using ...;

[assembly: AddScoped(typeof(IFooService), typeof(FooService)]

namespace MyProject {
  internal sealed class FooService : IFooService {
    ...
  }
}

And in Startup.cs

// FromAssembly<TType> resolves the assembly of TType and then calls GetCustomAttributes x 3
// AddMyProject takes care of calling options.FromAssembly with its own type
services.AddMyProject(options => options.FromAssembly<Startup>());

(Whether Assembly.GetCustomAttributes is faster than Assembly.GetTypes().Where(...) is an open question but it feels more direct.)

Note open generics are permitted via [AddWhatever(typeof(IRepo<>), typeof(Repo<>))].

I understand that ASP.NET DI is trying not to be opinionated and compatible with bring-your-own-container but some sort of auto-discovery with built-in ConventionBasedDiscovery (given all or a list of assemblies, add constructible types) and/or AttributeBasedDiscovery (give all or a list of assemblies, reads custom attributes).

I will state again I'm not in favour of blindly scanning all assemblies and types and adding them but it has its place.

khellang commented 8 years ago

I thought I already left this here, but apparently not, so here it goes... My approach; https://github.com/khellang/Scrutor

khellang commented 8 years ago

It doesn't look good for a built-in feature, though:

From https://github.com/aspnet/DependencyInjection/issues/322#issuecomment-157114371:

We don't have any plans to include this functionality in the default container. The default container is deliberately minimalistic. For a full featured container you can use any of the various existing containers.

davidfowl commented 7 years ago

Definitely have a look at https://github.com/khellang/Scrutor for features like this.