Coder Social home page Coder Social logo

yeah69 / mrmeeseeks.die Goto Github PK

View Code? Open in Web Editor NEW
6.0 5.0 0.0 19.74 MB

Compile-time dependency injection container which requires minimal configuration

License: MIT License

C# 100.00%
csharp-sourcegenerator csharp di-container dotnet source-generator source-generators

mrmeeseeks.die's Introduction

Welcome to MrMeeseeks.DIE

Gitter SOQnA

DIE is a secret agency organized by a bunch of Mr. Meeseekses. Its goal is to gather the information necessary to resolve your dependencies. Therefore, …

The acronym DIE stands for Dependency Injection DIE

Let the secret agency DIE compose these info in order to build factory methods which create instances of types of your choice.

Introduction

MrMeeseeks.DIE (in this documentation just referred to as DIE) is a compile-time dependency injection container for .Net. As such it generates factory methods which create instances that you need. Instead of relying on reflection the generated code uses the good old new operator to create instances like you would probably do yourself if you'd create a pure DI container.

Nuget

The easiest way to use DIE is to get it via nuget. Here is the package page:

https://www.nuget.org/packages/MrMeeseeks.DIE/

Either search for MrMeeseeks.DIE in the nuget manager of the IDE of your choice.

Or call the following PowerShell command:

Install-Package MrMeeseeks.DIE

Or manually insert the package reference into the target .csproj:

<PackageReference Include="MrMeeseeks.DIE" Version="[preferrably the current version]" />

Or manually add the package reference to the target .csproj:

<PackageReference Include="MrMeeseeks.DIE" Version="[preferrably the current version]" />

Characteristics Of DIE

  • Compile-Time Code Generation
    • Incomplete configurations will most likely result in a failed build
  • Unambiguousness
    • Container doesn't resolve ambiguity through assumptions
    • Configuration features to resolve ambiguities
  • Convenience
    • Default behaviors designed to reduce the amount of configuration required
    • Optional marker interfaces can be used for configurations
    • Mass configuration (e.g., register all implementations with a single configuration)
  • Flexibility
    • Allows opt-in configuration style
    • Allows opt-out configuration style
  • Feature richness
    • Scoping
    • Async support
    • Generics support
    • User-defined elements (factories, custom parameters, …)
    • Generated factories (Func<…>, Lazy<…>)
    • Decorators & Composites
    • Collection injections (IEnumerable<…>, IAsyncEnumerable<…>, IList<…> and many more)
  • Maximum transparency
    • Only your configuration code needs to know about DIE
    • The rest of your code base can remain oblivious

Documentation

Please visit https://die.mrmeeseeks.dev/ for a documentation.

mrmeeseeks.die's People

Contributors

dieter-enns-deepl avatar yeah69 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

mrmeeseeks.die's Issues

Convenience/optimization idea: Analyze the call-sides of the create functions and gather which type parameters are used. Create create functions accordingly

  • For example, if there is only one call Create<int, string>(), then instead of having the inner functions all generic they can just be like the ordinary functions for closed types. The entry function can then switch the known type parameter combinations.
  • Keep in mind that the call side can still inject open type parameters. Hence, we can't drop generic create functions, even if we do this optimization/convenience feature

Shared code generation for partial base scope configuration classes

Problem Statement

In Container configurations that use many custom scope configuration, that differ only in some details, the code generation duplicates a lot of code.

Idea

Identify partial base classes of these custom scopes and generate the shared part there for all.

Support for nested types

Check & implement support for nested types.

Specifically look into generics. For example:

internal class Foo<T0>
{
    internal class Bar<T1> where T1 : T0 {}
}

That is, how to support nested types, if the containing type has generic type parameter that are used in the nested type.

Refactor TransientScope disposal

When disposed early the TransientScope currently cannot remove itself from the disposal in the container. That's because the container - like all other scopes - has a ConcurrentBag for IDisposable tracking which doesn't support selective removal.

  • Use a suitable alternative to the ConcurrentBag
  • On early disposal remove the TransientScope from the disposal tracking of the container
  • Test disposal

Inheriting container/scope classes

Currently it isn't explicitly forbidden for the container and scope classes to inherit from other classes. However, because the GetMembers-call from a ITypeSymbol would only return the direct members of the type not the inherited, they'll be ignored. That means there is no support yet to share configuration parts by inheriting from some base classes,

This ticket is about investigating and implementing such a support.

Ignore inaccessible constructors

Currently, the DIE's constructor selection doesn't filter inaccessible consctructors (private and/or internal without appropriate InternalsVisibleToAttribute). That may lead to uncompilable generated code.

