GithubHelp home page GithubHelp logo

keboo / autodi Goto Github PK

View Code? Open in Web Editor NEW
96.0 8.0 17.0 4.72 MB

Dependency injection made simple.

License: MIT License

C# 99.52% PowerShell 0.48%
fody di-container ilweaving dependency-injection dependency-resolution hacktoberfest

autodi's Introduction

AutoDI

Have a question? Join the chat at https://gitter.im/AutoDIContainer/Lobby

NuGet Status NuGet Status NuGet Status

Build Status

AutoDI is both a dependency injection container and a framework to simplify working with dependency injection (DI). It is built on top of the Microsoft.Extensions.DependencyInjection.Abstractions library, and it works very similar to the way ASP.NET Core handles dependency injection.

AutoDI delivers delivers two excellent features:

  1. It is built on Microsoft.Extensions.DependencyInjection.Abstractions library to bring the same flavor of dependency injection enjoyed on ASP.NET Core to other platforms (.NET Framework, WPF, Xamarin, UWP, etc).
  2. It generates container registrations at compile time using conventions (of course you can also add more at run-time as well).

See the wiki for more details or check out the Quick Start page to get up and running fast.

Why do I need this?

The goal of this library is to make dependency injection as simple as adding a NuGet package.

In addition to standard constructor dependency injection, it also allows optional constructor parameters that will be resolved when the constructor is invoked.

In a typical DI setup, you can often find objects needing to take in dependencies that the object itself is not using, but needs them to pass into some other object. The bigger and deeper the object model gets, the worse this becomes. As a specific example, imagine you are inside of class Foo and wish to create an instance of Bar, but Bar has a dependency on IService. In many cases, one of several options can be used:

  • Pass a facade to either the DI container or a factory that can create a Bar
  • Add a dependency on Foo to take in an IService so that when it needs to create a Bar it can simply pass the instance to Bar's constructor.

Though both these options work, they add additional effort. After all, in many cases, when we want a Bar we simply call new Bar();. AutoDI lets you write exactly that, and moves the dependency resolution inside of Bar's constructor. This dependency resolution is then forwarded to the DI container to resolve it.

Does it work with <my favorite DI container / framework>?

Probably. Take a look at the AutoDI.Examples repository for samples. There are examples of adding support for other DI containers as well as simple examples for most frameworks.

Don't see your favorite listed? I accept PRs.

Icon

Needle designed by Austin Andrews from Material Design Icons

autodi's People

Contributors

adamskt avatar alex-451 avatar dependabot[bot] avatar hymccord avatar kashifsoofi avatar keboo avatar kennethwhite avatar marsmsj avatar ovidiucaba avatar paritoshmmmec avatar rubvardanyan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

autodi's Issues

Merge AutoDI and AutoDI.Container

This will be a breaking change, but since the AutoDI.Container depends on AutoDI to function properly it would be better to have a single weaver that can be configured rather than two weavers.

This will mean the AutoDI weaver will need a configuration switch to disable generation of the container. This switch should default on however, since the goal is to make AutoDI a single install nuget and things just work the way you expect solution. Integrating with your own DI container already requires coding.

GitLink does not appear to be working properly

I think the issue is the lack of pdb file being in included in the nuget.

https://oren.codes/2015/09/23/enabling-source-code-debugging-for-your-nuget-packages-with-gitlink/

From Gitter chat:

Geert van Horrik
the readme references the unstable packages (develop branch)
it's on the list to be released as stable, but I just have too much to do at the moment to release it as stable, maybe next week

Andrew Arnott
install the v3 prerelease to get msbuild integration for free. Yes, it works with .NET SDK projects.

Add iterator support

It would be nice to be able to write LINQ queries against the container to get all items of a given type (such as IDisposable).

By default these should not create instances; rather they should only return the instances that have already been created. Attaching the CreateInstances() into the expression would for creations of instances.

Perhaps something like this:

IContainer map = ...;

IEnumerable<IDisposable> disposableSingletons = map.Singletons().OfType<IDisposable>();
//Typically you would not need to add CreateInstances() to the Singleton enumeration (because the instances are always created). However because the singletons are not created until AFTER the setup method you may need to add it if you want to use this inside of the SetupMethod. (see issue 49)

