• This repository has been archived on 16/May/2022
  • Stars
    star
    176
  • Rank 216,987 (Top 5 %)
  • Language
    C#
  • License
    MIT License
  • Created over 7 years ago
  • Updated almost 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Extremely Fast Dependency Injection Library.

MicroResolver

Extremely Fast Dependency Injection Library.

Features

MicroResolver is desgined for performance. I've released two fastest serializers ZeroFormatter and MessagePack for C#, this library is using there dynamic il code generation technique.

MicroResolver achived fastest at some tests in IoCPerformance benchmark - Singleton(Multithread), Combined(Multithread), Property(Singlethread, Multithread) and other result also top level. This benchmark is nongeneric test(use (object Resove(Type type)), MicroResolver is focused to optimize for generic method(T Resolve<T>()), if using it, faster than other libraries.

Support Features - Consturctor Injection, Field Injection, Property Injection, Method Injection, Collection resolver and Three lifetime support(Singleton, Transient and Scoped).

Quick Start

Install from NuGet(for .NET Framework 4.6, .NET Standard 1.4)

// Create a new container
var resolver = ObjectResolver.Create();

// Register interface->type map, default is transient(instantiate every request)
resolver.Register<IUserRepository, SqlUserRepository>();

// You can configure lifestyle - Transient, Singleton or Scoped
resolver.Register<ILogger, MailLogger>(Lifestyle.Singleton);

// Compile and Verify container(this is required step)
resolver.Compile();

// Get instance from container
var userRepository = resolver.Resolve<IUserRepository>();
var logger = resolver.Resolve<ILogger>();

Notice: MicroResolver requests call Compile before use container.

InjectionAttribute and Resolve Collection

MicroResolver can resolve all public and private properties, fields, constructor and methods. Inject target have to mark [Inject] attribute.

public class MyType : IMyType
{
    // field injection

    [Inject]
    public IInjectTarget PublicField;

    [Inject]
    IInjectTarget PrivateField;

    // property injection

    [Inject]
    public IInjectTarget PublicProperty { get; set; }

    [Inject]
    IInjectTarget PrivateProperty { get; set; }

    // constructor injection
    // if not marked [Inject], the constructor with the most parameters is used.
    [Inject]
    public MyType(IInjectTarget x, IInjectTarget y, IInjectTarget z)
    {

    }

    // method injection

    [Inject]
    public void Initialize1()
    {
    }

    [Inject]
    public void Initialize2()
    {
    }
}

// and resolve it
var v = resolver.Resolve<IMyType>();

Inject order is Constructor -> Field -> Property -> Method.

If register many types per type, you can use RegisterCollection and Resolve<IEnumerable<T>>.

// Register type -> many types
resolver.RegisterCollection<IMyType>(typeof(T1), typeof(T2), typeof(T3));

resolver.Compile();

// can resolve by IEnumerbale<T> or T[] or IReadOnlyList<T>.
resolver.Resolve<IEnumerable<IMyType>>();
resolver.Resolve<IMyType[]>();
resolver.Resolve<IReadOnlyList<IMyType>>();

// can resolve other type's inject target.
public class AnotherType
{
    public AnotherType(IMyType[] targets)
    {
    }
}

Scoped

Lifetime.Scoped is usually the same as Transient but within BeginScope it behaves like a singleton in the scope.

// sample type of check scope
public class MyClass : IMyType, IDisposable
{
    public MyClass()
    {
        Console.WriteLine("Created");
    }

    public void Dispose()
    {
        Console.WriteLine("Disposed");
    }
}

// -----------

var resolver = ObjectResolver.Create();
resolver.Register<IMyType, MyClass>(Lifestyle.Scoped);
resolver.Compile();

using (var coResolver = resolver.BeginScope(ScopeProvider.Standard))
{
    var i1 = coResolver.Resolve<IMyType>(); // "Created"
    var i2 = coResolver.Resolve<IMyType>();

    Console.WriteLine(Object.ReferenceEquals(i1, i2)); // "True" -> same instance

    // if scope end and instantiated types is IDisposable, called Dispose.
} // "Disposed"

ScopeProvider has three option in default. ScopeProvider.Standard, ScopeProvider.ThreadLocal and ScopeProvider.AsyncLocal. If needs custom scope, you can create own ScopeProvider.

public class MyScopeProvider : ScopeProvider
{
    public override void Initialize(IObjectResolver resolver)
    {
        // when called from BeginScope().
    }

    protected override object GetValueFromScoped(Type type, out bool isFirstCreated)
    {
        // called per Resolve<T>.
    }
}

Performance Technique - Dynamic IL Inlining

Everyone creates dynamic code generation for optimize performance. But if target is complex type?

// sample of complex dependency type
public class ForPropertyInjection : IForPropertyInjection
{
    [Inject]
    public void OnCreate()
    {
    }
}

public class ForConstructorInjection : IForConsturctorInjection
{
    [Inject]
    public IForFieldInjection MyField;
}

public class ComplexType : IComplexType
{
    [Inject]
    public IForPropertyInjection MyProperty { get; set; }

    public ComplexType(IForConsturctorInjection instance1)
    {

    }

    [Inject]
    public void Initialize()
    {
    }
}

// for example, how to resolve ComplexType?
var v = resolver.Resolve<IComplexType>();

The following way is not slow, but it is not fastest.

// This is `slow` example of complex type resolve
static IComplexType ResolveComplexType(IObjectResolver resolver)
{
    var a = resolver.Resolve<IForConsturctorInjection>();
    var b = resolver.Resolve<IForPropertyInjection>();

    var result = new ComplexType(a);
    result.MyProperty = b;
    result.Initialize();

    return result;
}

MicroResolve choose inlining code generation, all dependencies are analyzed and inlined at compile time.

// This is actual code generation of MicroResolver, all dependency is inlined at il code generation
static IComplexType ResolveComposite()
{
    var a = new ForConstructorInjection();
    a.MyField = new ForFieldInjection();
    var b = new ForPropertyInjection();
    b.OnCreate();

    var result = new ComplexType(a);
    result.MyProperty = b;
    result.Initialize();

    return result;
}

Performance Technique - Generic Type Caching per resolver

The generated code is cached. And how to retrieve it? ConcurrentDictionary? Dictionary? They are slow. MicroResolve choose generic type caching.

This is ObjectResolver signature.

public abstract class ObjectResolver
{
    public abstract T Resolve<T>();
}

If called ObjectResolver.Create, generate dynamic inherited type.

public class ObjectResolver_Generated : ObjectResolver
{
    public override T Resolve<T>()
    {
        // too simple, of course simple is fastest.
        return Cache<T>.factory();
    }

    Cache<T>
    {
        // generated Func<T> code is see 'Dynamic IL Inlining' section.
        public Func<T> factory;
    }
}

The code path is too short, it means no overhead.

But generated container can not remove. This is a design constraint.

Performance Technique - Fast NonGeneric lookup table

Type Caching is require to use generics method. But often framework requests nongeneric type.

// fastest
resolver.Resolve<T>();

// slower but framework requests this method
(T)resolver.Resolve(type);

MicroResolver use fast type lookup by own fixed hashtable.

// buckets item
struct HashTuple
{
    public Type type;
    public Func<object> factory;
}

// simplest hash table(fixed-array chaining hashtable)
private HashTuple[][] table;

// register - Func<T> -> Func<object> by delegate covariance
table[hash][index] = new Func<object>(Cache<T>.factory);

// simplest == fastest lookup
public object Resolve(Type type)
{
    var hashCode = type.GetHashCode();
    var buckets = table[hashCode & tableMaskIndex]; // table size is power of 2, fast lookup

    // .Length for loop can remove array bounds check
    for (int i = 0; i < buckets.Length; i++)
    {
        if (buckets[i].type == type)
        {
            return buckets[i].factory();
        }
    }

    throw new MicroResolverException("Type was not dound, Type: " + type.FullName);
}

non-generic lookup is slower than generic but still fast.

Author Info

Yoshifumi Kawai(a.k.a. neuecc) is a software developer in Japan.
He is the Director/CTO at Grani, Inc.
Grani is a mobile game developer company in Japan and well known for using C#.
He is awarding Microsoft MVP for Visual C# since 2011.
He is known as the creator of UniRx(Reactive Extensions for Unity)

Blog: https://medium.com/@neuecc (English)
Blog: http://neue.cc/ (Japanese)
Twitter: https://twitter.com/neuecc (Japanese)

License

This library is under the MIT License.

More Repositories

1

UniRx

Reactive Extensions for Unity
C#
7,033
star
2

MessagePack-CSharp

Extremely Fast MessagePack Serializer for C#(.NET, .NET Core, Unity, Xamarin). / msgpack.org[C#]
C#
4,721
star
3

ZeroFormatter

Infinitely Fast Deserializer for .NET, .NET Core and Unity.
C#
2,381
star
4

Utf8Json

Definitely Fastest and Zero Allocation JSON Serializer for C#(NET, .NET Core, Unity, Xamarin).
C#
2,355
star
5

LINQ-to-GameObject-for-Unity

LINQ to GameObject - Traverse GameObject Hierarchy by LINQ
C#
798
star
6

MarkdownGenerator

Generate markdown from C# binary & xml document for GitHub Wiki.
C#
192
star
7

LightNode

Micro RPC/REST Framework built on OWIN
C#
181
star
8

linq.js

LINQ for JavaScript.
JavaScript
128
star
9

SerializableDictionary

SerializableCollections(SerializableDictionary, SerializableLookup, SerializableTuple) for Unity
C#
126
star
10

EtwStream

Logs are event streams. EtwStream provides In-Process and Out-of-Process ObservableEventListener. Everything can compose and output to anywhere by Reactive Extensions.
C#
125
star
11

PhotonWire

Typed Asynchronous RPC Layer for Photon Server + Unity
C#
118
star
12

AsyncOAuth

Portable Client Library and HttpClient based OAuth library, including all platform(for PCL).
C#
101
star
13

HyperMapper

An alternative to AutoMapper, Hyper fast object-to-object mapper built on fastest serializer technology.
C#
92
star
14

LINQ-to-BigQuery

LINQ to BigQuery is C# LINQ Provider for Google BigQuery. It also enables Desktop GUI Client with LINQPad and plug-in driver.
C#
85
star
15

ChainingAssertion

Method Chaining base UnitTesting Extension Methods and Dynamic Private Accessor for MSTest, NUnit, xUnit.net.
C#
75
star
16

FastHashtable

Infrastructure for high performance code.
C#
64
star
17

Open-on-GitHub

Visual Studio Extension for opening files on GitHub, GitLab, Gitea, Bitbucket and AzureDevOps (dev.azure.com, visualstudio.com, tfs)
C#
56
star
18

MySqlSharp

Extremely Fast MySQL Driver for C#, work in progress.
C#
55
star
19

NotifyPropertyChangedGenerator

Roslyn Analyzer/Generator for avoid boring boilerplate INotifyPropertyChanged implementation.
C#
52
star
20

DatadogSharp

Yet another C# Datadog client that supports DogStatsD and APM.
C#
41
star
21

ReMotion

Hyper Fast Reactive Tween Engine for Unity
C#
39
star
22

DynamicJson

dynamic json structure for C# 4.0.
C#
37
star
23

RespClient

RespClient is a minimal RESP(REdis Serialization Protocol) client for C# and PowerShell.
C#
32
star
24

OwinRequestScopeContext

Owin Middleware it is possible to RequestScopeContext like HttpContext.Current but no dependent System.Web.
C#
29
star
25

Resume

My Resume
21
star
26

Blog2

C# Static Site Generator and Hosting for neue.cc
C#
16
star
27

ObserveEveryValueChanged

Voodoo Magic for WPF.
C#
13
star
28

MemcachedTranscoder

C# Memcached Transcoders
C#
8
star
29

AnonymousComparer

Lambda compare selector for Linq
C#
7
star
30

Owin.RedisSession

Redis Session Provider for Owin.
C#
5
star
31

neuecc

5
star
32

MSBuildAssemblyLoadIssue

C#
4
star
33

DbExecutor

Simple and Lightweight Database Executor
C#
4
star
34

EsaClient

esa.io client for .NET Standard.
C#
3
star
35

XStreamingReader

Xml Stream(XmlReader) to IEnumerable for Windows Phone(memory save) or parse large Xml.
C#
3
star
36

ImplicitQueryString

Magic for QueryString parsing.
C#
2
star
37

ReactiveOAuth

OAuth library for .NET Framework 4 Client Profile, Silverlight 4 and Windows Phone 7.
C#
2
star