Skip to content

Commit 3509947

Browse files
committed
Add register factory attribute
1 parent f682322 commit 3509947

6 files changed

Lines changed: 472 additions & 1 deletion

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
3+
using JetBrains.Annotations;
4+
5+
using Unity.Lifetime;
6+
7+
8+
namespace UnityContainerAttributeRegistration.Attribute
9+
{
10+
/// <summary>
11+
/// Mark a method to be registered as factory to an <see cref="Unity.IUnityContainer" />
12+
/// </summary>
13+
[AttributeUsage(AttributeTargets.Method)]
14+
public class RegisterFactoryAttribute : System.Attribute
15+
{
16+
/// <summary>
17+
/// Candidate for registration to <see cref="Unity" />.
18+
/// </summary>
19+
public RegisterFactoryAttribute([CanBeNull] Type from = null,
20+
[CanBeNull] Type lifetimeManager = null)
21+
{
22+
From = from;
23+
LifetimeManager = lifetimeManager;
24+
}
25+
26+
/// <summary>
27+
/// <see cref="Type" /> that will be requested.
28+
/// </summary>
29+
[CanBeNull]
30+
internal Type From { get; }
31+
32+
/// <summary>
33+
/// The <see cref="LifetimeManager" /> that controls the lifetime
34+
/// of the returned instance.
35+
/// </summary>
36+
[CanBeNull]
37+
internal Type LifetimeManager { get; }
38+
}
39+
}

