Auto status change to "Under Review"
@@ -0,0 +1,12 | |||
|
1 | using System; | |
|
2 | using System.Xml.Serialization; | |
|
3 | ||
|
4 | namespace Implab.ServiceHost.Unity { | |
|
5 | public abstract class AbstractInjectionParameter : IInjectionParameter { | |
|
6 | ||
|
7 | [XmlAttribute("type")] | |
|
8 | public string TypeName { get; set; } | |
|
9 | ||
|
10 | public abstract void Visit(InjectionParameterBuilder builder); | |
|
11 | } | |
|
12 | } No newline at end of file |
@@ -0,0 +1,17 | |||
|
1 | using System; | |
|
2 | using System.Diagnostics; | |
|
3 | ||
|
4 | namespace Implab.Diagnostics | |
|
5 | { | |
|
6 | public class ChannelAdvertisementEventArgs : EventArgs { | |
|
7 | internal ChannelAdvertisementEventArgs(object channelId, TraceSource source) { | |
|
8 | ChannelId = channelId; | |
|
9 | Source = source; | |
|
10 | } | |
|
11 | ||
|
12 | public object ChannelId { get; private set; } | |
|
13 | ||
|
14 | public TraceSource Source { get; private set; } | |
|
15 | ||
|
16 | } | |
|
17 | } No newline at end of file |
@@ -1,16 +1,27 | |||
|
1 | 1 | <Project Sdk="Microsoft.NET.Sdk"> |
|
2 | 2 | |
|
3 | 3 | <PropertyGroup> |
|
4 | 4 | <TargetFrameworks>netstandard2.0;net46</TargetFrameworks> |
|
5 | 5 | <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46'">/usr/lib/mono/4.6-api/</FrameworkPathOverride> |
|
6 | <Authors>Sergey Smirnov</Authors> | |
|
7 | <Title>Implab.SrviceHost library</Title> | |
|
8 | <Description>Provides simple and flexible XML configuration for the Unity IoC</Description> | |
|
9 | <Copyright>2018 Sergey Smirnov</Copyright> | |
|
10 | <Version>1.0.0</Version> | |
|
11 | <PackageLicenseUrl>https://hg.implab.org/pub/ImplabNet/file/tip/Implab/license.txt</PackageLicenseUrl> | |
|
12 | <PackageProjectUrl>https://implab.org</PackageProjectUrl> | |
|
13 | <RepositoryUrl>https://hg.implab.org/pub/ImplabNet/</RepositoryUrl> | |
|
14 | <RepositoryType>mercurial</RepositoryType> | |
|
15 | <PackageTags>IoC; Unity; Dependency Injection</PackageTags> | |
|
16 | <TargetFrameworks>netstandard2.0;net46</TargetFrameworks> | |
|
6 | 17 | </PropertyGroup> |
|
7 | 18 | |
|
8 | 19 | <ItemGroup> |
|
9 | 20 | <PackageReference Include="Unity" Version="5.8.5" /> |
|
10 | 21 | </ItemGroup> |
|
11 | 22 | |
|
12 | 23 | <ItemGroup> |
|
13 | 24 | <ProjectReference Include="..\Implab\Implab.csproj" /> |
|
14 | 25 | </ItemGroup> |
|
15 | 26 | |
|
16 | 27 | </Project> |
@@ -1,20 +1,20 | |||
|
1 | 1 | using System.Xml.Serialization; |
|
2 | 2 | |
|
3 | 3 | namespace Implab.ServiceHost.Unity |
|
4 | 4 | { |
|
5 |
public class ArrayParameterElement : InjectionParameter |
|
|
5 | public class ArrayParameterElement : AbstractInjectionParameter { | |
|
6 | 6 | |
|
7 | 7 | [XmlAttribute("itemsType")] |
|
8 | 8 | public string ItemsType { get; set; } |
|
9 | 9 | |
|
10 | 10 | [XmlElement("dependency", typeof(DependencyParameterElement))] |
|
11 | 11 | [XmlElement("value", typeof(ValueParameterElement))] |
|
12 | 12 | [XmlElement("serialized", typeof(SerializedParameterElement))] |
|
13 | 13 | [XmlElement("default", typeof(DefaultParameterElement))] |
|
14 |
public InjectionParameter |
|
|
14 | public AbstractInjectionParameter[] Items { get; set; } | |
|
15 | 15 | |
|
16 | 16 | public override void Visit(InjectionParameterBuilder builder) { |
|
17 | 17 | builder.Visit(this); |
|
18 | 18 | } |
|
19 | 19 | } |
|
20 | 20 | } 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 |
public InjectionParameter |
|
|
11 | public AbstractInjectionParameter[] Parameters { get; set; } | |
|
12 | 12 | |
|
13 | 13 | public override void Visit(TypeRegistrationBuilder builder) { |
|
14 | 14 | builder.Visit(this); |
|
15 | 15 | } |
|
16 | 16 | } |
|
17 | 17 | } No newline at end of file |
@@ -1,13 +1,13 | |||
|
1 | 1 | namespace Implab.ServiceHost.Unity |
|
2 | 2 | { |
|
3 |
public class DefaultParameterElement : InjectionParameter |
|
|
3 | public class DefaultParameterElement : AbstractInjectionParameter { | |
|
4 | 4 | public string Value { |
|
5 | 5 | get { return null; } |
|
6 | 6 | } |
|
7 | 7 | |
|
8 | 8 | public override void Visit(InjectionParameterBuilder builder) { |
|
9 | 9 | var type = builder.ResolveInjectedValueType(TypeName); |
|
10 | 10 | builder.SetValue(type, Safe.CreateDefaultValue(type)); |
|
11 | 11 | } |
|
12 | 12 | } |
|
13 | 13 | } 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 |
public class DependencyParameterElement : InjectionParameter |
|
|
4 | public class DependencyParameterElement : AbstractInjectionParameter { | |
|
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 | 14 | builder.SetDependency(type, DependencyName, Optional); |
|
15 | 15 | } |
|
16 | 16 | } |
|
17 | 17 | } No newline at end of file |
@@ -1,72 +1,73 | |||
|
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 | 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 | FactoryType = factoryType | |
|
39 | FactoryType = factoryType, | |
|
40 | Lifetime = item.Lifetime.GetLifetime(builder) | |
|
40 | 41 | }; |
|
41 | 42 | builder.Visit(activator); |
|
42 | 43 | } |
|
43 | 44 | } else { |
|
44 | 45 | // если регистрация явно не задана, в качестве сервиса для регистрации |
|
45 | 46 | // используется тип создаваемый фабрикой, который будет добавлен в контейнер |
|
46 | 47 | // с темже именем, что и сама фабрика (разные типы могут иметь одно имя для регистрации) |
|
47 | 48 | var providedType = ( |
|
48 | 49 | factoryType.IsGenericType && factoryType.GetGenericTypeDefinition() == typeof(IFactory<>) ? |
|
49 | 50 | factoryType : |
|
50 | 51 | factoryType.GetInterface(typeof(IFactory<>).FullName) |
|
51 | 52 | )? |
|
52 | 53 | .GetGenericArguments()[0]; |
|
53 | 54 | |
|
54 | 55 | // не удалось определеить тип |
|
55 | 56 | if (providedType == null) |
|
56 | 57 | throw new ArgumentException("Failed to determine a type provided by the factory"); |
|
57 | 58 | |
|
58 | 59 | if (providedType.IsGenericParameter) |
|
59 | 60 | throw new ArgumentException("Can't register a generic type paramter as a service"); |
|
60 | 61 | |
|
61 | 62 | var activator = new FactoryActivator { |
|
62 | 63 | Name = Name, |
|
63 | 64 | RegistrationType = providedType, |
|
64 | 65 | FactoryName = Name, |
|
65 | 66 | FactoryType = factoryType |
|
66 | 67 | }; |
|
67 | 68 | |
|
68 | 69 | builder.Visit(activator); |
|
69 | 70 | } |
|
70 | 71 | } |
|
71 | 72 | } |
|
72 | 73 | } No newline at end of file |
@@ -1,137 +1,137 | |||
|
1 | 1 | using System; |
|
2 | 2 | using System.Collections; |
|
3 | 3 | using System.Collections.Generic; |
|
4 | 4 | using System.ComponentModel; |
|
5 | 5 | using System.Linq; |
|
6 | 6 | using System.Xml.Serialization; |
|
7 | 7 | using Unity.Injection; |
|
8 | 8 | |
|
9 | 9 | namespace Implab.ServiceHost.Unity { |
|
10 | 10 | |
|
11 | 11 | public class InjectionParameterBuilder { |
|
12 | 12 | |
|
13 | 13 | readonly TypeResolver m_resolver; |
|
14 | 14 | |
|
15 | 15 | public Type DefaultType { get; private set; } |
|
16 | 16 | |
|
17 | 17 | public Type ValueType { get; private set; } |
|
18 | 18 | |
|
19 | 19 | object m_value; |
|
20 | 20 | |
|
21 | 21 | public object Value { |
|
22 | 22 | get { |
|
23 | 23 | if (!ValueSpecified) |
|
24 | 24 | throw new InvalidOperationException("The regular value must be set (dependency or array are not situable in this context)"); |
|
25 | 25 | return m_value; |
|
26 | 26 | } |
|
27 | 27 | } |
|
28 | 28 | |
|
29 | 29 | public bool ValueSpecified { get; private set; } |
|
30 | 30 | |
|
31 | 31 | InjectionParameterValue m_injection; |
|
32 | 32 | |
|
33 | 33 | public InjectionParameterValue Injection { |
|
34 | 34 | get { |
|
35 | 35 | if (m_injection == null) |
|
36 | 36 | throw new InvalidOperationException("The injection parameter is not specified"); |
|
37 | 37 | return m_injection; |
|
38 | 38 | } |
|
39 | 39 | } |
|
40 | 40 | |
|
41 | 41 | public bool InjectionSpecified { |
|
42 | 42 | get { return m_injection != null; } |
|
43 | 43 | } |
|
44 | 44 | |
|
45 | 45 | internal InjectionParameterBuilder(TypeResolver resolver, Type defaultType) { |
|
46 | 46 | m_resolver = resolver; |
|
47 | 47 | DefaultType = defaultType; |
|
48 | 48 | } |
|
49 | 49 | |
|
50 | 50 | public Type ResolveInjectedValueType(string typeSpec) { |
|
51 | 51 | if (string.IsNullOrEmpty(typeSpec)) { |
|
52 | 52 | if (DefaultType == null) |
|
53 | 53 | throw new Exception("The type must be specified"); |
|
54 | 54 | return DefaultType; |
|
55 | 55 | } |
|
56 | 56 | return m_resolver.Resolve(typeSpec, true); |
|
57 | 57 | } |
|
58 | 58 | |
|
59 | 59 | public Type ResolveType(string typeSpec) { |
|
60 | 60 | return string.IsNullOrEmpty(typeSpec) ? null : m_resolver.Resolve(typeSpec, true); |
|
61 | 61 | } |
|
62 | 62 | |
|
63 | 63 | public void SetValue(Type type, object value) { |
|
64 | 64 | Safe.ArgumentNotNull(type, nameof(type)); |
|
65 | 65 | |
|
66 | 66 | ValueType = type; |
|
67 | 67 | m_value = value; |
|
68 | 68 | ValueSpecified = true; |
|
69 | 69 | |
|
70 | 70 | m_injection = new InjectionParameter(type, value); |
|
71 | 71 | } |
|
72 | 72 | |
|
73 | 73 | public void SetDependency(Type type, string name, bool optional) { |
|
74 | 74 | Safe.ArgumentNotNull(type, nameof(type)); |
|
75 | 75 | |
|
76 | 76 | ValueType = type; |
|
77 | 77 | ValueSpecified = false; |
|
78 | 78 | m_value = null; |
|
79 | 79 | |
|
80 | 80 | m_injection = optional ? (InjectionParameterValue)new OptionalParameter(type, name) : new ResolvedParameter(type, name); |
|
81 | 81 | } |
|
82 | 82 | |
|
83 | 83 | internal void Visit(ArrayParameterElement arrayParameter) { |
|
84 | 84 | Type itemsType = null; |
|
85 | 85 | var arrayType = string.IsNullOrEmpty(arrayParameter.TypeName) ? null : ResolveType(arrayParameter.TypeName); |
|
86 | 86 | |
|
87 | 87 | if (arrayType == null) |
|
88 | 88 | arrayType = DefaultType; |
|
89 | 89 | |
|
90 | 90 | |
|
91 | 91 | if (!string.IsNullOrEmpty(arrayParameter.ItemsType)) { |
|
92 | 92 | itemsType = ResolveType(arrayParameter.ItemsType); |
|
93 | 93 | arrayType = itemsType.MakeArrayType(); |
|
94 | 94 | } else { |
|
95 | 95 | itemsType = GetItemsType(arrayType); |
|
96 | 96 | } |
|
97 | 97 | |
|
98 | 98 | if (itemsType == null) |
|
99 | 99 | throw new Exception("Failed to determine array elements type"); |
|
100 | 100 | |
|
101 |
InjectionParameterValue[] injections = (arrayParameter.Items ?? new InjectionParameter |
|
|
101 | InjectionParameterValue[] injections = (arrayParameter.Items ?? new AbstractInjectionParameter[0]) | |
|
102 | 102 | .Select(x => { |
|
103 | 103 | var builder = new InjectionParameterBuilder(m_resolver, itemsType); |
|
104 | 104 | x.Visit(builder); |
|
105 | 105 | return builder.Injection; |
|
106 | 106 | }) |
|
107 | 107 | .ToArray(); |
|
108 | 108 | |
|
109 | 109 | var array = itemsType.IsGenericParameter ? |
|
110 | 110 | (InjectionParameterValue)new GenericResolvedArrayParameter(itemsType.Name, injections) : |
|
111 | 111 | new ResolvedArrayParameter(itemsType, injections); |
|
112 | 112 | |
|
113 | 113 | ValueType = arrayType; |
|
114 | 114 | m_value = null; |
|
115 | 115 | ValueSpecified = false; |
|
116 | 116 | |
|
117 | 117 | m_injection = array; |
|
118 | 118 | } |
|
119 | 119 | |
|
120 | 120 | Type GetItemsType(Type collectionType) { |
|
121 | 121 | if (collectionType == null) |
|
122 | 122 | return null; |
|
123 | 123 | |
|
124 | 124 | Type itemsType = null; |
|
125 | 125 | |
|
126 | 126 | if (collectionType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { |
|
127 | 127 | itemsType = collectionType.GetGenericArguments()[0]; |
|
128 | 128 | } else if (collectionType == typeof(IEnumerable)) { |
|
129 | 129 | itemsType = typeof(object); |
|
130 | 130 | } else { |
|
131 | 131 | itemsType = collectionType.GetInterface(typeof(IEnumerable<>).FullName)?.GetGenericArguments()[0]; |
|
132 | 132 | } |
|
133 | 133 | |
|
134 | 134 | return itemsType; |
|
135 | 135 | } |
|
136 | 136 | } |
|
137 | 137 | } 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 |
public InjectionParameter |
|
|
14 | public AbstractInjectionParameter[] Parameters { get; set; } | |
|
15 | 15 | |
|
16 | 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 |
public InjectionParameter |
|
|
14 | public AbstractInjectionParameter Value { get; set; } | |
|
15 | 15 | |
|
16 | 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,11 +1,17 | |||
|
1 | 1 | using System.Xml.Serialization; |
|
2 | 2 | |
|
3 | 3 | namespace Implab.ServiceHost.Unity { |
|
4 | 4 | public class ProvidesElement { |
|
5 | 5 | [XmlAttribute("type")] |
|
6 | 6 | public string RegistrationType { get; set; } |
|
7 | 7 | |
|
8 | 8 | [XmlAttribute("name")] |
|
9 | 9 | public string RegistrationName { get; set; } |
|
10 | ||
|
11 | [XmlElement("signleton", typeof(SingletonLifetimeElement))] | |
|
12 | [XmlElement("context", typeof(ContextLifetimeElement))] | |
|
13 | [XmlElement("container", typeof(ContainerLifetimeElement))] | |
|
14 | [XmlElement("hierarchy", typeof(HierarchicalLifetimeElement))] | |
|
15 | public LifetimeElement Lifetime {get; set;} | |
|
10 | 16 | } |
|
11 | 17 | } No newline at end of file |
@@ -1,33 +1,33 | |||
|
1 | 1 | using System; |
|
2 | 2 | using System.Xml; |
|
3 | 3 | using System.Xml.Schema; |
|
4 | 4 | using System.Xml.Serialization; |
|
5 | 5 | |
|
6 | 6 | namespace Implab.ServiceHost.Unity |
|
7 | 7 | { |
|
8 |
public class SerializedParameterElement : InjectionParameter |
|
|
8 | public class SerializedParameterElement : AbstractInjectionParameter { | |
|
9 | 9 | [XmlAttribute("href")] |
|
10 | 10 | public string Location { get; set; } |
|
11 | 11 | |
|
12 | 12 | [XmlAnyElement] |
|
13 | 13 | public XmlElement[] Content { get; set; } |
|
14 | 14 | |
|
15 | 15 | public XmlReader GetReader() { |
|
16 | 16 | if (!string.IsNullOrEmpty(Location)) |
|
17 | 17 | return XmlReader.Create(Location); |
|
18 | 18 | if (Content != null && Content.Length > 0) |
|
19 | 19 | return Content[0].CreateNavigator().ReadSubtree(); |
|
20 | 20 | |
|
21 | 21 | throw new Exception("No content found, expected XML document"); |
|
22 | 22 | } |
|
23 | 23 | |
|
24 | 24 | public override void Visit(InjectionParameterBuilder builder) { |
|
25 | 25 | var type = builder.ResolveInjectedValueType(TypeName); |
|
26 | 26 | |
|
27 | 27 | var serializer = new XmlSerializer(type); |
|
28 | 28 | using(var reader = GetReader()) |
|
29 | 29 | builder.SetValue(type, serializer.Deserialize(reader)); |
|
30 | 30 | |
|
31 | 31 | } |
|
32 | 32 | } |
|
33 | 33 | } No newline at end of file |
@@ -1,21 +1,21 | |||
|
1 | 1 | using System.ComponentModel; |
|
2 | 2 | using System.Xml.Serialization; |
|
3 | 3 | |
|
4 | 4 | namespace Implab.ServiceHost.Unity { |
|
5 |
public class ValueParameterElement : InjectionParameter |
|
|
5 | public class ValueParameterElement : AbstractInjectionParameter { | |
|
6 | 6 | [XmlAttribute("value")] |
|
7 | 7 | public string Value { get; set; } |
|
8 | 8 | |
|
9 | 9 | [XmlText] |
|
10 | 10 | public string Text { get; set; } |
|
11 | 11 | |
|
12 | 12 | string GetTextValue() { |
|
13 | 13 | return string.IsNullOrEmpty(Value) ? Text : Value; |
|
14 | 14 | } |
|
15 | 15 | |
|
16 | 16 | public override void Visit(InjectionParameterBuilder builder) { |
|
17 | 17 | var type = builder.ResolveInjectedValueType(TypeName); |
|
18 | 18 | builder.SetValue(type, TypeDescriptor.GetConverter(type).ConvertFromString(GetTextValue())); |
|
19 | 19 | } |
|
20 | 20 | } |
|
21 | 21 | } No newline at end of file |
@@ -1,100 +1,113 | |||
|
1 | 1 | # XML Конфигурация IoC контейнера |
|
2 | 2 | |
|
3 |
|
|
|
3 | Библиоетка создавалась для загрузки и применения конфигурации к IoC контейнеру, она не предназначена для конфигурирования | |
|
4 | 4 | контейнера во время выполнения, большинство контейнеров уже обладают данным функционалом. |
|
5 | 5 | |
|
6 | 6 | На данный момент поддерживается единственный вид контейнера - [Unity Container](https://unitycontainer.github.io/) |
|
7 | 7 | |
|
8 | Unity уже обладает средствами загрузки конфигурации из XML, данная библиотека позволяет решать следующие задачи | |
|
8 | Unity уже обладает средствами загрузки конфигурации из XML, данная библиотека реализует аналогичный функционал, кроме того, позволяет решать следующие задачи: | |
|
9 | 9 | |
|
10 | 10 | - Конфигурация является простым Xml документом, который можно получить из любого источика и не ограничивается `.config` файлами приложения |
|
11 | 11 | - Включение существующе конфигурации `<include href='config.xml'/>` в текущую |
|
12 | 12 | - Поддержка сериализованных объектов в качестве регистраций сервисов и параметров |
|
13 | - Описание зависимостей может быть расширено собсвтенными элементами с произвольной структурой | |
|
14 | - Поддержка специфиакции генериков любой вложенности `Dictionary{Foo,Bar{Int32}}` | |
|
13 | - Поддержка специфиакции генериков любой вложенности, например `Dictionary{Foo,Bar{Int32}}` | |
|
14 | - Поддержка фабрик в XML конфигурации | |
|
15 | - Расширение схемы XML конфигурации собственными элементами | |
|
15 | 16 | |
|
16 | 17 | ## Общая архитектура |
|
17 | 18 | |
|
18 |
`Implab.ServiceHost.Unity` содержит в себе классы для загрузки и применения XML конфигурации к IoC контейнеру |
|
|
19 | Пространство имен `Implab.ServiceHost.Unity` содержит в себе классы для загрузки и применения XML конфигурации к IoC контейнеру. | |
|
20 | ||
|
21 | Применение конфигурации к контейнеру состоит из следующих основных шагов: | |
|
19 | 22 | |
|
20 | 1. Настраивается схема `ContainerConfigurationSchema` | |
|
23 | 1. Настраивается схема `ContainerConfigurationSchema` XML конфигурации контейнера | |
|
21 | 24 | 2. Загружается документ и десереализуется в виде `ContainerElement` |
|
22 | 3. Создается `ContainerBuilder` при помощи которого применяются настройки из `ContainerElement` | |
|
25 | 3. Создается `ContainerBuilder` при помощи которого применяются настройки из `ContainerElement` к контейнеру | |
|
23 | 26 | |
|
24 |
`ContainerConfigurationSchema` |
|
|
27 | Схема `ContainerConfigurationSchema` определяет элементы документа, которые могут быть использованы в конфигурации контейнера, позволяет создать `XmlSerializer`, который используется для загрузки конфигурации. | |
|
25 | 28 | |
|
26 | `ContainerBuilder` - основной класс, который используется для применения конфигурации к контейнеру, добавляет записи регистрации сервисов. | |
|
29 | `ContainerBuilder` - основной класс, который используется для применения конфигурации к контейнеру, он добавляет записи регистрации сервисов из конфигурации в контейнер. | |
|
27 | 30 | |
|
28 | `ContainerElement` - Корневой элемент конфигурации, который загружается из документа. | |
|
31 | `ContainerElement` - Конфигурация контейнера, которая загружается из документа, состоит из директив для `ContainerBuilder`, а также из записей регистрации сервисов. | |
|
29 | 32 | |
|
30 | Классы заканчивающиеся словом `Builder` используются для применения конфигурации к контейнеру, | |
|
31 | классы заканчивающиеся на `Element` содержат информацию о конфигурации и десериализуются из исходного документа. | |
|
33 | В библиотеки приняты следующие правила именования классов: | |
|
34 | ||
|
35 | - `***Builder` используются для применения конфигурации к контейнеру, | |
|
36 | - `***Element` содержат информацию о конфигурации и десериализуются из исходного документа. | |
|
32 | 37 | |
|
33 | 38 | ## Структура конфигурации контейнера |
|
34 | 39 | |
|
35 |
Элемент верхнего уровня всегда ` |
|
|
40 | Элемент верхнего уровня всегда `container`, он используется для хранения набора элеметов, которые распознаются и выполняются `ContainerBuilder`. | |
|
36 | 41 | |
|
37 | 42 | Все элементы, входящие в контейнер наследуются от абстрактного класса `ContainerItemElement`, |
|
38 | 43 | который позволяет получить текущий `ContainerBuilder` и выполнить над ним какие-либо действия. |
|
39 | 44 | |
|
40 | Примерами элементов контейнера могут быть | |
|
45 | Основные элементы конфигурации контенейра | |
|
41 | 46 | |
|
42 | 47 | - `<include href='config.xml'/>` - включение конфигурации из указанного места |
|
43 | 48 | - `<namespace name='My.App'/>` - добавление пространства имен для поиска типов |
|
44 | 49 | - `<register type='MyGenericType{}'/>` - добавление в контейнер регистрации типа ``My.App.MyGenericType`1`` |
|
50 | - `<factory type='IFactory{Foo}' mapTo ='MyFactory'/>` - фабрика, регистрирует свой тип, а также тип `Foo` | |
|
51 | - `<serialized />` - сериализованный экземпляр объекта, использует `XmlSerializer` для десериализации | |
|
52 | - `<value />` - значение объекта в виде строки, используется для простых типов | |
|
45 | 53 | |
|
46 | 54 | Полный набор элементов, доступных для использования в контейнере определяет схема `ContainerConfigurationSchema`, |
|
47 | 55 | разработчик может расширить контейнер совими собственными элементами зарегистрировав их в схеме. |
|
48 | 56 | |
|
57 | ### register | |
|
58 | ||
|
59 | О | |
|
60 | ||
|
61 | ||
|
49 | 62 | Например, мы используем компоненту `MyHttpClient` для загрузки данных |
|
50 | 63 | |
|
51 | 64 | ```xml |
|
52 | 65 | <register type="IClient" mapTo="MyHttpClient"> |
|
53 | 66 | <property name="proxy"> |
|
54 | 67 | <value>socks5://proxy1.my.company</value> |
|
55 | 68 | </property> |
|
56 | 69 | </register> |
|
57 | 70 | ``` |
|
58 | 71 | |
|
59 | 72 | При частом использовании можно сделать описание конфигурации несколько проще, |
|
60 | 73 | описав новый эелемент конфигурации |
|
61 | 74 | |
|
62 | 75 | ```csharp |
|
63 | 76 | public class MyHttpClientElement : ContainerItemElement { |
|
64 | 77 | |
|
65 | 78 | public string Proxy { get; set; } |
|
66 | 79 | |
|
67 | 80 | public override void Visit(ContainerBuilder builder) { |
|
68 | 81 | // создаем описание элемента |
|
69 | 82 | var registration = new RegistrationElement { |
|
70 | 83 | RegistrationType = "IClient", |
|
71 | 84 | ImplementationType = "My.App.MyHttpClient", |
|
72 | 85 | Injectors = new [] { |
|
73 | 86 | new PropertyInjectionElement { |
|
74 | 87 | Name = "Proxy", |
|
75 | 88 | Value = ValueParameterElement { |
|
76 | 89 | Value = Proxy |
|
77 | 90 | } |
|
78 | 91 | } |
|
79 | 92 | } |
|
80 | 93 | }; |
|
81 | 94 | |
|
82 | 95 | // применяем созданное описание к контейнеру |
|
83 | 96 | builder.Visit(registration) |
|
84 | 97 | } |
|
85 | 98 | } |
|
86 | 99 | ``` |
|
87 | 100 | |
|
88 | 101 | Регистрируем новый элемент в схеме |
|
89 | 102 | |
|
90 | 103 | ```csharp |
|
91 | 104 | schema.RegisterContainerElement<MyHttpClientElement>("http"); |
|
92 | 105 | ``` |
|
93 | 106 | |
|
94 | 107 | Используем новый элемент в конфигурации |
|
95 | 108 | |
|
96 | 109 | ```xml |
|
97 | 110 | <http> |
|
98 | 111 | <proxy>socks5://proxy1.my.company</propxy> |
|
99 | 112 | </http> |
|
100 | 113 | ``` No newline at end of file |
@@ -1,144 +1,161 | |||
|
1 | using System; | |
|
1 | // enable System.Diagnostics trace methods | |
|
2 | #define TRACE | |
|
3 | ||
|
4 | using System; | |
|
2 | 5 | using System.Collections.Generic; |
|
3 | 6 | using System.Diagnostics; |
|
4 | 7 | using System.Linq; |
|
5 | 8 | using System.Text; |
|
6 | 9 | using System.Threading; |
|
7 | 10 | using System.Threading.Tasks; |
|
8 | 11 | |
|
9 | 12 | namespace Implab.Diagnostics { |
|
10 | 13 | public static class Trace<T> { |
|
11 | 14 | |
|
12 | public static TraceSource TraceSource { get; } = new TraceSource(typeof(T).Name); | |
|
15 | static Lazy<TraceSource> _traceSource = new Lazy<TraceSource>(CreateChannel, LazyThreadSafetyMode.ExecutionAndPublication); | |
|
16 | ||
|
17 | static int _nextId; | |
|
18 | ||
|
19 | static TraceSource CreateChannel() { | |
|
20 | var id = Interlocked.Increment(ref _nextId); | |
|
21 | return new TraceSource(typeof(T).Name); | |
|
22 | } | |
|
23 | ||
|
24 | public static TraceSource TraceSource { get { return _traceSource.Value; } } | |
|
25 | ||
|
26 | public static IDisposable Subscribe() { | |
|
27 | ||
|
28 | throw new NotImplementedException(); | |
|
29 | } | |
|
13 | 30 | |
|
14 | 31 | #if NETFX_TRACE_BUG |
|
15 | 32 | readonly static AsyncLocal<object> m_currentOperation = new AsyncLocal<object>(); |
|
16 | 33 | #endif |
|
17 | 34 | |
|
18 | 35 | /// <summary> |
|
19 | 36 | /// Starts the logical operation nested to the current operation nested to the current one. |
|
20 | 37 | /// </summary> |
|
21 | 38 | public static void StartLogicalOperation() { |
|
22 | 39 | Trace.CorrelationManager.StartLogicalOperation(); |
|
23 | 40 | |
|
24 | 41 | } |
|
25 | 42 | |
|
26 | 43 | /// <summary> |
|
27 | 44 | /// Starts the logical operation with the specified name, this name is usefull in logs. |
|
28 | 45 | /// </summary> |
|
29 | 46 | /// <param name="name">Name.</param> |
|
30 | 47 | #if NETFX_TRACE_BUG |
|
31 | 48 | public static void StartLogicalOperation(object name) { |
|
32 | 49 | m_currentOperation.Value = name; |
|
33 | 50 | Trace.CorrelationManager.StartLogicalOperation(name); |
|
34 | 51 | } |
|
35 | 52 | #else |
|
36 | 53 | public static void StartLogicalOperation(object name) { |
|
37 | 54 | Trace.CorrelationManager.StartLogicalOperation(name); |
|
38 | 55 | } |
|
39 | 56 | #endif |
|
40 | 57 | |
|
41 | 58 | /// <summary> |
|
42 | 59 | /// Ends the logical operation and restores the previous one. |
|
43 | 60 | /// </summary> |
|
44 | 61 | public static void StopLogicalOperation() { |
|
45 | 62 | Trace.CorrelationManager.StopLogicalOperation(); |
|
46 | 63 | } |
|
47 | 64 | |
|
48 | 65 | /// <summary> |
|
49 | 66 | /// Writes a debug message. |
|
50 | 67 | /// </summary> |
|
51 | 68 | /// <param name="format">Format.</param> |
|
52 | 69 | /// <param name="arguments">Arguments.</param> |
|
53 | 70 | [Conditional("DEBUG")] |
|
54 | 71 | public static void Debug(string format, params object[] arguments) { |
|
55 | 72 | |
|
56 | 73 | } |
|
57 | 74 | |
|
58 | 75 | /// <summary> |
|
59 | 76 | /// Writes an informational message. |
|
60 | 77 | /// </summary> |
|
61 | 78 | /// <param name="format">Format.</param> |
|
62 | 79 | /// <param name="arguments">Arguments.</param> |
|
63 | 80 | [Conditional("TRACE")] |
|
64 | 81 | public static void Log(string format, params object[] arguments) { |
|
65 | 82 | TraceSource.TraceEvent(TraceEventType.Information, 0, format, arguments); |
|
66 | 83 | } |
|
67 | 84 | |
|
68 | 85 | /// <summary> |
|
69 | 86 | /// Writes a warning message. |
|
70 | 87 | /// </summary> |
|
71 | 88 | /// <param name="format">Format.</param> |
|
72 | 89 | /// <param name="arguments">Arguments.</param> |
|
73 | 90 | public static void Warn(string format, params object[] arguments) { |
|
74 | 91 | TraceSource.TraceEvent(TraceEventType.Warning, 0, format, arguments); |
|
75 | 92 | } |
|
76 | 93 | |
|
77 | 94 | /// <summary> |
|
78 | 95 | /// Writes a error message. |
|
79 | 96 | /// </summary> |
|
80 | 97 | /// <param name="format">Format.</param> |
|
81 | 98 | /// <param name="arguments">Arguments.</param> |
|
82 | 99 | public static void Error(string format, params object[] arguments) { |
|
83 | 100 | TraceSource.TraceEvent(TraceEventType.Error, 0, format, arguments); |
|
84 | 101 | } |
|
85 | 102 | |
|
86 | 103 | public static void Error(Exception err) { |
|
87 | 104 | TraceSource.TraceData(TraceEventType.Error, 0, err); |
|
88 | 105 | } |
|
89 | 106 | |
|
90 | 107 | /// <summary> |
|
91 | 108 | /// This method save the current activity, and transfers to the specified activity, |
|
92 | 109 | /// emits <see cref="TraceEventType.Start"/> and returns a scope of the new |
|
93 | 110 | /// activity. |
|
94 | 111 | /// </summary> |
|
95 | 112 | /// <param name="activityName">The name of the new activity/</param> |
|
96 | 113 | /// <param name="activityId">The identifier of the activity to which |
|
97 | 114 | /// the control will be transferred</param> |
|
98 | 115 | /// <returns>A scope of the new activity, dispose it to transfer |
|
99 | 116 | /// the control back to the original activity.</returns> |
|
100 | 117 | public static ActivityScope TransferActivity(string activityName, Guid activityId) { |
|
101 | 118 | var prev = Trace.CorrelationManager.ActivityId; |
|
102 | 119 | |
|
103 | 120 | TraceSource.TraceTransfer(0, "Transfer", activityId); |
|
104 | 121 | Trace.CorrelationManager.ActivityId = activityId; |
|
105 | 122 | TraceSource.TraceEvent(TraceEventType.Start, 0, activityName); |
|
106 | 123 | |
|
107 | 124 | return new ActivityScope(TraceSource, prev, 0, activityName); |
|
108 | 125 | } |
|
109 | 126 | |
|
110 | 127 | /// <summary> |
|
111 | 128 | /// Emits <see cref="TraceEventType.Start"/> and returns a scope of the |
|
112 | 129 | /// activity. |
|
113 | 130 | /// </summary> |
|
114 | 131 | /// <param name="activityName">The name of the activity to start</param> |
|
115 | 132 | /// <returns>A scope of the new activity, dispose it to emit |
|
116 | 133 | /// <see cref="TraceEventType.Stop"/> for the current activity.</returns> |
|
117 | 134 | public static ActivityScope StartActivity(string activityName) { |
|
118 | 135 | if (Trace.CorrelationManager.ActivityId == Guid.Empty) |
|
119 | 136 | Trace.CorrelationManager.ActivityId = Guid.NewGuid(); |
|
120 | 137 | |
|
121 | 138 | var prev = Trace.CorrelationManager.ActivityId; |
|
122 | 139 | |
|
123 | 140 | TraceSource.TraceEvent(TraceEventType.Start, 0, activityName); |
|
124 | 141 | return new ActivityScope(TraceSource, prev, 0, activityName); |
|
125 | 142 | } |
|
126 | 143 | |
|
127 | 144 | /// <summary> |
|
128 | 145 | /// Creates new <see cref="LogicalOperation(string)"/> and calls |
|
129 | 146 | /// to <see cref="CorrelationManager.StartLogicalOperation(object)"/> |
|
130 | 147 | /// passing the created operation as identity. Calls |
|
131 | 148 | /// <see cref="TraceSource.TraceData(TraceEventType, int, object)"/> |
|
132 | 149 | /// to notify listeners on operation start. |
|
133 | 150 | /// </summary> |
|
134 | 151 | /// <param name="name">The name of the logical operation.</param> |
|
135 | 152 | /// <returns>Logical operation scope, disposing it will stop |
|
136 | 153 | /// logical operation and notify trace listeners.</returns> |
|
137 | 154 | public static LogicalOperationScope LogicalOperation(string name) { |
|
138 | 155 | var operation = new LogicalOperation(name); |
|
139 | 156 | TraceSource.TraceData(TraceEventType.Information, TraceEventCodes.StartLogicalOperation, operation); |
|
140 | 157 | StartLogicalOperation(operation); |
|
141 | 158 | return new LogicalOperationScope(TraceSource, operation); |
|
142 | 159 | } |
|
143 | 160 | } |
|
144 | 161 | } |
@@ -1,26 +1,26 | |||
|
1 | 1 | <Project Sdk="Microsoft.NET.Sdk"> |
|
2 | 2 | |
|
3 | 3 | <PropertyGroup> |
|
4 | 4 | <Authors>Sergey Smirnov</Authors> |
|
5 | 5 | <Title>Implab library</Title> |
|
6 | 6 | <Description>Provides some helper clesses like XML serialization helpers, JSON XML reader, |
|
7 | 7 | JSON pull-parser, ECMA-style promises, lightweight synchonization routines Signal |
|
8 | 8 | and SharedLock, Trace helpers on top of System.Diagnostics, ObjectPool etc. |
|
9 | 9 | </Description> |
|
10 | 10 | <Copyright>2012-2018 Sergey Smirnov</Copyright> |
|
11 |
<Version>3.0.1 |
|
|
11 | <Version>3.0.12</Version> | |
|
12 | 12 | <PackageLicenseUrl>https://hg.implab.org/pub/ImplabNet/file/tip/Implab/license.txt</PackageLicenseUrl> |
|
13 | 13 | <PackageProjectUrl>https://implab.org</PackageProjectUrl> |
|
14 | 14 | <RepositoryUrl>https://hg.implab.org/pub/ImplabNet/</RepositoryUrl> |
|
15 | 15 | <RepositoryType>mercurial</RepositoryType> |
|
16 | 16 | <PackageTags>IMPLAB;Json pull-parser;Json Xml;async;diagnostics;serialization;</PackageTags> |
|
17 | 17 | <TargetFrameworks>netstandard2.0;net46</TargetFrameworks> |
|
18 | 18 | <FrameworkPathOverride Condition="'$(TargetFramework)'=='net46' and '$(OSTYPE)'=='linux'">/usr/lib/mono/4.5/</FrameworkPathOverride> |
|
19 | 19 | <DefineConstants Condition="'$(TargetFramework)'=='net46'">NETFX_TRACE_BUG;$(DefineConstants)</DefineConstants> |
|
20 | 20 | </PropertyGroup> |
|
21 | 21 | |
|
22 | 22 | <ItemGroup> |
|
23 | 23 | <EmbeddedResource Include="Xml\json.xsl"/> |
|
24 | 24 | </ItemGroup> |
|
25 | 25 | |
|
26 | 26 | </Project> |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
General Comments 3
ok, latest stable version should be in default
You need to be logged in to leave comments.
Login now