Skip to content

Commit dfa2772

Browse files
authored
Merge pull request #8 from ManticSic/register-instance
RegisterInstanceAttribute
2 parents 7c231a6 + 6a4726e commit dfa2772

11 files changed

Lines changed: 541 additions & 104 deletions

File tree

README.md

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ This way you can immediate see if a type is registered or not. Additionally clea
88

99
## Usage
1010

11+
### Register types
12+
1113
You can register types to an unity container using `UnityContainerAttributeRegistration.RegisterTypeAttribute`.
1214

1315
```cs
@@ -19,7 +21,8 @@ namespace My.Awesome.App
1921
{
2022
public static void Main(string[] args)
2123
{
22-
IUnityContainer container = UnityContainerPopulator.Populate();
24+
UnityContainerPopulator populator = new UnityContainerPopulator();
25+
IUnityContainer container = populator.Populate();
2326
}
2427
}
2528

@@ -29,3 +32,77 @@ namespace My.Awesome.App
2932
}
3033
}
3134
```
35+
36+
### Register instances
37+
38+
You can register instances to an unity container using `UnityContainerAttributeRegistration.Attribute.RegisterInstanceProviderAttribute` and `UnityContainerAttributeRegistration.Attribute.RegisterInstanceAttribute`.
39+
40+
Classes marked with `UnityContainerAttributeRegistration.Attribute.RegisterInstanceProviderAttribute` will be instantiated using the container which should be populated with the instances.
41+
So you can use already registered services to create the instances, which should be later registered.
42+
43+
```
44+
namespace My.Awesome.App
45+
{
46+
public class Program
47+
{
48+
public static void Main(string[] args)
49+
{
50+
UnityContainerPopulator populator = new UnityContainerPopulator();
51+
IUnityContainer container = populator.Populate();
52+
}
53+
}
54+
55+
[RegisterInstanceProvider]
56+
public class InstanceProvider
57+
{
58+
[RegisterInstance]
59+
public string Token = "Hard coded token";
60+
}
61+
}
62+
```
63+
64+
### Using a custom container
65+
66+
It is possible to populate a already created container.
67+
68+
```
69+
namespace My.Awesome.App
70+
{
71+
public class Program
72+
{
73+
public static void Main(string[] args)
74+
{
75+
IUnityContainer container = new UnityContainer();
76+
UnityContainerPopulator populator = new UnityContainerPopulator();
77+
populator.Populate(container);
78+
}
79+
}
80+
}
81+
```
82+
83+
### Restrict the checked assemblies
84+
85+
You can restrict the assemblies to check, e. g. to create a container only containing registrations of one assembly.
86+
87+
```
88+
namespace My.Awesome.App
89+
{
90+
public class Program
91+
{
92+
public static void Main(string[] args)
93+
{
94+
IAssemblyProvider assemblyProvider = new CustomAssemblyProvider();
95+
UnityContainerPopulator populator = new UnityContainerPopulator(assemblyProvider);
96+
IUnityContainer container = populator.Populate();
97+
}
98+
}
99+
100+
public class CustomAssemblyProvider : IAssemblyProvider
101+
{
102+
public IList<Assembly> GetAssemblies()
103+
{
104+
// return a list of assemblies to use
105+
}
106+
}
107+
}
108+
```
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+
6+
namespace UnityContainerAttributeRegistration.Attribute
7+
{
8+
/// <summary>
9+
/// Mark a property to be registered to an <see cref="Unity.IUnityContainer" />
10+
/// </summary>
11+
[AttributeUsage(AttributeTargets.Property)]
12+
public class RegisterInstanceAttribute : System.Attribute
13+
{
14+
/// <summary>
15+
/// Candidate for registration to <see cref="Unity" />.
16+
/// </summary>
17+
/// <param name="from"><see cref="Type" /> that will be requested.</param>
18+
/// <param name="lifetimeManager">The <see cref="Unity.Lifetime.IInstanceLifetimeManager" /> that controls the lifetime of the returned instance.</param>
19+
public RegisterInstanceAttribute([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+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
3+
4+
namespace UnityContainerAttributeRegistration.Attribute
5+
{
6+
/// <summary>
7+
/// Mark a type to be a provider for <see cref="RegisterInstanceAttribute" />
8+
/// </summary>
9+
[AttributeUsage(AttributeTargets.Class)]
10+
public class RegisterInstanceProviderAttribute : System.Attribute
11+
{
12+
}
13+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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.Provider;
12+
using UnityContainerAttributeRegistration.Attribute;
13+
using UnityContainerAttributeRegistration.Exention;
14+
15+
16+
namespace UnityContainerAttributeRegistration.Populator
17+
{
18+
/// <summary>
19+
/// Populator for the <see cref="UnityContainerAttributeRegistration.Attribute.RegisterInstanceAttribute" />.
20+
/// </summary>
21+
internal class InstancePopulator : Populator
22+
{
23+
/// <summary>
24+
/// ctor
25+
/// </summary>
26+
/// <param name="appDomain">Used <see cref="IAssemblyProvider" /> for searching for candidates.</param>
27+
public InstancePopulator(IAssemblyProvider appDomain) : base(appDomain)
28+
{
29+
}
30+
31+
/// <summary>
32+
/// Populate the passed <paramref name="container" />.
33+
/// </summary>
34+
/// <param name="container"><see cref="IUnityContainer" /> to populate.</param>
35+
/// <returns>Passed <paramref name="container" />.</returns>
36+
/// <exception cref="InvalidOperationException">Class type must not be static or abstract.</exception>
37+
public override IUnityContainer Populate(IUnityContainer container)
38+
{
39+
IList<Type> typesWithAttribute = GetTypesWith<RegisterInstanceProviderAttribute>(TypeDefined.Inherit)
40+
.ToList();
41+
42+
IEnumerable<InstanceToRegister> instancesToRegister =
43+
typesWithAttribute.SelectMany(providerClassType => GetInstancesToRegisterFor(container, providerClassType));
44+
45+
foreach(InstanceToRegister instanceToRegister in instancesToRegister)
46+
{
47+
Type type = instanceToRegister.Type;
48+
object instance = instanceToRegister.Instance;
49+
IInstanceLifetimeManager lifetimeManager = instanceToRegister.LifetimeManager;
50+
container.RegisterInstance(type, instance, lifetimeManager);
51+
}
52+
53+
return container;
54+
}
55+
56+
/// <summary>
57+
/// Create a list of <see cref="InstanceToRegister" /> depending on class marked with <see cref="RegisterInstanceProviderAttribute" />
58+
/// </summary>
59+
/// <param name="container"><see cref="IUnityContainer" /> to resolve <paramref name="providerClassType" /></param>
60+
/// <param name="providerClassType">Class type used to search for <see cref="RegisterInstanceAttribute" /></param>
61+
/// <returns>List of instances to register with all needed parameters</returns>
62+
/// <exception cref="InvalidOperationException"><paramref name="providerClassType" /> type must not be static or abstract.</exception>
63+
private IEnumerable<InstanceToRegister> GetInstancesToRegisterFor(IUnityContainer container, Type providerClassType)
64+
{
65+
if(providerClassType.IsStatic() || providerClassType.IsAbstract)
66+
{
67+
throw new InvalidOperationException(
68+
$"Class type must not be static or abstract to be used with RegisterTypeAttribute: {providerClassType.FullName}");
69+
}
70+
71+
object providerClassInstance = container.Resolve(providerClassType);
72+
PropertyInfo[] properties = providerClassType.GetProperties();
73+
74+
return properties
75+
.Where(info => info.CustomAttributes.Any(data => data.AttributeType == typeof(RegisterInstanceAttribute)))
76+
.Select(info =>
77+
{
78+
object instance = info.GetValue(providerClassInstance);
79+
RegisterInstanceAttribute attribute = info.GetCustomAttribute<RegisterInstanceAttribute>();
80+
Type from = attribute.From;
81+
IInstanceLifetimeManager lifetimeManager =
82+
attribute.LifetimeManager == null
83+
? null
84+
: GetInstanceByType<IInstanceLifetimeManager>(attribute.LifetimeManager);
85+
86+
return new InstanceToRegister(instance, from, lifetimeManager);
87+
})
88+
.ToList();
89+
}
90+
91+
/// <summary>
92+
/// Wrapper to regsiter isntances
93+
/// </summary>
94+
private sealed class InstanceToRegister
95+
{
96+
public InstanceToRegister([CanBeNull] object instance,
97+
[CanBeNull] Type type,
98+
[CanBeNull] IInstanceLifetimeManager lifetimeManager)
99+
{
100+
Instance = instance;
101+
Type = type;
102+
LifetimeManager = lifetimeManager;
103+
}
104+
105+
/// <summary>
106+
/// Concrete instance to register
107+
/// </summary>
108+
[CanBeNull]
109+
public object Instance { get; }
110+
111+
/// <summary>
112+
/// Requested type
113+
/// </summary>
114+
[CanBeNull]
115+
public Type Type { get; }
116+
117+
/// <summary>
118+
/// Used lifetime manager
119+
/// </summary>
120+
[CanBeNull]
121+
public IInstanceLifetimeManager LifetimeManager { get; }
122+
}
123+
}
124+
}

src/UnityContainerAttributeRegistration/Populator/Populator.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Reflection;
5+
6+
using JetBrains.Annotations;
47

58
using Unity;
69

@@ -41,5 +44,33 @@ protected IEnumerable<Type> GetTypesWith<TAttribute>(TypeDefined typeDefined) wh
4144
.Where(type => type.IsDefined(typeof(TAttribute), typeDefined == TypeDefined.Inherit))
4245
;
4346
}
47+
48+
/// <summary>
49+
/// Create an instance for <paramref name="objectType" />.
50+
/// </summary>
51+
/// <param name="objectType"><see cref="Type" /> used to create an instance.</param>
52+
/// <typeparam name="T"><paramref name="objectType" /> must be type-equal to, inherit or implement <typeparamref name="T" />.</typeparam>
53+
/// <returns>New instance of <paramref name="objectType" /> as <typeparamref name="T" />.</returns>
54+
/// <exception cref="InvalidOperationException">Cannot create an instance of <paramref name="objectType" />. Whether <paramref name="objectType" /> is not type-equal, does not inherit or implement <typeparamref name="T" /> or has no default constructor.</exception>
55+
[NotNull]
56+
protected T GetInstanceByType<T>([NotNull] Type objectType)
57+
{
58+
Type targetType = typeof(T);
59+
60+
if(!targetType.IsAssignableFrom(objectType))
61+
{
62+
throw new InvalidOperationException($"Type {objectType.FullName} cannot be assigned from {targetType.FullName}");
63+
}
64+
65+
ConstructorInfo ctor = objectType.GetConstructor(Type.EmptyTypes);
66+
67+
if(ctor == null)
68+
{
69+
throw new InvalidOperationException(
70+
$"Cannot create instance of ITypeLifetimeManager. No default constructor found for {objectType.FullName}");
71+
}
72+
73+
return (T) ctor.Invoke(new object[0]);
74+
}
4475
}
4576
}

src/UnityContainerAttributeRegistration/Populator/TypePopulator.cs

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
using System.Linq;
44
using System.Reflection;
55

6-
using JetBrains.Annotations;
7-
86
using Unity;
97
using Unity.Lifetime;
108

@@ -59,33 +57,5 @@ public override IUnityContainer Populate(IUnityContainer container)
5957

6058
return container;
6159
}
62-
63-
/// <summary>
64-
/// Create an instance for <paramref name="objectType" />.
65-
/// </summary>
66-
/// <param name="objectType"><see cref="Type" /> used to create an instance.</param>
67-
/// <typeparam name="T"><paramref name="objectType" /> must be type-equal to, inherit or implement <typeparamref name="T" />.</typeparam>
68-
/// <returns>New instance of <paramref name="objectType" /> as <typeparamref name="T" />.</returns>
69-
/// <exception cref="InvalidOperationException">Cannot create an instance of <paramref name="objectType" />. Whether <paramref name="objectType" /> is not type-equal, does not inherit or implement <typeparamref name="T" /> or has no default constructor.</exception>
70-
[NotNull]
71-
private T GetInstanceByType<T>([NotNull] Type objectType)
72-
{
73-
Type targetType = typeof(T);
74-
75-
if(!targetType.IsAssignableFrom(objectType))
76-
{
77-
throw new InvalidOperationException($"Type {objectType.FullName} cannot be assigned from {targetType.FullName}");
78-
}
79-
80-
ConstructorInfo ctor = objectType.GetConstructor(Type.EmptyTypes);
81-
82-
if(ctor == null)
83-
{
84-
throw new InvalidOperationException(
85-
$"Cannot create instance of ITypeLifetimeManager. No default constructor found for {objectType.FullName}");
86-
}
87-
88-
return (T) ctor.Invoke(new object[0]);
89-
}
9060
}
9161
}