IEnumerable<IDisposable> aliveDisposableLazySingletons = map.LazySingletons().OfType<IDisposble>();

IEnumerable<IDisposable> allDisposableLazySingletons = map.LazySingletons().CreateInstances().OfType<IDisposble>();

IEnumerable<IDisposable> createdWeakTransients = map.WeakTransients().OfType<IDispoable>();

IEnumerable<IDisposable> allWeakTransients = map.WeakTransients().CreateInstances().OfType<IDispoable>();

//For now simply ignore transient instances since working with them would require tracking all instances generated.

No way to create a singleton that depends on run-time dependencies

Because the singletons are evaluated before any run-time dependencies can be added there is no way for AutoDI to have a Singleton with a run-time dependency.

I think the best way to handle this is to not invoke the Setup Method until after the container has been initialized as set as the default resolver. This allows for code inside of the Setup method to act the on the ContainerMap.

Calling the SetupMethod from inside of the constructor means that it is not set as the default resolver and thus you cannot resolve dependencies from the container.

Allow for passing parameters to IDependencyResolver.Resolve

Currently there is no way to pass a parameter to the IDependencyReoslver.Resolve method. Many DI containers support registering and resolving dependencies with keys or other custom parameters. Ideally these parameters would be specified on the DependencyAttribute

Update release notes in all nuspec files

Rather than maintaining release notes in the nuspec files we should remove the <releaseNotes> tag and append the following to all of the <description> tags.

Release notes: https://github.com/Keboo/AutoDI/wiki/Release-Notes

Code signing on dependent assemblies is likely broken

This needs testing to confirm.

When merging AutoDI and AutoDI.Container the code for processing dependent assemblies for types also got merged. In some cases this is fine, as it lets both work together to build up mappings for types. However, Fody handles re-signing the assembly that the weaver processed; which is great. However if we are going to also modify the dependent assemblies we likely need to handle re-signing those assemblies as well.

This might also be solved by having a weaver that runs on the assemblies after AutoDI to force Fody to do the signing for us.

AutoFac example

It would be nice to have a simple example using AutoFac, similar to the StructureMap and MS thirdParty examples.

A simple dotnet core console application similar to the projects here and here. The AutoFac example should also be in the /Examples/ThirdParty directory (like those two examples).

Rather than creating a separate project (like what I did with StructureMap.AutoDI), I think it would be cleaner to just create an AutoFac equivelent of ApplicationBuilderMixins directly inside the example dotnet core console application rather than in a separate project. I am assuming we can simply add a reference to the Autofac.Extensions.DependencyInjection package and use the classes in there to get similar results.

Also note that examples are being moved to the wiki so this is where the documentation should go.

Warnings and Errors should log sequence points

Currently the weaver just logs warnings to the output window. This is ok, but it would be better to log with the sequence point so the user can simply click the warning to navigate.

The LogError and LogWarning delegate properties have an additional overload that also logs the sequence point. AutoDI should be calling these overloads and including the sequence points so it is obvious where the problems are located.

At a minimum, all the error/warning messages should indicate at least the file, and enough information so the offending code can be easily located.

Cleanup ThirdParty folder

Right now there is a ThirdParty directory at the root of the repository. This should be merged into the Examples\ThirdParty directory and the appropriate project references updated.

Clean up interaction for type mapping

Right now the config supports lots of regex matching patterns which is great, but is more complicated than necessary.
This change is expected to be a breaking API change. Backward compatibility is not required or expected.
Some suggestions for a better API:

  • Require the pattern to be prefixed with regex: in order to get regex match. Otherwise simple contains style matching with wildcards * will do. Yes this is a breaking change.
  • Avoid making invalid mapping to generic types. Even if the user explicitly request a particular mapping it should be ignored if it results in the target type being an open generic. This should certainly be logged as a warning, but given that open generics cannot be instantiated we should avoid the mapping to prevent a run-time crash.
  • Nested classes are done with '/' rather than the '+' that most developers are used to. Since neither character is valid in C#, all specifying either in the map and type nodes.
  • Allow specifying lifetime in the map directly. Since all of the commands are specified in order, simply allowing last-in-wins should be acceptable.