src/UnityContainerAttributeRegistration/Attribute/RegisterProviderAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
namespace UnityContainerAttributeRegistration.Attribute
55
{
66
/// <summary>
7-
/// Mark a type to be a provider for <see cref="RegisterInstanceAttribute" />
7+
/// Mark a type to be a provider for <see cref="RegisterInstanceAttribute" /> and <see cref="RegisterFactoryAttribute" />
88
/// </summary>
99
[AttributeUsage(AttributeTargets.Class)]
1010
public class RegisterProviderAttribute : System.Attribute
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
6+
using JetBrains.Annotations;
7+
8+
using Unity;
9+
using Unity.Lifetime;
10+
11+
using UnityContainerAttributeRegistration.Attribute;
12+
using UnityContainerAttributeRegistration.Exention;
13+
using UnityContainerAttributeRegistration.Provider;
14+
15+
16+
namespace UnityContainerAttributeRegistration.Populator
17+
{
18+
internal class FactoryPopulator : Populator
19+
{
20+
public FactoryPopulator(IAssemblyProvider assemblyProvider) : base(assemblyProvider)
21+
{
22+
}
23+
24+
public override IUnityContainer Populate(IUnityContainer container)
25+
{
26+
IList<Type> typesWithAttribute = GetTypesWith<RegisterProviderAttribute>(TypeDefined.Inherit)
27+
.ToList();
28+
29+
IEnumerable<FactoryToRegister> factoriesToRegister =
30+
typesWithAttribute.SelectMany(providerClassType => GetInstancesToRegisterFor(container, providerClassType));
31+
32+
foreach(FactoryToRegister factoryToRegister in factoriesToRegister)
33+
{
34+
Type returnType = factoryToRegister.ReturnType;
35+
Func<IUnityContainer, Type, string, object> factory = factoryToRegister.Factory;
36+
IFactoryLifetimeManager lifetimeManager = factoryToRegister.LifetimeManager;
37+
38+
container.RegisterFactory(returnType, factory, lifetimeManager);
39+
}
40+
41+
return container;
42+
}
43+
44+
private IEnumerable<FactoryToRegister> GetInstancesToRegisterFor(IUnityContainer container, Type providerClassType)
45+
{
46+
if(providerClassType.IsStatic() || providerClassType.IsAbstract)
47+
{
48+
// todo is not covered, exception is thrown by another populator
49+
throw new InvalidOperationException(
50+
$"Class type must not be static or abstract to be used with RegisterTypeAttribute: {providerClassType.FullName}");
51+
}
52+
53+
object providerClassInstance = container.Resolve(providerClassType);
54+
MethodInfo[] methodInfos = providerClassType.GetMethods();
55+
56+
return methodInfos
57+
.Where(info => info.CustomAttributes.Any(data => data.AttributeType == typeof(RegisterFactoryAttribute)))
58+
.Select(info => CreateFactoryToRegisterFrom(info, providerClassInstance))
59+
.ToList();
60+
}
61+
62+
private FactoryToRegister CreateFactoryToRegisterFrom(MethodInfo info, object instance)
63+
{
64+
RegisterFactoryAttribute attribute = info.GetCustomAttribute<RegisterFactoryAttribute>();
65+
Type returnType = attribute.From ?? info.ReturnType;
66+
67+
if(returnType == typeof(void))
68+
{
69+
throw new InvalidOperationException("Return type must not be void.");
70+
}
71+
72+
if(!IsUnityFactorySignatur(info))
73+
{
74+
throw new InvalidOperationException("Factory method signature does not match.");
75+
}
76+
77+
IFactoryLifetimeManager lifetimeManager =
78+
attribute.LifetimeManager == null
79+
? null
80+
: GetInstanceByType<IFactoryLifetimeManager>(attribute.LifetimeManager);
81+
82+
83+
return new FactoryToRegister(returnType, GetFactoryMethodFor(info, instance), lifetimeManager);
84+
}
85+
86+
private bool IsUnityFactorySignatur(MethodInfo methodInfo)
87+
{
88+
var parameters = methodInfo.GetParameters();
89+
90+
if(parameters.Length == 1
91+
&& parameters[0].ParameterType == typeof(IUnityContainer)
92+
)
93+
{
94+
return true;
95+
}
96+
97+
if(parameters.Length == 3
98+
&& parameters[0].ParameterType == typeof(IUnityContainer)
99+
&& parameters[1].ParameterType == typeof(Type)
100+
&& parameters[2].ParameterType == typeof(string)
101+
)
102+
{
103+
return true;
104+
}
105+
106+
return false;
107+
}
108+
109+
private Func<IUnityContainer, Type, string, object> GetFactoryMethodFor(MethodInfo methodInfo, object instance)
110+
{
111+
return (container, typeValue, stringValue) => {
112+
IList<object> invokeParams = new List<object>{container};
113+
114+
if(methodInfo.GetParameters().Length == 3)
115+
{
116+
invokeParams.Add(typeValue);
117+
invokeParams.Add(stringValue);
118+
}
119+
120+
return methodInfo.Invoke(instance, invokeParams.ToArray());
121+
};
122+
}
123+
124+
private sealed class FactoryToRegister
125+
{
126+
public FactoryToRegister([NotNull] Type returnType, [NotNull] Func<IUnityContainer, Type, string, object> factory, [CanBeNull] IFactoryLifetimeManager lifetimeManager)
127+
{
128+
ReturnType = returnType;
129+
Factory = factory;
130+
LifetimeManager = lifetimeManager;
131+
}
132+
133+
[NotNull]
134+
public Type ReturnType { get; }
135+
136+
[NotNull]
137+
public Func<IUnityContainer, Type, string, object> Factory { get; }
138+
139+
[CanBeNull]
140+
public IFactoryLifetimeManager LifetimeManager { get; }
141+
}
142+
}
143+
}

src/UnityContainerAttributeRegistration/UnityContainerPopulator.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public sealed class UnityContainerPopulator
1515
{
1616
private readonly IPopulator typePopulator;
1717
private readonly IPopulator instancePopulator;
18+
private readonly IPopulator factoryPopulator;
1819

1920
/// <summary>
2021
/// Use <see cref="System.AppDomain.CurrentDomain" /> to populate an <see cref="Unity.IUnityContainer" />
@@ -31,6 +32,7 @@ public UnityContainerPopulator([NotNull] IAssemblyProvider appDomain)
3132
{
3233
typePopulator = new TypePopulator(appDomain);
3334
instancePopulator = new InstancePopulator(appDomain);
35+
factoryPopulator = new FactoryPopulator(appDomain);
3436
}
3537

3638
/// <summary>
@@ -53,6 +55,7 @@ public IUnityContainer Populate([NotNull] IUnityContainer container)
5355
{
5456
typePopulator.Populate(container);
5557
instancePopulator.Populate(container);
58+
factoryPopulator.Populate(container);
5659

5760
return container;
5861
}

0 commit comments

Comments
 (0)