src/UnityContainerAttributeRegistration/UnityContainerPopulator.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ namespace UnityContainerAttributeRegistration
1313
/// </summary>
1414
public sealed class UnityContainerPopulator
1515
{
16-
private readonly IAssemblyProvider appDomain;
17-
private readonly IPopulator TypePopulator;
16+
private readonly IPopulator typePopulator;
17+
private readonly IPopulator instancePopulator;
1818

1919
/// <summary>
2020
/// Use <see cref="System.AppDomain.CurrentDomain" /> to populate an <see cref="Unity.IUnityContainer" />
@@ -29,9 +29,8 @@ public UnityContainerPopulator() : this(new AssemblyProvider())
2929
/// <param name="appDomain">Custom <see cref="IAssemblyProvider" /></param>
3030
public UnityContainerPopulator([NotNull] IAssemblyProvider appDomain)
3131
{
32-
this.appDomain = appDomain;
33-
34-
TypePopulator = new TypePopulator(appDomain);
32+
typePopulator = new TypePopulator(appDomain);
33+
instancePopulator = new InstancePopulator(appDomain);
3534
}
3635

3736
/// <summary>
@@ -52,7 +51,8 @@ public IUnityContainer Populate()
5251
/// </returns>
5352
public IUnityContainer Populate([NotNull] IUnityContainer container)
5453
{
55-
TypePopulator.Populate(container);
54+
typePopulator.Populate(container);
55+
instancePopulator.Populate(container);
5656

5757
return container;
5858
}

0 commit comments

Comments
 (0)