/
SimpleContainer.cs
156 lines (126 loc) · 5.6 KB
/
SimpleContainer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using ServiceStack.Configuration;
using ServiceStack.Web;
namespace ServiceStack;
public class SimpleContainer : IContainer, IResolver
{
public HashSet<string> IgnoreTypesNamed { get; } = new();
protected readonly ConcurrentDictionary<Type, object> InstanceCache = new();
protected readonly ConcurrentDictionary<Type, Func<object>> Factory = new();
public object Resolve(Type type)
{
Factory.TryGetValue(type, out Func<object> fn);
return fn?.Invoke();
}
public bool Exists(Type type) => Factory.ContainsKey(type);
public object RequiredResolve(Type type, Type ownerType)
{
var instance = Resolve(type);
if (instance == null)
throw new Exception($"Required Type of '{type.Name}' in '{ownerType.Name}' constructor was not registered in '{GetType().Name}'");
return instance;
}
public IContainer AddSingleton(Type serviceType, Func<object> factory)
{
Factory[serviceType] = () => InstanceCache.GetOrAdd(serviceType, factory());
return this;
}
public IContainer AddTransient(Type serviceType, Func<object> factory)
{
Factory[serviceType] = factory;
return this;
}
public T TryResolve<T>() => (T) Resolve(typeof(T));
protected virtual bool IncludeProperty(PropertyInfo pi)
{
return pi.CanWrite
&& !pi.PropertyType.IsValueType
&& pi.PropertyType != typeof(string)
&& pi.PropertyType != typeof(object)
&& !IgnoreTypesNamed.Contains(pi.PropertyType.FullName);
}
protected virtual ConstructorInfo ResolveBestConstructor(Type type)
{
return type.GetConstructors()
.OrderByDescending(x => x.GetParameters().Length) //choose constructor with most params
.FirstOrDefault(ctor => !ctor.IsStatic);
}
public Func<object> CreateFactory(Type type)
{
var containerParam = Expression.Constant(this);
var memberBindings = type.GetPublicProperties()
.Where(IncludeProperty)
.Select(x =>
Expression.Bind
(
x,
Expression.TypeAs(Expression.Call(containerParam, GetType().GetMethod(nameof(Resolve)), Expression.Constant(x.PropertyType)), x.PropertyType)
)
).ToArray();
var ctorWithMostParameters = ResolveBestConstructor(type);
if (ctorWithMostParameters == null)
throw new Exception($"Constructor not found for Type '{type.Name}");
var constructorParameterInfos = ctorWithMostParameters.GetParameters();
var regParams = constructorParameterInfos
.Select(x =>
Expression.TypeAs(Expression.Call(containerParam, GetType().GetMethod(nameof(RequiredResolve)), Expression.Constant(x.ParameterType), Expression.Constant(type)), x.ParameterType)
);
return Expression.Lambda<Func<object>>
(
Expression.TypeAs(Expression.MemberInit
(
Expression.New(ctorWithMostParameters, regParams.ToArray()),
memberBindings
), typeof(object))
).Compile();
}
public void Dispose()
{
var hold = InstanceCache;
InstanceCache.Clear();
foreach (var instance in hold)
{
try
{
using (instance.Value as IDisposable) {}
}
catch { /* ignored */ }
}
}
}
public static class ContainerExtensions
{
public static T Resolve<T>(this IResolver container)
{
var ret = container is IRequest req
? req.TryResolve<T>()
: container.TryResolve<T>();
if (ret == null)
throw new Exception($"Error trying to resolve Service '{typeof(T).Name}' or one of its autowired dependencies.");
return ret;
}
public static T Resolve<T>(this IContainer container) =>
(T)container.Resolve(typeof(T));
public static bool Exists<T>(this IContainer container) => container.Exists(typeof(T));
public static IContainer AddTransient<TService>(this IContainer container) =>
container.AddTransient(typeof(TService), container.CreateFactory(typeof(TService)));
public static IContainer AddTransient<TService>(this IContainer container, Func<TService> factory) =>
container.AddTransient(typeof(TService), () => factory());
public static IContainer AddTransient<TService, TImpl>(this IContainer container) where TImpl : TService =>
container.AddTransient(typeof(TService), container.CreateFactory(typeof(TImpl)));
public static IContainer AddTransient(this IContainer container, Type type) =>
container.AddTransient(type, container.CreateFactory(type));
public static IContainer AddSingleton<TService>(this IContainer container) =>
container.AddSingleton(typeof(TService), container.CreateFactory(typeof(TService)));
public static IContainer AddSingleton<TService>(this IContainer container, Func<TService> factory) =>
container.AddSingleton(typeof(TService), () => factory());
public static IContainer AddSingleton<TService, TImpl>(this IContainer container) where TImpl : TService =>
container.AddSingleton(typeof(TService), container.CreateFactory(typeof(TImpl)));
public static IContainer AddSingleton(this IContainer container, Type type) =>
container.AddSingleton(type, container.CreateFactory(type));
}