Use default value of the dependency

Currently the weaver is assuming that null will be the default value of the dependency and comparing against null when determining if the dependency was provided by the caller. Though this will likely be correct in most case the weaver should use the default value specified on the parameter rather than assuming null.

Update README

Right now the README has nothing useful in it.

  • Install via NuGet
  • Setup on DependencyResolver
  • Example using various DI containers
  • Unit testing

Referenced dll not setup properly

When adding AutoDI.Fody nuget package. The AutoDI.dll is set as a reference but it is not marked as copy local. In addition on ClickOnce deployed apps it is setup as a Prerequisite.

Add event to allow for handling the cases when a requested type is not matched

Currently the container simply returns default(T) when it is unable to locate a dependency.

The ContainerMap (also added to IContainer interface) should have an event that is raised when a service type key is not found to allow for a caller to either fill the dependency, log a failure message, or even throw an exception (if that is the desired behavior).

Lower required .NET framework

Currently AutoDI is targeting the latest .NET framework. This make the it less available to people who cannot simply update their .NET version. Change the .NET version to be the lowest possible.

Automatically inject resolver

As part of #20 we want to allow for classes to manually specify that they want an IDependencyResolver or similar. We should pass in the current resolver for this parameter.

Allow for specifying a dependency resolver behavior

There should be an overload to DependencyResolver.Set that takes in an IDependencyResolverBehavior. This interface should have a single member that mirrors DependencyResolver.Get():

IDependencyResolver Get()

This will allow for callers to define additional logic for determining what IDependencyResolver instance gets returned. This will also lay the groundwork for leveraging nested DI containers.

SetupMethod not invoked if it is internal

The setup method only appears to get invoked when it is public.

It should also allow internal as well since it will be in the same module as the generated container.

Implement nested containers.

Working idea 2

//IScope derives from IDisposable so you can clean up and tell it when scope is done.
IScope @global = AutoDI.Scope.Global;
IScope nested = @global.CreateNested();

var controller = nested.Resolve<Controller>();

public interface IService {}
public class Service : IService {}

public class Controller
{
    public Controller([Dependency]IService service = null)
    { }
}

In short remove most of the magic and work exactly like the other containers (at least in terms of nesting, and the code that you write).