Eagerly dispose created managed disposables if creation is aborted with an exception

If an exception occurs during the creation of a component, all disposable (transitive) dependencies which are already created will stay cached in the container (or scope) until it gets disposed. As long as they'll eventually get disposed when the container (or the scope) will be disposed, this behavior is problematic only in niche cases and rarely critical.

However, fixing this behavior is still necessary in order to be completely correct.

After fix: managed disposables created for a component which's creation aborts (due to an exception) should be disposed eagerly right after the occurrence of the exception.

Support abstractions as ranged instances

Problem Statement

Currently only implementation types can be assigned to be a ranged instance (container or scope level). In some situations this turns out to be an issue, like

  • when an instance from a user-defined factory which has an abstraction type should be treated as a ranged instance. That just not possible right now.
  • or when passing scoped dependencies from a parent scope to a child scope. In such cases, the workaround would be to pass the concrete implementation instead of the abstraction type. However, that is inconvenient when it is preferred to work with abstractions.

Idea

Treat abstractions as ranged instances as well. This should be automatically possible for all cases where an abstraction has a single implementation type chosen or a user-defined factory. Then instead of having all the ranged instance overhead from the implementation-based ranged instances, just delegate via auto-property to the backing implementation-based ranged or user-defined factory.

Struct-Support

  • Per default ignore parameterless constructor if another one exists
  • Ignore structs for disposal completely (always transient)

Optimization: Merge abstraction and implementation node to one

In hindsight there is no need to technically distinguish between these two. Merging would optimize the code generation of code bases which use interfaces extensively.

Key points:

  • Remove abstraction node
  • Instead set target type of implementation node to abstraction type (instead of the implementation type)

Generated Code Optimization: Remove redundant functions

There is potential for optimizing the size of the generated code by resolving redundancies with generated functions.

An example:

  • The implementation class Foo gets several create-functions generated, because on different occasions different sets of overrides are active
    • One for overrides of type string & int
    • The other for string & long
  • Now, after building the resolution graph it can turn out that in neither case int nor long is used at all
  • So if we remove the override of type int on the one function and long on the other, then both of them are semantically equal.
  • So one can replace the other

Such an optimization would mean to at least optimizing a half of the code for such cases.

Nullability

  • Nullalbe/optional type: multiple constructory

Open generic Create functions

It should be possible to resolve [CreateFunction(typeof(Class<>), "Create"] and other open generic types (like interfaces or multiple generic parameters and so on).

On surface level, DIE "just" needs to be able to generate Create-functions with generic type parameters which are then passed accordingly.

Started implementation on branch feature/open_generic_creates. As proof of concept it looks promising so far. Still to do:

  • support type parameters with constraints
  • support type parameters for custom property/constructor/initializer parameter overloads
  • multi functions (enumerable based)
  • check whether adjustments needed for keyed injections
  • generic ranged instances should be backed by a dictionary (type to instance) instead of single instance
    • Reason being: Single instance per unique combination of filled type parameters. The actually used type parameters are
    • single instance per closed generic type
  • remove AsyncTypeFullName from FunctionNodeBase if not needed anymore
  • Don't allow implementations for abstractions where the constraints for open type parameters are less strict
    • the given type parameter might not fulfill the constraints of the implementations; not possible to check on generation-time
    • If allowed it might lead to uncompilable generations

Likely won't be feasible

  • support for user-defined factories with type parameters
    • Impossible to decide in compile time which type parameter to chose if the current context has several matching type parameters (contrary, the custom parameter overloads have at least the name to match and determine the right type)
    • Might become tricky to explain the users that generic user-defined factories can only be used with open generic type parameters, but explicitly not declare a custom factory for arbitrary types as fallback
  • void functions (i.e. initialized instances)
    • only has a hypothetical benefit (i.e. no known use case yet)
    • technically not trivial (field variables of the initialized instances would need to be generic or casted; feature wouldn't be allowed for scopes or very complicated to use and implement)

Developer Notes:

Current approach that I work on:

  • For each Create-function for each open generic type raise a type parameter with same but a controlled name (e.g. T_0_0)
  • Create a SymbolEqualityComparer that ignores the name on type parameters for the Create-function caches to use
    • Constraints of specific type parameters may contain type parameters as well (even such that don't exist in the type itself)
  • For Create-function calls and Create-functions implement a logic that extracts type parameters in the same way
    • Constraints of specific type parameters may contain type parameters as well (even such that don't exist in the type itself)

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.