##// END OF EJS Templates
Container configuration cleanup, RC2
cin -
r279:8714471e8d78 v3
parent child
Show More
@@ -0,0 +1,40
1 using System.IO;
2 using Unity;
3
4 namespace Implab.ServiceHost.Unity
5 {
6 public static class UnityContainerExtensions
7 {
8 public static IUnityContainer LoadXmlConfiguration(this IUnityContainer container, string file, ContainerConfigurationSchema schema) {
9 Safe.ArgumentNotNull(container, nameof(container));
10 var builder = new ContainerBuilder(container,schema);
11 builder.LoadConfig(file);
12 return builder.Container;
13 }
14
15 public static IUnityContainer LoadXmlConfiguration(this IUnityContainer container, Stream stream, ContainerConfigurationSchema schema) {
16 Safe.ArgumentNotNull(container, nameof(container));
17 Safe.ArgumentNotNull(stream, nameof(stream));
18
19 if (schema == null)
20 schema = ContainerConfigurationSchema.Default;
21
22 var builder = new ContainerBuilder(container,schema);
23 var config = (ContainerElement)schema.Serializer.Deserialize(stream);
24 if (config.Items != null) {
25 foreach(var item in config.Items)
26 item?.Visit(builder);
27 }
28
29 return builder.Container;
30 }
31
32 public static IUnityContainer LoadXmlConfiguration(this IUnityContainer container, Stream stream) {
33 return LoadXmlConfiguration(container, stream, ContainerConfigurationSchema.Default);
34 }
35
36 public static IUnityContainer LoadXmlConfiguration(this IUnityContainer container, string file) {
37 return LoadXmlConfiguration(container, file, ContainerConfigurationSchema.Default);
38 }
39 }
40 } No newline at end of file
@@ -1,136 +1,141
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Diagnostics;
4 4 using System.Linq;
5 5 using Implab.Components;
6 6 using Implab.Diagnostics;
7 7 using Implab.ServiceHost.Unity;
8 8 using Implab.Xml;
9 9 using Unity;
10 10 using Unity.Injection;
11 11 using Unity.Registration;
12 12
13 13 namespace Implab.Playground {
14 14
15 15 public class Foo {
16 16
17 17 public class Bar {
18 18
19 19 }
20 20
21 21 public string Name { get; set; }
22 22
23 23 public int IntValue { get; set; }
24 24
25 25 public string StringValue { get; set; }
26 26
27 27 public void AddRange(Foo[] items) {
28 28 Console.WriteLine($"AddRange: Foo[]");
29 29 }
30 30
31 31 }
32 32
33 33 public class FooFactory : IFactory<Foo>, IFactory<Foo.Bar> {
34 34
35 35 public bool UseSsl { get; set; }
36 36
37 37 public string Connection { get; set; }
38 38
39 39 public Foo Create() {
40 40 return new Foo() {
41 41 Name = "AutoFac"
42 42 };
43 43 }
44 44
45 45 Foo.Bar IFactory<Foo.Bar>.Create() {
46 46 return new Foo.Bar();
47 47 }
48 48 }
49 49
50 50 public interface IContainer<T> {
51 51 T Instance { get; set; }
52 52 }
53 53
54 54 public class Container<T> : IContainer<T> {
55 55 public class Bar {
56 56
57 57 }
58 58
59 59 public class Bar<T2> {
60 60 public class Baz {
61 61
62 62 }
63 63
64 64 }
65 65
66 66 public Container() {
67 67
68 68 }
69 69
70 70 public Container(T instance) {
71 71 Instance = instance;
72 72 }
73 73
74 74 public T Instance { get; set; }
75 75
76 76 public void SetInstance(T value) {
77 77 Instance = value;
78 78 }
79 79
80 80 public void AddRange(List<T> items) {
81 81 Console.WriteLine($"AddRange: {typeof(List<T>)}");
82 82 }
83 83
84 84 public void AddRange(T[] items) {
85 85 Console.WriteLine($"AddRange: T[] ofType {typeof(T[])}");
86 86 }
87 87 }
88 88
89 89 public class Program {
90 90
91 91 static void Main(string[] args) {
92 92 var listener = new SimpleTraceListener(Console.Out);
93 93 var source = Trace<TypeResolver>.TraceSource;
94 94 source.Switch.Level = SourceLevels.All;
95 95 source.Listeners.Add(listener);
96 96
97 var resolver = new TypeResolver();
98 resolver.AddNamespace("System");
99 resolver.AddNamespace("System.Collections.Generic");
100 resolver.AddNamespace("Implab.Playground");
101 resolver.AddMapping("string", typeof(string));
102 resolver.AddMapping("listOf`1", typeof(List<>));
97 var stopwatch = new Stopwatch();
98 stopwatch.Start();
99
100 var container = new UnityContainer();
101
102 Console.WriteLine($"Created: {stopwatch.ElapsedMilliseconds}");
103 stopwatch.Restart();
104
105 container.LoadXmlConfiguration("data/sample.xml");
103 106
104 var spec = TypeReference.Parse("Container{listOf{string}}+Bar{List{string}}");
107 Console.WriteLine($"Loaded: {stopwatch.ElapsedMilliseconds}");
105 108
106 var t = resolver.Resolve(spec, true);
109 stopwatch.Restart();
110 var instace1 = container.Resolve<IContainer<string>>();
111 Console.WriteLine($"Resolved1: {stopwatch.ElapsedMilliseconds}");
107 112
108 Console.WriteLine("{0}", t);
109 Console.WriteLine("Spec: {0}", spec);
110 Console.WriteLine("IsGenericType: {0}", t.IsGenericType);
111 Console.WriteLine("IsGenericTypeDefinition: {0}", t.IsGenericTypeDefinition);
112 Console.WriteLine("ContainsGenericParameters: {0}", t.ContainsGenericParameters);
113 stopwatch.Restart();
114 var instace2 = container.Resolve<IContainer<Foo>>();
115 Console.WriteLine($"Resolved2: {stopwatch.ElapsedMilliseconds}");
116
117 DisplayContainerRegistrations(container);
113 118 }
114 119
115 120 static void DisplayContainerRegistrations(IUnityContainer theContainer) {
116 121 string regName, regType, mapTo, lifetime;
117 122 Console.WriteLine("Container has {0} Registrations:",
118 123 theContainer.Registrations.Count());
119 124 foreach (ContainerRegistration item in theContainer.Registrations) {
120 125 regType = item.RegisteredType.FullName;
121 126 mapTo = item.MappedToType.FullName;
122 127 regName = item.Name ?? "[default]";
123 128 lifetime = item.LifetimeManager.LifetimeType.Name;
124 129 if (mapTo != regType) {
125 130 mapTo = " -> " + mapTo;
126 131 } else {
127 132 mapTo = string.Empty;
128 133 }
129 134 lifetime = lifetime.Substring(0, lifetime.Length - "LifetimeManager".Length);
130 135 Console.WriteLine("+ {0}{1} '{2}' {3}", regType, mapTo, regName, lifetime);
131 136 }
132 137 }
133 138
134 139
135 140 }
136 141 }
@@ -1,10 +1,10
1 1 using System;
2 2
3 3 namespace Implab.ServiceHost.Unity {
4 4 /// <summary>
5 5 /// Base class for injections, each injection is applied to the type registration context.
6 6 /// </summary>
7 public abstract class AbstractMemberInjection {
8 internal abstract void Visit(TypeRegistrationBuilder context);
7 public abstract class AbstractMemberInjection : ITypeMemberInjection {
8 public abstract void Visit(TypeRegistrationBuilder builder);
9 9 }
10 10 } No newline at end of file
@@ -1,42 +1,48
1 1 using System;
2 2 using System.Xml.Serialization;
3 3 using Unity.Lifetime;
4 4 using Unity.Registration;
5 5
6 6 namespace Implab.ServiceHost.Unity
7 7 {
8 8 /// <summary>
9 9 /// Базовая информаци о регистрации в контейнере: тип, имя и время жизни
10 10 /// </summary>
11 public abstract class AbstractRegistration : AbstractContainerItem {
11 public abstract class AbstractRegistration : AbstractContainerItem, IRegistration {
12 12
13 13 /// <summary>
14 14 /// An optional name for a registration in the container
15 15 /// </summary>
16 16 [XmlAttribute("name")]
17 17 public string Name {
18 18 get; set;
19 19 }
20 20
21 21 [XmlElement("signleton", typeof(SingletonLifetimeElement))]
22 22 [XmlElement("context", typeof(ContextLifetimeElement))]
23 23 [XmlElement("container", typeof(ContainerLifetimeElement))]
24 24 [XmlElement("hierarchy", typeof(HierarchicalLifetimeElement))]
25 25 public LifetimeElement Lifetime {get; set;}
26 26
27 27 /// <summary>
28 28 /// A type specification for the service registration,
29 29 /// </summary>
30 30 [XmlAttribute("type")]
31 31 public string RegistrationType { get; set; }
32 32
33 public virtual LifetimeManager GetLifetime(ContainerBuilder builder) {
34 return Lifetime?.GetLifetime(builder);
35 }
36
33 37 public virtual Type GetRegistrationType(Func<string,Type> resolver) {
34 38 return resolver(RegistrationType);
35 39 }
36 40
37 public virtual void Visit(RegistrationBuilder builder) {
38 Lifetime?.Visit(builder);
41 public virtual Type GetRegistrationType(ContainerBuilder builder) {
42 return builder.ResolveType(RegistrationType);
39 43 }
40 44
45
46
41 47 }
42 48 } No newline at end of file
@@ -1,17 +1,17
1 1 using System.Xml.Serialization;
2 2
3 3 namespace Implab.ServiceHost.Unity {
4 4 public class ConstructorInjectionElement : AbstractMemberInjection {
5 5
6 6 [XmlElement("dependency", typeof(DependencyParameterElement))]
7 7 [XmlElement("value", typeof(ValueParameterElement))]
8 8 [XmlElement("serialized", typeof(SerializedParameterElement))]
9 9 [XmlElement("default", typeof(DefaultParameterElement))]
10 10 [XmlElement("array", typeof(ArrayParameterElement))]
11 11 public InjectionParameterElement[] Parameters { get; set; }
12 12
13 internal override void Visit(TypeRegistrationBuilder context) {
14 context.Visit(this);
13 public override void Visit(TypeRegistrationBuilder builder) {
14 builder.Visit(this);
15 15 }
16 16 }
17 17 } No newline at end of file
@@ -1,177 +1,111
1 1 using System;
2 2 using System.Reflection;
3 3 using Unity;
4 4
5 5 namespace Implab.ServiceHost.Unity {
6 6 public class ContainerBuilder {
7 7
8 8 readonly TypeResolver m_resolver;
9 9
10 readonly UnityContainer m_container;
10 readonly IUnityContainer m_container;
11 11
12 12 readonly ContainerConfigurationSchema m_schema;
13 13
14 public UnityContainer Container {
14 public IUnityContainer Container {
15 15 get {
16 16 return m_container;
17 17 }
18 18 }
19 19
20 20 public ContainerBuilder() : this(null, null) {
21 21 }
22 22
23 public ContainerBuilder(UnityContainer container, ContainerConfigurationSchema schema) {
23 public ContainerBuilder(IUnityContainer container, ContainerConfigurationSchema schema) {
24 24 m_container = container ?? new UnityContainer();
25 25 m_resolver = new TypeResolver();
26 26 m_schema = schema ?? ContainerConfigurationSchema.Default;
27 27 }
28 28
29 29 public Type ResolveType(string typeReference) {
30 return m_resolver.Resolve(typeReference, true);
31 }
32
33
34 internal void Visit(TypeAbstractRegistration typeRegistration) {
35 var registrationType = typeRegistration.GetRegistrationType(ResolveType);
36 var implementationType = typeRegistration.GetImplementationType(ResolveType) ?? registrationType;
37
38 var registrationContext = new TypeRegistrationBuilder(
39 m_resolver,
40 registrationType,
41 implementationType
42 );
43
44 typeRegistration.Visit(registrationContext);
45
46 m_container.RegisterType(
47 registrationContext.RegistrationType,
48 registrationContext.ImplementationType,
49 typeRegistration.Name,
50 registrationContext.Lifetime,
51 registrationContext.Injections
52 );
53 }
54
55 internal void Visit(InstanceAbstractRegistration instanceRegistration) {
56 var registrationType = instanceRegistration.GetRegistrationType(ResolveType);
57
58 var builder = new InstanceRegistrationBuilder(m_resolver, registrationType);
59
60 instanceRegistration.Visit(builder);
61
62 m_container.RegisterInstance(
63 builder.ValueBuilder.ValueType,
64 instanceRegistration.Name,
65 builder.ValueBuilder.Value,
66 builder.Lifetime
67 );
68 }
69
70 internal void Visit(FactoryAbstractRegistratrion factoryRgistration) {
71 var registrationType = factoryRgistration.GetRegistrationType(ResolveType);
72
73 var builder = new FactoryRegistrationBuilder(registrationType);
74
75 factoryRgistration.Visit(builder);
76
77 m_container.RegisterType(
78 builder.RegistrationType,
79 factoryRgistration.Name,
80 builder.Lifetime,
81 builder.Factory
82 );
30 return string.IsNullOrEmpty(typeReference) ? null : m_resolver.Resolve(typeReference, true);
83 31 }
84 32
85 33 public void Visit(ITypeRegistration registration) {
86 34 Safe.ArgumentNotNull(registration, nameof(registration));
87 35
88 36 var registrationType = registration.GetRegistrationType(this);
89 37 var implementationType = registration.GetImplementationType(this) ?? registrationType;
90 38
91 39 if (registrationType == null)
92 40 throw new Exception($"A type must be specified for the registration {registration.Name}");
93 41
94 42 var builder = new TypeRegistrationBuilder(
95 43 m_resolver,
96 44 registrationType,
97 45 implementationType
98 46 );
99 47
100 48 builder.Lifetime = registration.GetLifetime(this);
101 49
102 50 if (registration.MemberInjections != null) {
103 51 foreach(var member in registration.MemberInjections)
104 52 member.Visit(builder);
105 53 }
106 54
107 55 m_container.RegisterType(
108 56 builder.RegistrationType,
109 57 builder.ImplementationType,
110 58 registration.Name,
111 59 builder.Lifetime,
112 60 builder.Injections
113 61 );
114 62 }
115 63
116 64 public void Visit(IInstanceRegistration registration) {
117 65 Safe.ArgumentNotNull(registration, nameof(registration));
118 66
119 67 var registrationType = registration.GetRegistrationType(this);
120 68
121 69 var builder = new InstanceRegistrationBuilder (
122 70 m_resolver,
123 71 registrationType
124 72 );
125 73
126 74 builder.Lifetime = registration.GetLifetime(this);
127 75
128 76 if (registration.MemberInjections != null) {
129 77 foreach(var member in registration.MemberInjections)
130 78 member.Visit(builder.ValueBuilder);
131 79 }
132 80
133 81 if (builder.RegistrationType == null && builder.ValueBuilder.ValueType == null)
134 82 throw new Exception($"A type must be specified for the registration {registration.Name}");
135 83
136 84 m_container.RegisterInstance(
137 85 builder.RegistrationType ?? builder.ValueBuilder.ValueType,
138 86 registration.Name,
139 builder.ValueBuilder.Injection,
87 builder.ValueBuilder.Value,
140 88 builder.Lifetime
141 89 );
142 90 }
143 91
144 public void Visit(IFactoryRegistration registration) {
145 Safe.ArgumentNotNull(registration, nameof(registration));
146
147 var registrationType = registration.GetRegistrationType(this);
148
149 var builder = new FactoryRegistrationBuilder(registrationType);
150
151 if (registration.MemberInjections != null) {
152 foreach(var member in registration.MemberInjections)
153 member?.Visit(builder);
154 }
155
156 }
157
158 92 public void AddNamespace(string ns) {
159 93 m_resolver.AddNamespace(ns);
160 94 }
161 95
162 96 public void AddAssembly(string assembly) {
163 97
164 98 }
165 99
166 100 public void Include(string file) {
167 101 var includeContext = new ContainerBuilder(m_container, m_schema);
168 102 includeContext.LoadConfig(file);
169 103 }
170 104
171 105 public void LoadConfig(string file) {
172 106 var config = m_schema.LoadFile(file);
173 107 config.Visit(this);
174 108 }
175 109
176 110 }
177 111 } No newline at end of file
@@ -1,10 +1,12
1 1 using Unity.Lifetime;
2 2
3 3 namespace Implab.ServiceHost.Unity
4 4 {
5 5 public class ContainerLifetimeElement : LifetimeElement {
6 public override void Visit(RegistrationBuilder builder) {
7 builder.Visit(this);
6 public override LifetimeManager GetLifetime(ContainerBuilder builder) {
7 return new ContainerControlledLifetimeManager();
8 8 }
9
10
9 11 }
10 12 } No newline at end of file
@@ -1,10 +1,10
1 1 using Unity.Lifetime;
2 2
3 3 namespace Implab.ServiceHost.Unity
4 4 {
5 5 public class ContextLifetimeElement : LifetimeElement {
6 public override void Visit(RegistrationBuilder builder) {
7 builder.Visist(this);
6 public override LifetimeManager GetLifetime(ContainerBuilder builder) {
7 return new PerResolveLifetimeManager();
8 8 }
9 9 }
10 10 } No newline at end of file
@@ -1,17 +1,17
1 1 using System.Xml.Serialization;
2 2
3 3 namespace Implab.ServiceHost.Unity {
4 4 public class DependencyParameterElement : InjectionParameterElement {
5 5
6 6 [XmlAttribute("name")]
7 7 public string DependencyName { get; set; }
8 8
9 9 [XmlAttribute("optional")]
10 10 public bool Optional { get; set; }
11 11
12 12 public override void Visit(InjectionParameterBuilder builder) {
13 13 var type = builder.ResolveInjectedValueType(TypeName);
14 builder.SetDependencyReference(type, DependencyName, Optional);
14 builder.SetDependency(type, DependencyName, Optional);
15 15 }
16 16 }
17 17 } No newline at end of file
@@ -1,29 +1,63
1 1 using System;
2 using System.Collections.Generic;
3 using System.Reflection;
4 using Implab.Components;
5 using Unity;
6 using Unity.Injection;
7 using Unity.Lifetime;
2 8
3 9 namespace Implab.ServiceHost.Unity {
4 public class FactoryActivator : FactoryAbstractRegistratrion {
10 public class FactoryActivator : ITypeRegistration {
11
12 class FactoryInjector : ITypeMemberInjection {
13 public InjectionFactory Factory { get; set; }
14 public void Visit(TypeRegistrationBuilder builder) {
15 builder.AddInjectionMember(Factory);
16 }
17 }
18
5 19
6 20 public Type FactoryType { get; set; }
7 21
8 22 public string FactoryName { get; set; }
9 23
10 public new Type RegistrationType { get; set; }
24 public Type RegistrationType { get; set; }
11 25
12 public override void Visit(FactoryRegistrationBuilder builder) {
13 base.Visit(builder);
26 public LifetimeManager Lifetime { get; set; }
14 27
15 builder.GetType()
16 .GetMethod(
17 nameof(FactoryRegistrationBuilder.SetFactoryDependency)
18 , new[] { typeof(string) }
19 )
20 .MakeGenericMethod(FactoryType, RegistrationType)
21 .Invoke(builder, new[] { FactoryName });
28 public IEnumerable<ITypeMemberInjection> MemberInjections {
29 get {
30 yield return new FactoryInjector {
31 Factory = (InjectionFactory)GetType()
32 .GetMethod(nameof(CreateInjectionFactory), BindingFlags.Static | BindingFlags.NonPublic)
33 .MakeGenericMethod(FactoryType, RegistrationType)
34 .Invoke(null, new [] { FactoryName })
35 };
36 }
22 37 }
23 38
24 public override Type GetRegistrationType(Func<string, Type> resolver) {
39 public string Name { get; set; }
40
41 public Type GetRegistrationType(ContainerBuilder builder) {
25 42 return RegistrationType;
26 43 }
27 44
45 public LifetimeManager GetLifetime(ContainerBuilder builder) {
46 return Lifetime;
47 }
48
49 public Type GetImplementationType(ContainerBuilder builder) {
50 return null;
51 }
52
53 /// <summary>
54 /// Указывает зависимость, реализующую интерфейс <see cref="IFactory{TObj}"/>,
55 /// которая будет использоваться в качестве фабрики для создания объектов
56 /// </summary>
57 /// <param name="dependencyName"></param>
58 static InjectionFactory CreateInjectionFactory<TFac, TObj>(string dependencyName) where TFac : IFactory<TObj> {
59
60 return new InjectionFactory(c => c.Resolve<TFac>(dependencyName).Create());
61 }
28 62 }
29 63 } No newline at end of file
@@ -1,72 +1,72
1 1 using System;
2 2 using System.Xml.Serialization;
3 3 using Implab.Components;
4 4
5 5 namespace Implab.ServiceHost.Unity {
6 6 /// <summary>
7 7 /// Расширяет стандартную регистрацию типа до фабрики, вместе с регистрацией
8 8 /// самой фабрики создаются регистрации сервисов, которые она предоставляет.
9 9 /// </summary>
10 public class FactoryElement : RegisterElement {
10 public class FactoryElement : RegisterElement, ITypeRegistration {
11 11
12 12 /// <summary>
13 13 /// Записи о сервисах, которые создаются данной фабрикой.
14 14 /// </summary>
15 15 /// <remarks>
16 16 /// Сервисы, которые указаны в регистарциях они должны соответсвовать тому,
17 17 /// что фабрика возвращает, но это остается на откуп контейнера
18 18 /// </remarks>
19 19 [XmlElement("provides")]
20 20 public ProvidesElement[] Provides { get; set; }
21 21
22 22 /// <summary>
23 23 /// Переопределяет стандарное поведение регистрации типа в контейнере,
24 24 /// дополняя его регистрацией фабричных методов для получения типов.
25 25 /// </summary>
26 26 /// <param name="builder">Объект для конфигурирования контейнера.</param>
27 27 public override void Visit(ContainerBuilder builder) {
28 28 var factoryType = GetRegistrationType(builder.ResolveType);
29 29
30 30 base.Visit(builder);
31 31
32 32 if (Provides != null && Provides.Length > 0) {
33 33 // если регистрации явно заданы, используеися информация из них
34 34 foreach(var item in Provides) {
35 35 var activator = new FactoryActivator {
36 36 Name = item.RegistrationName,
37 37 RegistrationType = builder.ResolveType(item.RegistrationType),
38 38 FactoryName = Name,
39 39 FactoryType = factoryType
40 40 };
41 activator.Visit(builder);
41 builder.Visit(activator);
42 42 }
43 43 } else {
44 44 // если регистрация явно не задана, в качестве сервиса для регистрации
45 45 // используется тип создаваемый фабрикой, который будет добавлен в контейнер
46 46 // с темже именем, что и сама фабрика (разные типы могут иметь одно имя для регистрации)
47 47 var providedType = (
48 48 factoryType.IsGenericType && factoryType.GetGenericTypeDefinition() == typeof(IFactory<>) ?
49 49 factoryType :
50 50 factoryType.GetInterface(typeof(IFactory<>).FullName)
51 51 )?
52 52 .GetGenericArguments()[0];
53 53
54 54 // не удалось определеить тип
55 55 if (providedType == null)
56 56 throw new ArgumentException("Failed to determine a type provided by the factory");
57 57
58 58 if (providedType.IsGenericParameter)
59 59 throw new ArgumentException("Can't register a generic type paramter as a service");
60 60
61 61 var activator = new FactoryActivator {
62 62 Name = Name,
63 63 RegistrationType = providedType,
64 64 FactoryName = Name,
65 65 FactoryType = factoryType
66 66 };
67 67
68 activator.Visit(builder);
68 builder.Visit(activator);
69 69 }
70 70 }
71 71 }
72 72 } No newline at end of file
@@ -1,10 +1,10
1 1 using Unity.Lifetime;
2 2
3 3 namespace Implab.ServiceHost.Unity
4 4 {
5 5 public class HierarchicalLifetimeElement : LifetimeElement {
6 public override void Visit(RegistrationBuilder builder) {
7 builder.Visit(this);
6 public override LifetimeManager GetLifetime(ContainerBuilder builder) {
7 return new HierarchicalLifetimeManager();
8 8 }
9 9 }
10 10 } No newline at end of file
@@ -1,12 +1,12
1 1 using System;
2 2 using System.Xml.Serialization;
3 3
4 4 namespace Implab.ServiceHost.Unity {
5 public abstract class InjectionParameterElement {
5 public abstract class InjectionParameterElement : IInjectionParameter {
6 6
7 7 [XmlAttribute("type")]
8 8 public string TypeName { get; set; }
9 9
10 10 public abstract void Visit(InjectionParameterBuilder builder);
11 11 }
12 12 } No newline at end of file
@@ -1,92 +1,137
1 1 using System;
2 using System.Collections;
2 3 using System.Collections.Generic;
3 4 using System.ComponentModel;
4 5 using System.Linq;
5 6 using System.Xml.Serialization;
6 7 using Unity.Injection;
7 8
8 9 namespace Implab.ServiceHost.Unity {
9 10
10 11 public class InjectionParameterBuilder {
11 12
12 13 readonly TypeResolver m_resolver;
13 14
14 15 public Type DefaultType { get; private set; }
15 16
16 17 public Type ValueType { get; private set; }
17 18
18 public object Value { get; set; }
19 object m_value;
19 20
20 internal InjectionParameterValue Injection {
21 public object Value {
21 22 get {
22 if (Value != null)
23 return InjectionParameterValue.ToParameter(Value);
23 if (!ValueSpecified)
24 throw new InvalidOperationException("The regular value must be set (dependency or array are not situable in this context)");
25 return m_value;
26 }
27 }
28
29 public bool ValueSpecified { get; private set; }
30
31 InjectionParameterValue m_injection;
24 32
25 return new InjectionParameter(ValueType, null);
33 public InjectionParameterValue Injection {
34 get {
35 if (m_injection == null)
36 throw new InvalidOperationException("The injection parameter is not specified");
37 return m_injection;
26 38 }
27 39 }
28 40
41 public bool InjectionSpecified {
42 get { return m_injection != null; }
43 }
44
29 45 internal InjectionParameterBuilder(TypeResolver resolver, Type defaultType) {
30 46 m_resolver = resolver;
31 47 DefaultType = defaultType;
32 48 }
33 49
34 50 public Type ResolveInjectedValueType(string typeSpec) {
35 51 if (string.IsNullOrEmpty(typeSpec)) {
36 52 if (DefaultType == null)
37 53 throw new Exception("The type must be specified");
38 54 return DefaultType;
39 55 }
40 56 return m_resolver.Resolve(typeSpec, true);
41 57 }
42 58
43 59 public Type ResolveType(string typeSpec) {
44 return m_resolver.Resolve(typeSpec, true);
60 return string.IsNullOrEmpty(typeSpec) ? null : m_resolver.Resolve(typeSpec, true);
45 61 }
46 62
47 63 public void SetValue(Type type, object value) {
64 Safe.ArgumentNotNull(type, nameof(type));
65
48 66 ValueType = type;
49 Value = value;
67 m_value = value;
68 ValueSpecified = true;
69
70 m_injection = new InjectionParameter(type, value);
50 71 }
51 72
52 public void SetValue<T>(T value) {
53 SetValue(typeof(T), value);
54 }
73 public void SetDependency(Type type, string name, bool optional) {
74 Safe.ArgumentNotNull(type, nameof(type));
55 75
56 public void SetDependencyReference(Type type, string name, bool optional) {
57 76 ValueType = type;
58 Value = optional ? (object)new OptionalParameter(type, name) : new ResolvedParameter(type, name);
77 ValueSpecified = false;
78 m_value = null;
79
80 m_injection = optional ? (InjectionParameterValue)new OptionalParameter(type, name) : new ResolvedParameter(type, name);
59 81 }
60 82
61 83 internal void Visit(ArrayParameterElement arrayParameter) {
62 84 Type itemsType = null;
63 85 var arrayType = string.IsNullOrEmpty(arrayParameter.TypeName) ? null : ResolveType(arrayParameter.TypeName);
64 86
87 if (arrayType == null)
88 arrayType = DefaultType;
89
90
65 91 if (!string.IsNullOrEmpty(arrayParameter.ItemsType)) {
66 92 itemsType = ResolveType(arrayParameter.ItemsType);
67 if (arrayType == null)
68 arrayType = itemsType.MakeArrayType();
93 arrayType = itemsType.MakeArrayType();
69 94 } else {
70 itemsType = arrayType?.GetInterface(typeof(IEnumerable<>).FullName)?.GetGenericArguments()[0];
95 itemsType = GetItemsType(arrayType);
71 96 }
72 97
73 98 if (itemsType == null)
74 99 throw new Exception("Failed to determine array elements type");
75 100
76 101 InjectionParameterValue[] injections = (arrayParameter.Items ?? new InjectionParameterElement[0])
77 102 .Select(x => {
78 103 var builder = new InjectionParameterBuilder(m_resolver, itemsType);
79 104 x.Visit(builder);
80 105 return builder.Injection;
81 106 })
82 107 .ToArray();
83 108
84 109 var array = itemsType.IsGenericParameter ?
85 (object)new GenericResolvedArrayParameter(itemsType.Name, injections) :
110 (InjectionParameterValue)new GenericResolvedArrayParameter(itemsType.Name, injections) :
86 111 new ResolvedArrayParameter(itemsType, injections);
87 112
88 113 ValueType = arrayType;
89 Value = array;
114 m_value = null;
115 ValueSpecified = false;
116
117 m_injection = array;
118 }
119
120 Type GetItemsType(Type collectionType) {
121 if (collectionType == null)
122 return null;
123
124 Type itemsType = null;
125
126 if (collectionType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) {
127 itemsType = collectionType.GetGenericArguments()[0];
128 } else if (collectionType == typeof(IEnumerable)) {
129 itemsType = typeof(object);
130 } else {
131 itemsType = collectionType.GetInterface(typeof(IEnumerable<>).FullName)?.GetGenericArguments()[0];
132 }
133
134 return itemsType;
90 135 }
91 136 }
92 137 } No newline at end of file
@@ -1,5 +1,7
1 using Unity.Lifetime;
2
1 3 namespace Implab.ServiceHost.Unity {
2 4 public abstract class LifetimeElement {
3 public abstract void Visit(RegistrationBuilder builder);
5 public abstract LifetimeManager GetLifetime(ContainerBuilder builder);
4 6 }
5 7 } No newline at end of file
@@ -1,20 +1,20
1 1 using System.Xml.Serialization;
2 2
3 3 namespace Implab.ServiceHost.Unity {
4 4 public class MethodInjectionElement : AbstractMemberInjection {
5 5
6 6 [XmlAttribute("name")]
7 7 public string Name { get; set; }
8 8
9 9 [XmlElement("dependency", typeof(DependencyParameterElement))]
10 10 [XmlElement("value", typeof(ValueParameterElement))]
11 11 [XmlElement("serialized", typeof(SerializedParameterElement))]
12 12 [XmlElement("default", typeof(DefaultParameterElement))]
13 13 [XmlElement("array", typeof(ArrayParameterElement))]
14 14 public InjectionParameterElement[] Parameters { get; set; }
15 15
16 internal override void Visit(TypeRegistrationBuilder context) {
16 public override void Visit(TypeRegistrationBuilder context) {
17 17 context.Visit(this);
18 18 }
19 19 }
20 20 } No newline at end of file
@@ -1,20 +1,20
1 1 using System.Xml.Serialization;
2 2
3 3 namespace Implab.ServiceHost.Unity {
4 4 public class PropertyInjectionElement : AbstractMemberInjection {
5 5
6 6 [XmlAttribute("name")]
7 7 public string Name { get; set; }
8 8
9 9 [XmlElement("dependency", typeof(DependencyParameterElement))]
10 10 [XmlElement("value", typeof(ValueParameterElement))]
11 11 [XmlElement("serialized", typeof(SerializedParameterElement))]
12 12 [XmlElement("default", typeof(DefaultParameterElement))]
13 13 [XmlElement("array", typeof(ArrayParameterElement))]
14 14 public InjectionParameterElement Value { get; set; }
15 15
16 internal override void Visit(TypeRegistrationBuilder context) {
16 public override void Visit(TypeRegistrationBuilder context) {
17 17 context.Visit(this);
18 18 }
19 19 }
20 20 } No newline at end of file
@@ -1,36 +1,39
1 1 using System;
2 using System.Collections.Generic;
2 3 using System.Xml.Serialization;
3 4 using Unity.Lifetime;
4 5 using Unity.Registration;
5 6
6 7 namespace Implab.ServiceHost.Unity {
7 8
8 9 [XmlRoot("register", Namespace = Schema.ContainerConfigurationNamespace)]
9 public class RegisterElement : TypeAbstractRegistration {
10 public class RegisterElement : AbstractRegistration, ITypeRegistration {
10 11
11 12 /// <summary>
12 13 /// An optional type which is registered as a service in the container, must be assignable to <see cref="ProvidesType">.
13 14 /// </summary>
14 15 [XmlAttribute("mapTo")]
15 16 public string MapToType { get; set; }
16 17
17 18
18 19 [XmlElement("constructor", typeof(ConstructorInjectionElement))]
19 20 [XmlElement("property", typeof(PropertyInjectionElement))]
20 21 [XmlElement("method", typeof(MethodInjectionElement))]
21 22 public AbstractMemberInjection[] Injectors { get; set; }
22 23
23 public override Type GetImplementationType(Func<string, Type> resolver) {
24 return string.IsNullOrEmpty(MapToType) ? null : resolver(MapToType);
24 IEnumerable<ITypeMemberInjection> ITypeRegistration.MemberInjections {
25 get {
26 return Injectors;
27 }
25 28 }
26 29
27 public override void Visit(TypeRegistrationBuilder builder) {
28 if(Injectors != null)
29 foreach(var injector in Injectors)
30 injector.Visit(builder);
30 public Type GetImplementationType(ContainerBuilder builder) {
31 return builder.ResolveType(MapToType);
31 32 }
32 33
33
34 public override void Visit(ContainerBuilder builder) {
35 builder.Visit(this);
36 }
34 37 }
35 38
36 39 } No newline at end of file
@@ -1,29 +1,32
1 1 using System;
2 using System.Collections.Generic;
2 3 using System.Xml;
3 4 using System.Xml.Serialization;
4 5
5 namespace Implab.ServiceHost.Unity
6 {
7 public class SerializedElement : InstanceAbstractRegistration {
6 namespace Implab.ServiceHost.Unity {
7 public class SerializedElement : AbstractRegistration, IInstanceRegistration {
8 8 [XmlAttribute("href")]
9 9 public string Location { get; set; }
10 10
11 11 [XmlAttribute("serializedType")]
12 12 public string SerializedType { get; set; }
13 13
14 14
15 15 [XmlAnyElement]
16 16 public XmlElement[] Content { get; set; }
17 17
18 public override void Visit(InstanceRegistrationBuilder builder) {
19 base.Visit(builder);
18 public IEnumerable<IInjectionParameter> MemberInjections {
19 get {
20 yield return new SerializedParameterElement {
21 TypeName = SerializedType,
22 Location = Location,
23 Content = Content
24 };
25 }
26 }
20 27
21 var parameter = new SerializedParameterElement {
22 TypeName = SerializedType,
23 Location = Location,
24 Content = Content
25 };
26 parameter.Visit(builder.ValueBuilder);
28 public override void Visit(ContainerBuilder builder) {
29 builder.Visit(this);
27 30 }
28 31 }
29 32 } No newline at end of file
@@ -1,8 +1,10
1 using Unity.Lifetime;
2
1 3 namespace Implab.ServiceHost.Unity
2 4 {
3 5 public class SingletonLifetimeElement : LifetimeElement {
4 public override void Visit(RegistrationBuilder builder) {
5 builder.Visit(this);
6 public override LifetimeManager GetLifetime(ContainerBuilder builder) {
7 return new SingletonLifetimeManager();
6 8 }
7 9 }
8 10 } No newline at end of file
@@ -1,75 +1,81
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using Unity.Injection;
5 5 using Unity.Registration;
6 6
7 7 namespace Implab.ServiceHost.Unity {
8 8 public class TypeRegistrationBuilder : RegistrationBuilder {
9 9
10 10 readonly TypeResolver m_resolver;
11 11
12 12 readonly List<InjectionMember> m_injections = new List<InjectionMember>();
13 13
14 14 internal InjectionMember[] Injections { get { return m_injections.ToArray(); } }
15 15
16 16 public Type ImplementationType {
17 17 get;
18 18 private set;
19 19 }
20 20
21 21 internal TypeRegistrationBuilder(TypeResolver resolver, Type registrationType, Type implementationType) : base(registrationType) {
22 22 ImplementationType = implementationType;
23 23
24 24 // when registering a generic mapping, register all generic parameter names as local types
25 25 if (ImplementationType.IsGenericTypeDefinition) {
26 26 m_resolver = new TypeResolver(resolver);
27 27
28 28 foreach (var p in ImplementationType.GetGenericArguments())
29 29 m_resolver.AddMapping(p.Name, p);
30 30 } else {
31 31 m_resolver = resolver;
32 32 }
33 33 }
34 34
35 35 internal void Visit(ConstructorInjectionElement constructorInjection) {
36 36
37 37
38 38 var parameters = constructorInjection.Parameters?
39 39 .Select(x => {
40 40 var valueBuilder = new InjectionParameterBuilder(m_resolver, null);
41 41 x.Visit(valueBuilder);
42 42 return valueBuilder.Injection;
43 43 })
44 44 .ToArray();
45 45
46 46 var injection = parameters != null ? new InjectionConstructor(parameters) : new InjectionConstructor();
47 47 m_injections.Add(injection);
48 48 }
49 49
50 50 internal void Visit(MethodInjectionElement methodInjection) {
51 51 var parameters = methodInjection.Parameters?
52 52 .Select(x => {
53 53 var valueBuilder = new InjectionParameterBuilder(m_resolver, null);
54 54 x.Visit(valueBuilder);
55 55 return valueBuilder.Injection;
56 56 })
57 57 .ToArray();
58 58
59 59 var injection = parameters != null ? new InjectionMethod(methodInjection.Name, parameters) : new InjectionMethod(methodInjection.Name);
60 60 m_injections.Add(injection);
61 61 }
62 62
63 63 internal void Visit(PropertyInjectionElement propertyInjection) {
64 64 if (propertyInjection.Value == null)
65 65 throw new Exception($"A value value must be specified for the property '{propertyInjection.Name}'");
66 66
67 67 var propertyType = ImplementationType.GetProperty(propertyInjection.Name)?.PropertyType;
68 68 var valueContext = new InjectionParameterBuilder(m_resolver, propertyType);
69 69
70 70 propertyInjection.Value.Visit(valueContext);
71 71 var injection = new InjectionProperty(propertyInjection.Name, valueContext.Injection);
72 72 m_injections.Add(injection);
73 73 }
74
75 public void AddInjectionMember(InjectionMember injection) {
76 Safe.ArgumentNotNull(injection, nameof(injection));
77
78 m_injections.Add(injection);
79 }
74 80 }
75 81 } No newline at end of file
@@ -1,98 +1,99
1 1 using System;
2 2 using System.Collections.Generic;
3 3 using System.Linq;
4 4 using System.Text;
5 5 using System.Text.RegularExpressions;
6 6 using Implab.Diagnostics;
7 7
8 8 namespace Implab.ServiceHost.Unity {
9 9 using System.Diagnostics;
10 10 using static Trace<TypeResolver>;
11 11 public class TypeResolver {
12 12 readonly Dictionary<string, Type> m_cache = new Dictionary<string, Type>();
13 13
14 14 Regex _nsRx = new Regex(@"^\w+(\.\w+)*$", RegexOptions.Compiled);
15 15 readonly LinkedList<string> m_namespases = new LinkedList<string>();
16 16
17 17 internal Type Resolve(string ns, string typeName) {
18 18 var fullName = string.IsNullOrEmpty(ns) ? typeName : $"{ns}.{typeName}";
19 19
20 20 return ProbeInNamespaces(fullName);
21 21 }
22 22
23 23 public Type Resolve(TypeReference typeReference, bool throwOnFail) {
24 24 var context = new TypeResolutionContext(this, throwOnFail);
25 25 typeReference.Visit(context);
26 26 return context.MakeType();
27 27 }
28 28
29 29 public Type Resolve(string typeSpec, bool throwOnFail) {
30 Safe.ArgumentNotEmpty(typeSpec, nameof(typeSpec));
30 31 var typeReference = TypeReference.Parse(typeSpec);
31 32 return Resolve(typeReference, throwOnFail);
32 33 }
33 34
34 35 LinkedListNode<string> m_insertAt;
35 36
36 37 readonly TypeResolver m_parent;
37 38
38 39 public TypeResolver() : this(null) {
39 40 }
40 41
41 42 public TypeResolver(TypeResolver parent) {
42 43 m_parent = parent;
43 44 m_insertAt = new LinkedListNode<string>(string.Empty);
44 45 m_namespases.AddFirst(m_insertAt);
45 46 }
46 47
47 48 public void AddNamespace(string ns) {
48 49 Safe.ArgumentMatch(ns, nameof(ns), _nsRx);
49 50 if (m_insertAt != null)
50 51 m_namespases.AddAfter(m_insertAt, ns);
51 52 else
52 53 m_namespases.AddFirst(ns);
53 54 }
54 55
55 56 public void AddMapping(string typeName, Type type) {
56 57 Safe.ArgumentNotEmpty(typeName, nameof(typeName));
57 58 Safe.ArgumentNotNull(type, nameof(type));
58 59
59 60 m_cache[typeName] = type;
60 61 }
61 62
62 63 Type ProbeInNamespaces(string localName) {
63 64
64 65 Type resolved;
65 66 if (!m_cache.TryGetValue(localName, out resolved)) {
66 67 foreach (var ns in m_namespases) {
67 68 var typeName = string.IsNullOrEmpty(ns) ? localName : $"{ns}.{localName}";
68 69 resolved = Probe(typeName);
69 70 if (resolved != null) {
70 71 Log($"Probe '{localName}' -> '{resolved.FullName}'");
71 72 break;
72 73 }
73 74 }
74 75
75 76 if (resolved == null && m_parent != null)
76 77 resolved = m_parent.ProbeInNamespaces(localName);
77 78
78 79 if(resolved == null)
79 80 Log($"Probe '{localName}' failed");
80 81
81 82 m_cache[localName] = resolved;
82 83 }
83 84
84 85 return resolved;
85 86 }
86 87
87 88 Type Probe(string typeName) {
88 89 var assemblies = AppDomain.CurrentDomain.GetAssemblies();
89 90
90 91 foreach (var assembly in assemblies) {
91 92 var type = assembly.GetType(typeName);
92 93 if (type != null)
93 94 return type;
94 95 }
95 96 return null;
96 97 }
97 98 }
98 99 } No newline at end of file
@@ -1,31 +1,36
1 using System.Collections.Generic;
1 2 using System.Xml.Serialization;
2 3
3 4 namespace Implab.ServiceHost.Unity {
4 public class ValueElement : InstanceAbstractRegistration {
5
5 public class ValueElement : AbstractRegistration, IInstanceRegistration {
6
6 7 [XmlAttribute("value")]
7 8 public string Value { get; set; }
8 9
9 10 [XmlText]
10 11 public string Text { get; set; }
11 12
12 13 string GetTextValue() {
13 14 return string.IsNullOrEmpty(Value) ? Text : Value;
14 15 }
15 16
16 17 public string TypeName {
17 18 get {
18 19 return RegistrationType;
19 20 }
20 21 }
21 22
22 public override void Visit(InstanceRegistrationBuilder builder) {
23 base.Visit(builder);
24 var parameter = new ValueParameterElement {
25 Value = Value,
26 Text = Text
27 };
28 parameter.Visit(builder.ValueBuilder);
23 public IEnumerable<IInjectionParameter> MemberInjections {
24 get {
25 yield return new ValueParameterElement {
26 Value = Value,
27 Text = Text
28 };
29 }
30 }
31
32 public override void Visit(ContainerBuilder builder) {
33 builder.Visit(this);
29 34 }
30 35 }
31 36 } No newline at end of file
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 3
Under Review
author

Auto status change to "Under Review"

Approved
author

ok, latest stable version should be in default

You need to be logged in to leave comments. Login now