Some small bits of magic:

  • If you declare a dependency property of IScope in your constructor it will be populated with either the scope that creates the object, or if none, will be the Global one. This would be an alternative to passing down factories (which you could still do if you wanted).
  • This will take a little investigation, but I think in the case of a class that uses property injection (dumb CLR making sure I don't abuse everything), I have to generate an alternate constructor.
public class Controller
{
    [Dependency]
    private IService Service{ get; }

    public Controller([Dependency]IOther other = null)
    { }
}

=>

public class Controller
{
    [Dependency]
    private IService Service{ get; }

    //Using generated class as last parameter to ensure no collisions.
    public Controller(IScope <scope>, IOther other, <AutoDIGenerated> unused)
    {
        Service = <scope>.Resolve<IService>();
        this(other);
    }

    public Controller([Dependency] IOther other = null)
    { }
}

Cleanup AutoDI.Container.ContainerMap

Currently the API is a bit clunky to work with.

Need to add some cleaner methods for:

  • Adding and removing keys for a given type
  • Removing a given type
  • Adding a new type

Add AutoDI.Container setup method

Currently to modify the container's map at run time requires a reflection call. Though this is fine for testing the mapping in unit tests, it would be much nicer if the weaver would simply looking for a method like this:

[AutoDI.Container.SetupMethod]
private static void ContainerSetup(ContainerMap map)
{

}

The method must be static.
The method must reside in the main module of the assembly with the generated container (this allows for the method to be either public or non-public).
The method must accept a single ContainerMap parameter.
The method must be decorated with the AutoDI.Container.SetupMethod.
The method should be invoked by the generated container after it has added everything to the map. This will allow the caller greater control over the contents of the map. This means that it may be invoked prior to any user code.

In addition it would be nice to clean up the ContainerMap API to make it a bit friendlier to work with.

Add Lazy<> support to AutoDI.Container.

Add support to the AutoDI.Container.ContainerMap when a key is of type Lazy if no key is found, but a key for T is found, build up a new Lazy instance that resolves by retrieving the T from the map.

Add a property to the ContainerMap to disable this feature, it should default to true, however.

Add configuration support to also disable this feature.

Add support for custom behaviors

Right now there are a fixed number of hard coded behaviors. It would be nice if it were possible to declare a custom behavior (in either a dll, or directly inside of the project) and have it run as part of the weaver.

Split into two nuget packages

There should be one package for Fody and one for just the AutoDI lib. This is useful for unit tests that want to test an assembly that is using the AutoDI IL weaver but don't need to take a dependency on Fody itself.

GlobalDI.GetService needs more overloads

The GlobalDI class currently has a single T GetService<T>(object[]) method. This method simply wrapps calls into IServiceProvider.GetService.

The following overloads should be added:

  • T GetService<T>() - this should simply call the original method passing an empty array for the parameters.
  • object GetService(Type serviceType, object[] parameters) - Same as the original method but should call the non-generic version of the IServiceProvider.GetService method.
  • object GetService(Type serviceType) - Should call the method above passing an empty array for the parameters.

Add support for open generics

Current there is no way to register open generics.

Consider a simple case using Microsoft.Extensions.Logging.

Ideally I would like to write the following code

public class MyClass
{
    public MyClass([Dependency] ILogger<MyClass> logger = null) { ... }
}

This poses a problem for registering because we currently only resolve closed generics. What I would like is some way to register a factory method for the open generic.

Something like:

ContainerMap map = ...;
map.AddLazySingleton(genericTypes => GetLogger(genericTypes[0]), new[] {typeof(ILogger<>});


//In ContainerMap
public void AddLazySingleton(Func<Type[], object> genericCreate, Type[] keys) { ... }

During resolution this would change the logic to be:

  • Request dependency of ILogger - Fails to match key
  • Because the requested key is a generic type, attempt to match key of the open generic. Since the opened generic matches, it should assume the delegate is a Func<Type[], object>. After invoking the delegate it should verify that the returned object is of the requested type.

Create AutoDI specific exception type for use in DI class

There are several TODOs in the DI class. When the operations fail it is currently throwing an InvalidOperationException for all failures.
What needs to be done is create three specific exception types to provide more clarity on exactly which issue occurred.

Currently all of the exceptions are stored in the AutoDIException.cs file. All of the created exceptions should derive from AutoDIException.

Remove the TODO comments and replace with the following exceptions in the DI class:
RequiredMethodMissingException - throw this exception in all of the cases where it fails to find the expected method on the generated type.
GeneratedClassMissingException - throw this exception in the case when the generated AutoDI type is not found.
GlobalServiceProviderNotFoundException - throw this exception when the global service provider field is not found on the generated type.

Allow for passing parameters to DependencyResolver.Get

We want to allow passing parameters to to the DependencyResolver.Get() method to allow for more flexibility when determining what resolver to return.
Ideally there will be an attribute that can be used on the constructor or class that will allow for setting additional information that is passed to the method.

IDependencyResolver should support non-generic Resolve

Currently the IDependencyResolver only has a single Resolve method.

It should be possible to also have a non-generic overload.

Currently working around it with this extension method.

public static class AutoDIMixins
{
    public static object Resolve(this IDependencyResolver resolver, Type targetType)
    {
        var methodInfo = typeof(IDependencyResolver).GetMethod(nameof(IDependencyResolver.Resolve));
        methodInfo = methodInfo.MakeGenericMethod(targetType);
        return methodInfo.Invoke(resolver, new object[] { new object[0] });
    }
}

Fix up nugets

Because of casing there are now two "nuget" folders in the repo. This needs to be cleaned up.

The nuspec files need to properly specify a max version. I am thinking that given the number of breaking changes likely to be introduced, it would be best to do something like: version="[2.1,2.2)"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.