Mercurial > pub > ImplabNet
changeset 277:963b17c275be v3
Refactoring
Added <array> element to injection parameters
Working on registrations of factories
line wrap: on
line diff
--- a/Implab.Playground/Program.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.Playground/Program.cs Sat Apr 28 18:48:09 2018 +0300 @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Implab.Diagnostics; @@ -22,6 +23,10 @@ public string StringValue { get; set; } + public void AddRange(Foo[] items) { + Console.WriteLine($"AddRange: Foo[]"); + } + } public interface IContainer<T> { @@ -42,30 +47,44 @@ public void SetInstance(T value) { Instance = value; } + + public void AddRange(List<T> items) { + Console.WriteLine($"AddRange: {typeof(List<T>)}"); + } + + public void AddRange(T[] items) { + Console.WriteLine($"AddRange: T[] ofType {typeof(T[])}"); + } } public class Program { static void Main(string[] args) { + var listener = new SimpleTraceListener(Console.Out); + var source = Trace<TypeResolver>.TraceSource; + source.Switch.Level = SourceLevels.All; + source.Listeners.Add(listener); + var stopwatch = new Stopwatch(); stopwatch.Start(); var ctx = new ContainerBuilder(); Console.WriteLine($"Created: {stopwatch.ElapsedMilliseconds}"); - + stopwatch.Restart(); + ctx.LoadConfig("data/sample.xml"); Console.WriteLine($"Loaded: {stopwatch.ElapsedMilliseconds}"); var container = ctx.Container; - - + stopwatch.Restart(); var instace1 = container.Resolve<IContainer<string>>(); Console.WriteLine($"Resolved1: {stopwatch.ElapsedMilliseconds}"); + + stopwatch.Restart(); var instace2 = container.Resolve<IContainer<Foo>>(); - Console.WriteLine($"Resolved2: {stopwatch.ElapsedMilliseconds}"); DisplayContainerRegistrations(container);
--- a/Implab.Playground/data/sample.xml Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.Playground/data/sample.xml Sat Apr 28 18:48:09 2018 +0300 @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <container xmlns="http://implab.org/schemas/servicehost/unity.v1.xsd"> <namespace name="System"/> + <namespace name="System.Collections.Generic"/> <namespace name="Implab.Playground"/> <!-- foo1 --> @@ -21,6 +22,11 @@ </register> <register type="Foo"> + <method name="AddRange"> + <array itemsType="Foo"> + <dependency name="foo2"/> + </array> + </method> </register> <register type="IContainer{}" mapTo="Container{}"> @@ -28,6 +34,15 @@ <method name="SetInstance"> <dependency type="T"/> </method> + <method name="AddRange"> + <array itemsType="T"> + <dependency name="foo2"/> + </array> + </method> + </register> + + <register type="List{}"> + <constructor /> </register> <register type="IContainer{String}" mapTo="Container{String}"> @@ -46,4 +61,8 @@ <value name="connection1" type="String"><![CDATA[Connect me <here>!]]></value> <value name="name1" type="String" value="Hello!"/> + <factory name="foo3" type="FooFactory"> + <parameter name="FooName"><![CDATA[Wired "" objecty <> name @#$%^&]]></parameter> + </factory> + </container> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/AbstractContainerItem.cs Sat Apr 28 18:48:09 2018 +0300 @@ -0,0 +1,12 @@ +namespace Implab.ServiceHost.Unity { + + /// <summary> + /// Базовый класс для элеметов контейнера. + /// </summary> + /// <remarks> + /// XmlSerializer требует использования классов при объявлении свойств, которые будут сериализованы <see cref="ContainerElement.Items"/> + /// </remarks> + public abstract class AbstractContainerItem { + public abstract void Visit(ContainerBuilder builder); + } +} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/AbstractInjectionElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -using System; - -namespace Implab.ServiceHost.Unity { - /// <summary> - /// Base class for injections, each injection is applied to the type registration context. - /// </summary> - public abstract class AbstractInjectionElement { - internal abstract void Visit(TypeRegistrationBuilder context); - } -} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/AbstractMemberInjection.cs Sat Apr 28 18:48:09 2018 +0300 @@ -0,0 +1,10 @@ +using System; + +namespace Implab.ServiceHost.Unity { + /// <summary> + /// Base class for injections, each injection is applied to the type registration context. + /// </summary> + public abstract class AbstractMemberInjection { + internal abstract void Visit(TypeRegistrationBuilder context); + } +} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/AbstractRegistration.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/AbstractRegistration.cs Sat Apr 28 18:48:09 2018 +0300 @@ -5,7 +5,10 @@ namespace Implab.ServiceHost.Unity { - public abstract class AbstractRegistration : ContainerItemElement { + /// <summary> + /// Базовая информаци о регистрации в контейнере: тип, имя и время жизни + /// </summary> + public abstract class AbstractRegistration : AbstractContainerItem { /// <summary> /// An optional name for a registration in the container @@ -15,7 +18,7 @@ get; set; } - [XmlElement("signleton", typeof(SimgletonLifetimeElement))] + [XmlElement("signleton", typeof(SingletonLifetimeElement))] [XmlElement("context", typeof(ContextLifetimeElement))] [XmlElement("container", typeof(ContainerLifetimeElement))] [XmlElement("hierarchy", typeof(HierarchicalLifetimeElement))] @@ -27,5 +30,13 @@ [XmlAttribute("type")] public string RegistrationType { get; set; } + public virtual Type GetRegistrationType(Func<string,Type> resolver) { + return resolver(RegistrationType); + } + + public virtual void Visit(RegistrationBuilder builder) { + Lifetime?.Visit(builder); + } + } } \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/ArrayParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -0,0 +1,20 @@ +using System.Xml.Serialization; + +namespace Implab.ServiceHost.Unity +{ + public class ArrayParameterElement : InjectionParameterElement { + + [XmlAttribute("itemsType")] + public string ItemsType { get; set; } + + [XmlElement("dependency", typeof(DependencyParameterElement))] + [XmlElement("value", typeof(ValueParameterElement))] + [XmlElement("serialized", typeof(SerializedParameterElement))] + [XmlElement("default", typeof(DefaultParameterElement))] + public InjectionParameterElement[] Items { get; set; } + + public override void Visit(InjectionValueBuilder builder) { + builder.Visit(this); + } + } +} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/AssemblyElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/AssemblyElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -3,12 +3,12 @@ namespace Implab.ServiceHost.Unity { [XmlRoot("assembly", Namespace = Schema.ContainerConfigurationNamespace)] - public class AssemblyElement : ContainerItemElement { + public class AssemblyElement : AbstractContainerItem { [XmlAttribute("name")] public string AssemblyName { get; set; } - public override void Visit(ContainerBuilder context) { - context.Visit(this); + public override void Visit(ContainerBuilder builder) { + builder.Visit(this); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ConstructorInjectionElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/ConstructorInjectionElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -1,12 +1,13 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { - public class ConstructorInjectionElement : AbstractInjectionElement { + public class ConstructorInjectionElement : AbstractMemberInjection { [XmlElement("dependency", typeof(DependencyParameterElement))] [XmlElement("value", typeof(ValueParameterElement))] [XmlElement("serialized", typeof(SerializedParameterElement))] [XmlElement("default", typeof(DefaultParameterElement))] + [XmlElement("array", typeof(ArrayParameterElement))] public InjectionParameterElement[] Parameters { get; set; } internal override void Visit(TypeRegistrationBuilder context) {
--- a/Implab.ServiceHost/Unity/ContainerBuilder.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/ContainerBuilder.cs Sat Apr 28 18:48:09 2018 +0300 @@ -39,9 +39,10 @@ return m_resolver.Resolve(typeReference); } - internal void Visit(RegisterElement registerElement) { - var registrationType = ResolveType(registerElement.RegistrationType); - var implementationType = string.IsNullOrEmpty(registerElement.MapToType) ? registrationType : ResolveType(registerElement.MapToType); + + internal void Visit(TypeAbstractRegistration typeRegistration) { + var registrationType = typeRegistration.GetRegistrationType(ResolveType); + var implementationType = typeRegistration.GetImplementationType(ResolveType) ?? registrationType; var registrationContext = new TypeRegistrationBuilder( m_resolver, @@ -49,46 +50,44 @@ implementationType ); - if (registerElement.Injectors != null) { - foreach (var injector in registerElement.Injectors) { - injector.Visit(registrationContext); - } - } + typeRegistration.Visit(registrationContext); m_container.RegisterType( registrationContext.RegistrationType, registrationContext.ImplementationType, - registerElement.Name, - registerElement.Lifetime?.GetLifetimeManager(this), + typeRegistration.Name, + registrationContext.Lifetime, registrationContext.Injections ); } - internal void Visit(SerializedElement serializedElement) { - var registrationType = ResolveType(serializedElement.RegistrationType); - var valueBuilder = new InjectionValueBuilder(m_resolver, null); + internal void Visit(InstanceAbstractRegistration instanceRegistration) { + var registrationType = instanceRegistration.GetRegistrationType(ResolveType); + + var builder = new InstanceRegistrationBuilder(m_resolver, registrationType); - valueBuilder.Visit(serializedElement); - + instanceRegistration.Visit(builder); + m_container.RegisterInstance( - registrationType, - serializedElement.Name, - valueBuilder.Value, - serializedElement.Lifetime?.GetLifetimeManager(this) + builder.ValueBuilder.ValueType, + instanceRegistration.Name, + builder.ValueBuilder.Value, + builder.Lifetime ); } - internal void Visit(ValueElement valueElement) { - var registrationType = ResolveType(valueElement.RegistrationType); - var valueBuilder = new InjectionValueBuilder(m_resolver, null); + internal void Visit(FactoryAbstractRegistratrion factoryRgistration) { + var registrationType = factoryRgistration.GetRegistrationType(ResolveType); + + var builder = new FactoryRegistrationBuilder(registrationType); - valueBuilder.Visit(valueElement); - - m_container.RegisterInstance( - registrationType, - valueElement.Name, - valueBuilder.Value, - valueElement.Lifetime?.GetLifetimeManager(this) + factoryRgistration.Visit(builder); + + m_container.RegisterType( + builder.RegistrationType, + factoryRgistration.Name, + builder.Lifetime, + builder.Factory ); } @@ -111,15 +110,9 @@ public void LoadConfig(string file) { var config = m_schema.LoadFile(file); - Visit(config); + + config.Visit(this); } - - internal void Visit(ContainerElement containerElement) { - foreach (var item in containerElement.Items) - item.Visit(this); - } - - - + } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ContainerConfigurationSchema.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/ContainerConfigurationSchema.cs Sat Apr 28 18:48:09 2018 +0300 @@ -33,15 +33,15 @@ Safe.ArgumentNotNull(type, nameof(type)); Safe.ArgumentNotEmpty(name, nameof(name)); - if(!type.IsSubclassOf(typeof(ContainerItemElement))) - throw new Exception($"RegisterContainerElement '{name}': {type} must be subclass of {typeof(ContainerItemElement)}"); + if(!type.IsSubclassOf(typeof(AbstractContainerItem))) + throw new Exception($"RegisterContainerElement '{name}': {type} must be subclass of {typeof(AbstractContainerItem)}"); m_containerItems.XmlElements.Add( new XmlElementAttribute(name, type) ); } - public void RegisterContainerElement<T>(string name) where T : ContainerItemElement { + public void RegisterContainerElement<T>(string name) where T : AbstractContainerItem { RegisterContainerElement(typeof(T), name); }
--- a/Implab.ServiceHost/Unity/ContainerElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/ContainerElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -6,12 +6,14 @@ namespace Implab.ServiceHost.Unity { [XmlRoot("container", Namespace = Schema.ContainerConfigurationNamespace)] - public class ContainerElement : ContainerItemElement { + public class ContainerElement : AbstractContainerItem { - public List<ContainerItemElement> Items { get; set; } = new List<ContainerItemElement>(); + public List<AbstractContainerItem> Items { get; set; } = new List<AbstractContainerItem>(); public override void Visit(ContainerBuilder context) { - context.Visit(this); + if (Items != null) + foreach(var item in Items) + item.Visit(context); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ContainerItemElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public abstract class ContainerItemElement { - public abstract void Visit(ContainerBuilder context); - } -} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ContainerLifetimeElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/ContainerLifetimeElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -3,8 +3,8 @@ namespace Implab.ServiceHost.Unity { public class ContainerLifetimeElement : LifetimeElement { - public override LifetimeManager GetLifetimeManager(ContainerBuilder ctx) { - return new ContainerControlledLifetimeManager(); + public override void Visit(RegistrationBuilder builder) { + builder.Visit(this); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ContextLifetimeElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/ContextLifetimeElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -3,8 +3,8 @@ namespace Implab.ServiceHost.Unity { public class ContextLifetimeElement : LifetimeElement { - public override LifetimeManager GetLifetimeManager(ContainerBuilder ctx) { - return new PerResolveLifetimeManager(); + public override void Visit(RegistrationBuilder builder) { + builder.Visist(this); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/DefaultParameterElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/DefaultParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -1,12 +1,13 @@ namespace Implab.ServiceHost.Unity { - public class DefaultParameterElement : InjectionParameterElement, ITextValue { + public class DefaultParameterElement : InjectionParameterElement { public string Value { get { return null; } } public override void Visit(InjectionValueBuilder builder) { - builder.Visit(this); + var type = builder.ResolveInjectedValueType(TypeName); + builder.SetValue(type, Safe.CreateDefaultValue(type)); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/DependencyParameterElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/DependencyParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -1,7 +1,7 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { - public class DependencyParameterElement : InjectionParameterElement, IDependencyReference { + public class DependencyParameterElement : InjectionParameterElement { [XmlAttribute("name")] public string DependencyName { get; set; } @@ -10,7 +10,8 @@ public bool Optional { get; set; } public override void Visit(InjectionValueBuilder builder) { - builder.Visit(this); + var type = builder.ResolveInjectedValueType(TypeName); + builder.SetDependencyReference(type, DependencyName, Optional); } } } \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/FactoryAbstractRegistration.cs Sat Apr 28 18:48:09 2018 +0300 @@ -0,0 +1,11 @@ +namespace Implab.ServiceHost.Unity { + public class FactoryAbstractRegistratrion : AbstractRegistration { + public override void Visit(ContainerBuilder builder) { + builder.Visit(this); + } + + public virtual void Visit(FactoryRegistrationBuilder builder) { + base.Visit(builder); + } + } +} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/FactoryInjectionElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public class FactoryInjectionElement : AbstractInjectionElement { - internal override void Visit(TypeRegistrationBuilder context) { - - } - } -} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/FactoryRegistrationBuilder.cs Sat Apr 28 18:48:09 2018 +0300 @@ -0,0 +1,36 @@ +using System; +using Implab.Components; +using Unity; +using Unity.Injection; +using Unity.Registration; + +namespace Implab.ServiceHost.Unity +{ + public class FactoryRegistrationBuilder : RegistrationBuilder { + + internal InjectionMember Factory { get; private set; } + + internal FactoryRegistrationBuilder(Type registrationType) : base(registrationType) { + } + + public void SetFactoryDelegate(Delegate factory) { + Safe.ArgumentNotNull(factory, nameof(factory)); + + Factory = new DelegateInjectionFactory(factory); + } + + public void SetFactoryDependency<T>(string dependencyName, Func<T, object> factory) { + Safe.ArgumentNotNull(factory, nameof(factory)); + + Factory = new InjectionFactory((c,t,name) => { + var backend = c.Resolve<T>(dependencyName); + return factory(backend); + }); + } + + public void SetFactoryDependency<TFac, TObj>(string dependencyName) where TFac : IFactory<TObj> { + + Factory = new InjectionFactory(c => c.Resolve<TFac>(dependencyName).Create()); + } + } +} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/HierarchicalLifetimeElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/HierarchicalLifetimeElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -3,8 +3,8 @@ namespace Implab.ServiceHost.Unity { public class HierarchicalLifetimeElement : LifetimeElement { - public override LifetimeManager GetLifetimeManager(ContainerBuilder ctx) { - return new HierarchicalLifetimeManager(); + public override void Visit(RegistrationBuilder builder) { + builder.Visit(this); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/IDependencyReference.cs Sat Apr 28 00:11:38 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public interface IDependencyReference { - - string TypeName { get; } - - bool Optional { get; } - - string DependencyName { get; } - } -} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/IInjectionArray.cs Sat Apr 28 00:11:38 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace Implab.ServiceHost.Unity -{ - public interface IArrayInjectionParameter { - - string TypeName { get; } - - IEnumerable<IInjectionParameter> Items { get; } - - } -} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/IInjectionParameter.cs Sat Apr 28 00:11:38 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public interface IInjectionParameter { - void Visit(InjectionValueBuilder builder); - } -} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ISerializedValue.cs Sat Apr 28 00:11:38 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -using System.Xml; - -namespace Implab.ServiceHost.Unity { - public interface ISerializedValue { - - string TypeName { get; } - - XmlReader GetReader(); - - } -} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ITextValue.cs Sat Apr 28 00:11:38 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public interface ITextValue { - - string TypeName { get; } - - string Value { get; } - - } -} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/IncludeElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/IncludeElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -2,7 +2,7 @@ namespace Implab.ServiceHost.Unity { [XmlRoot("include", Namespace = Schema.ContainerConfigurationNamespace)] - public class IncludeElement : ContainerItemElement { + public class IncludeElement : AbstractContainerItem { [XmlAttribute("href")] public string Href { get; set; }
--- a/Implab.ServiceHost/Unity/InjectionParameterElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/InjectionParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -2,7 +2,7 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { - public abstract class InjectionParameterElement : IInjectionParameter { + public abstract class InjectionParameterElement { [XmlAttribute("type")] public string TypeName { get; set; }
--- a/Implab.ServiceHost/Unity/InjectionValueBuilder.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/InjectionValueBuilder.cs Sat Apr 28 18:48:09 2018 +0300 @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Xml.Serialization; using Unity.Injection; @@ -11,11 +13,11 @@ public Type DefaultType { get; private set; } - public Type ValueType { get; set; } + public Type ValueType { get; private set; } public object Value { get; set; } - public InjectionParameterValue Injection { + internal InjectionParameterValue Injection { get { if (Value != null) return InjectionParameterValue.ToParameter(Value); @@ -24,12 +26,12 @@ } } - internal InjectionValueBuilder(TypeResolver resolver, Type acceptsType) { + internal InjectionValueBuilder(TypeResolver resolver, Type defaultType) { m_resolver = resolver; - DefaultType = acceptsType; + DefaultType = defaultType; } - public Type ResolveType(string typeSpec) { + public Type ResolveInjectedValueType(string typeSpec) { if (string.IsNullOrEmpty(typeSpec)) { if (DefaultType == null) throw new Exception("The type must be specified"); @@ -38,27 +40,50 @@ return m_resolver.Resolve(typeSpec); } - public void Visit(ITextValue value) { - ValueType = ResolveType(value.TypeName); + public Type ResolveType(string typeSpec) { + return m_resolver.Resolve(typeSpec); + } - Value = string.IsNullOrEmpty(value.Value) ? - Safe.CreateDefaultValue(ValueType) : - TypeDescriptor.GetConverter(ValueType).ConvertFromString(value.Value); + public void SetValue(Type type, object value) { + ValueType = type; + Value = value; + } + + public void SetValue<T>(T value) { + SetValue(typeof(T), value); + } + + public void SetDependencyReference(Type type, string name, bool optional) { + ValueType = type; + Value = optional ? (object)new OptionalParameter(type, name) : new ResolvedParameter(type, name); } - public void Visit(ISerializedValue value) { - ValueType = ResolveType(value.TypeName); + internal void Visit(ArrayParameterElement arrayParameter) { + Type itemsType = null; + var arrayType = string.IsNullOrEmpty(arrayParameter.TypeName) ? null : ResolveType(arrayParameter.TypeName); - var serializer = new XmlSerializer(ValueType); + if (!string.IsNullOrEmpty(arrayParameter.ItemsType)) { + itemsType = ResolveType(arrayParameter.ItemsType); + if (arrayType == null) + arrayType = itemsType.MakeArrayType(); + } else { + itemsType = arrayType?.GetInterface(typeof(IEnumerable<>).FullName)?.GetGenericArguments()[0]; + } - using (var reader = value.GetReader()) - Value = new InjectionParameter(ValueType, serializer.Deserialize(reader)); - } + if (itemsType == null) + throw new Exception("Failed to determine array elements type"); - public void Visit(IDependencyReference value) { - ValueType = ResolveType(value.TypeName); - Value = new ResolvedParameter(ValueType, value.DependencyName); + InjectionParameterValue[] injections = (arrayParameter.Items ?? new InjectionParameterElement[0]) + .Select(x => { + var builder = new InjectionValueBuilder(m_resolver, itemsType); + x.Visit(builder); + return builder.Injection; + }) + .ToArray(); + + var array = itemsType.IsGenericParameter ? (object)new GenericResolvedArrayParameter(itemsType.Name, injections) : new ResolvedArrayParameter(itemsType, injections); + ValueType = arrayType; + Value = array; } - } } \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/InstanceAbstractRegistration.cs Sat Apr 28 18:48:09 2018 +0300 @@ -0,0 +1,12 @@ +namespace Implab.ServiceHost.Unity +{ + public abstract class InstanceAbstractRegistration : AbstractRegistration { + public override void Visit(ContainerBuilder builder) { + builder.Visit(this); + } + + public virtual void Visit(InstanceRegistrationBuilder builder) { + base.Visit(builder); + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/InstanceRegistrationBuilder.cs Sat Apr 28 18:48:09 2018 +0300 @@ -0,0 +1,13 @@ +using System; + +namespace Implab.ServiceHost.Unity +{ + public class InstanceRegistrationBuilder : RegistrationBuilder { + + public InjectionValueBuilder ValueBuilder { get; private set; } + + internal InstanceRegistrationBuilder(TypeResolver typeResolver, Type registrationType) : base(registrationType) { + ValueBuilder = new InjectionValueBuilder(typeResolver, registrationType); + } + } +} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/LifetimeElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/LifetimeElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -1,10 +1,5 @@ -using Unity.Lifetime; - -namespace Implab.ServiceHost.Unity -{ - public abstract class LifetimeElement - { - public abstract LifetimeManager GetLifetimeManager(ContainerBuilder ctx); - +namespace Implab.ServiceHost.Unity { + public abstract class LifetimeElement { + public abstract void Visit(RegistrationBuilder builder); } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/MethodInjectionElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/MethodInjectionElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -1,7 +1,7 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { - public class MethodInjectionElement : AbstractInjectionElement { + public class MethodInjectionElement : AbstractMemberInjection { [XmlAttribute("name")] public string Name { get; set; } @@ -10,6 +10,7 @@ [XmlElement("value", typeof(ValueParameterElement))] [XmlElement("serialized", typeof(SerializedParameterElement))] [XmlElement("default", typeof(DefaultParameterElement))] + [XmlElement("array", typeof(ArrayParameterElement))] public InjectionParameterElement[] Parameters { get; set; } internal override void Visit(TypeRegistrationBuilder context) {
--- a/Implab.ServiceHost/Unity/NamespaceElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/NamespaceElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -3,7 +3,7 @@ namespace Implab.ServiceHost.Unity { [XmlRoot("namespace", Namespace = Schema.ContainerConfigurationNamespace)] - public class NamespaceElement : ContainerItemElement { + public class NamespaceElement : AbstractContainerItem { [XmlAttribute("name")] public string Name { get; set; }
--- a/Implab.ServiceHost/Unity/PropertyInjectionElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/PropertyInjectionElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -1,7 +1,7 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { - public class PropertyInjectionElement : AbstractInjectionElement { + public class PropertyInjectionElement : AbstractMemberInjection { [XmlAttribute("name")] public string Name { get; set; } @@ -10,6 +10,7 @@ [XmlElement("value", typeof(ValueParameterElement))] [XmlElement("serialized", typeof(SerializedParameterElement))] [XmlElement("default", typeof(DefaultParameterElement))] + [XmlElement("array", typeof(ArrayParameterElement))] public InjectionParameterElement Value { get; set; } internal override void Visit(TypeRegistrationBuilder context) {
--- a/Implab.ServiceHost/Unity/RegisterElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/RegisterElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -6,7 +6,7 @@ namespace Implab.ServiceHost.Unity { [XmlRoot("register", Namespace = Schema.ContainerConfigurationNamespace)] - public class RegisterElement : AbstractRegistration { + public class RegisterElement : TypeAbstractRegistration { /// <summary> /// An optional type which is registered as a service in the container, must be assignable to <see cref="ProvidesType">. @@ -18,11 +18,19 @@ [XmlElement("constructor", typeof(ConstructorInjectionElement))] [XmlElement("property", typeof(PropertyInjectionElement))] [XmlElement("method", typeof(MethodInjectionElement))] - public AbstractInjectionElement[] Injectors { get; set; } + public AbstractMemberInjection[] Injectors { get; set; } + + public override Type GetImplementationType(Func<string, Type> resolver) { + return string.IsNullOrEmpty(MapToType) ? null : resolver(MapToType); + } - public override void Visit(ContainerBuilder context) { - context.Visit(this); + public override void Visit(TypeRegistrationBuilder builder) { + if(Injectors != null) + foreach(var injector in Injectors) + injector.Visit(builder); } + + } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/RegistrationBuilder.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/RegistrationBuilder.cs Sat Apr 28 18:48:09 2018 +0300 @@ -5,17 +5,39 @@ using System.Xml.Serialization; using Implab.Xml; using Unity.Injection; +using Unity.Lifetime; using Unity.Registration; namespace Implab.ServiceHost.Unity { + /// <summary> + /// Базовый класс для формирования записей в контейнере, созволяет указать время жизни для записи + /// </summary> public abstract class RegistrationBuilder { public Type RegistrationType { get; private set; } + internal LifetimeManager Lifetime { get; set; } + protected RegistrationBuilder(Type registrationType) { RegistrationType = registrationType; } + + internal void Visit(SingletonLifetimeElement simgletonLifetime) { + Lifetime = new SingletonLifetimeManager(); + } + + internal void Visit(ContainerLifetimeElement containerLifetime) { + Lifetime = new ContainerControlledLifetimeManager(); + } + + internal void Visit(HierarchicalLifetimeElement hierarchicalLifetime) { + Lifetime = new HierarchicalLifetimeManager(); + } + + internal void Visist(ContextLifetimeElement contextLifetime) { + Lifetime = new PerResolveLifetimeManager(); + } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/SerializedElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/SerializedElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -4,7 +4,7 @@ namespace Implab.ServiceHost.Unity { - public class SerializedElement : AbstractRegistration, ISerializedValue { + public class SerializedElement : InstanceAbstractRegistration { [XmlAttribute("href")] public string Location { get; set; } @@ -15,29 +15,15 @@ [XmlAnyElement] public XmlElement[] Content { get; set; } - string ISerializedValue.TypeName { - get { - return string.IsNullOrEmpty(SerializedType) ? RegistrationType : SerializedType; - } - } - - public string TypeName => throw new NotImplementedException(); - - public override void Visit(ContainerBuilder context) { - context.Visit(this); - } + public override void Visit(InstanceRegistrationBuilder builder) { + base.Visit(builder); - public XmlReader GetReader() { - if (!string.IsNullOrEmpty(Location)) - return XmlReader.Create(Location); - if (Content != null && Content.Length > 0) - return Content[0].CreateNavigator().ReadSubtree(); - - throw new Exception("No content found, expected XML document"); - } - - public void Visit(InjectionValueBuilder builder) { - throw new NotImplementedException(); + var parameter = new SerializedParameterElement { + TypeName = SerializedType, + Location = Location, + Content = Content + }; + parameter.Visit(builder.ValueBuilder); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/SerializedParameterElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/SerializedParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -5,7 +5,7 @@ namespace Implab.ServiceHost.Unity { - public class SerializedParameterElement : InjectionParameterElement, ISerializedValue { + public class SerializedParameterElement : InjectionParameterElement { [XmlAttribute("href")] public string Location { get; set; } @@ -22,7 +22,12 @@ } public override void Visit(InjectionValueBuilder builder) { - builder.Visit(this); + var type = builder.ResolveInjectedValueType(TypeName); + + var serializer = new XmlSerializer(type); + using(var reader = GetReader()) + builder.SetValue(type, serializer.Deserialize(reader)); + } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/SimgletonLifetimeElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -using Unity.Lifetime; - -namespace Implab.ServiceHost.Unity -{ - public class SimgletonLifetimeElement : LifetimeElement { - public override LifetimeManager GetLifetimeManager(ContainerBuilder ctx) { - return new SingletonLifetimeManager(); - } - } -} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/SingletonLifetimeElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -0,0 +1,8 @@ +namespace Implab.ServiceHost.Unity +{ + public class SingletonLifetimeElement : LifetimeElement { + public override void Visit(RegistrationBuilder builder) { + builder.Visit(this); + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/TypeAbstractRegistration.cs Sat Apr 28 18:48:09 2018 +0300 @@ -0,0 +1,17 @@ +using System; + +namespace Implab.ServiceHost.Unity +{ + public abstract class TypeAbstractRegistration : AbstractRegistration { + + public abstract Type GetImplementationType(Func<string,Type> resolver); + + override public void Visit(ContainerBuilder builder) { + builder.Visit(this); + } + + public virtual void Visit(TypeRegistrationBuilder builder) { + base.Visit(builder); + } + } +} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/TypeReferenceParser.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/TypeReferenceParser.cs Sat Apr 28 18:48:09 2018 +0300 @@ -17,10 +17,14 @@ CloseList, + OpenArray, + + CloseArray, + Eof } - readonly Regex _tokens = new Regex(@"([\w\+]+)|\s*([\.{},])\s*"); + readonly Regex _tokens = new Regex(@"([\w\+]+)|\s*([\.{},\[\]])\s*"); TokenType m_token; @@ -73,6 +77,12 @@ case ",": m_token = TokenType.Comma; break; + case "[": + m_token = TokenType.OpenArray; + break; + case "]": + m_token = TokenType.CloseArray; + break; } } return true; @@ -82,7 +92,7 @@ public TypeReference Parse() { var result = ReadTypeReference(); - if (ReadToken()) + if (Token != TokenType.Eof) ThrowUnexpectedToken(); return result; } @@ -142,6 +152,10 @@ return typeReference; } + int CountDimentions() { + return 0; + } + TypeReference[] ReadTypeReferenceList() { var list = new List<TypeReference>();
--- a/Implab.ServiceHost/Unity/TypeRegistrationBuilder.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/TypeRegistrationBuilder.cs Sat Apr 28 18:48:09 2018 +0300 @@ -11,7 +11,7 @@ readonly List<InjectionMember> m_injections = new List<InjectionMember>(); - public InjectionMember[] Injections { get { return m_injections.ToArray(); } } + internal InjectionMember[] Injections { get { return m_injections.ToArray(); } } public Type ImplementationType { get;
--- a/Implab.ServiceHost/Unity/TypeResolver.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/TypeResolver.cs Sat Apr 28 18:48:09 2018 +0300 @@ -5,11 +5,9 @@ using System.Text.RegularExpressions; using Implab.Diagnostics; -namespace Implab.ServiceHost.Unity -{ +namespace Implab.ServiceHost.Unity { using static Trace<TypeResolver>; - public class TypeResolver - { + public class TypeResolver { readonly Dictionary<string, Type> m_cache = new Dictionary<string, Type>(); Regex _nsRx = new Regex(@"^\w+(\.\w+)*$", RegexOptions.Compiled); @@ -49,7 +47,7 @@ var argc = reference.IsGeneric ? reference.GenericParameters.Length : 0; Type resolved; - if(!m_cache.TryGetValue(reference.ToString(), out resolved)) { + if (!m_cache.TryGetValue(reference.ToString(), out resolved)) { resolved = ResolveInternal(reference, args, argc); if (resolved == null) throw new Exception($"Failed to resolve {reference}"); @@ -65,7 +63,7 @@ Type ResolveInternal(TypeReference reference, Type[] args, int argc) { var resolved = ProbeInNamespaces( - String.Join(".", new [] { reference.Namespace, reference.TypeName }.Where(x => !string.IsNullOrEmpty(x)) ), + String.Join(".", new[] { reference.Namespace, reference.TypeName }.Where(x => !string.IsNullOrEmpty(x))), args, argc, reference.IsArray, @@ -73,17 +71,24 @@ ); if (resolved == null && m_parent != null) - resolved = m_parent.Resolve(reference); - + resolved = m_parent.ResolveInternal(reference, args, argc); + return resolved; } public Type ProbeInNamespaces(string localName, Type[] args, int argc, bool isArray, string referenceName) { foreach (var ns in m_namespases) { - var typeName = FormatName(new [] { ns, localName}, argc, args, isArray); + var typeName = FormatName(new[] { ns, localName }, argc); var resolved = Probe(typeName); if (resolved != null) { + if (args != null && args.Length > 0) { + resolved = resolved.MakeGenericType(args); + } + + if (isArray) + resolved = resolved.MakeArrayType(); + Log("Probe succeed {0} in '{1}': {2} -> {3}", referenceName, ns, typeName, resolved.AssemblyQualifiedName); return resolved; } else { @@ -97,7 +102,7 @@ Type Probe(string typeName) { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach(var assembly in assemblies) { + foreach (var assembly in assemblies) { var type = assembly.GetType(typeName); if (type != null) return type; @@ -105,25 +110,16 @@ return null; } - string FormatName(string[] parts, int argc, Type[] args, bool isArray) { + string FormatName(string[] parts, int argc) { var builder = new StringBuilder(); - + builder.Append(String.Join(".", parts.Where(x => !string.IsNullOrEmpty(x)))); if (argc > 0) { builder.Append('`'); builder.Append(argc); } - if (args!= null && args.Length > 0) { - builder.Append('['); - builder.Append(string.Join(",", args.Select(x => $"[{x.AssemblyQualifiedName}]"))); - builder.Append(']'); - } - - if(isArray) - builder.Append("[]"); - - return builder.ToString(); + return builder.ToString(); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ValueElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/ValueElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -1,7 +1,7 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { - public class ValueElement : AbstractRegistration, ITextValue { + public class ValueElement : InstanceAbstractRegistration { [XmlAttribute("value")] public string Value { get; set; } @@ -9,10 +9,8 @@ [XmlText] public string Text { get; set; } - string ITextValue.Value { - get { - return string.IsNullOrEmpty(Value) ? Text : Value; - } + string GetTextValue() { + return string.IsNullOrEmpty(Value) ? Text : Value; } public string TypeName { @@ -21,8 +19,13 @@ } } - public override void Visit(ContainerBuilder context) { - context.Visit(this); + public override void Visit(InstanceRegistrationBuilder builder) { + base.Visit(builder); + var parameter = new ValueParameterElement { + Value = Value, + Text = Text + }; + parameter.Visit(builder.ValueBuilder); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ValueParameterElement.cs Sat Apr 28 00:11:38 2018 +0300 +++ b/Implab.ServiceHost/Unity/ValueParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 @@ -1,13 +1,21 @@ +using System.ComponentModel; using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { - public class ValueParameterElement : InjectionParameterElement, ITextValue { - [XmlText] + public class ValueParameterElement : InjectionParameterElement { [XmlAttribute("value")] public string Value { get; set; } + [XmlText] + public string Text { get; set; } + + string GetTextValue() { + return string.IsNullOrEmpty(Value) ? Text : Value; + } + public override void Visit(InjectionValueBuilder builder) { - builder.Visit(this); + var type = builder.ResolveInjectedValueType(TypeName); + builder.SetValue(type, TypeDescriptor.GetConverter(type).ConvertFromString(GetTextValue())); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/readme.md Sat Apr 28 00:11:38 2018 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,100 +0,0 @@ -# XML Конфигурация IoC контейнера - -Данная библиоетка создавалась для загрузки и применения конфигурации к IoC контейнеру, она не предназначена для конфигурирования -контейнера во время выполнения, большинство контейнеров уже обладают данным функционалом. - -На данный момент поддерживается единственный вид контейнера - [Unity Container](https://unitycontainer.github.io/) - -Unity уже обладает средствами загрузки конфигурации из XML, данная библиотека позволяет решать следующие задачи - -- Конфигурация является простым Xml документом, который можно получить из любого источика и не ограничивается `.config` файлами приложения -- Включение существующе конфигурации `<include href='config.xml'/>` в текущую -- Поддержка сериализованных объектов в качестве регистраций сервисов и параметров -- Описание зависимостей может быть расширено собсвтенными элементами с произвольной структурой -- Поддержка специфиакции генериков любой вложенности `Dictionary{Foo,Bar{Int32}}` - -## Общая архитектура - -`Implab.ServiceHost.Unity` содержит в себе классы для загрузки и применения XML конфигурации к IoC контейнеру, в частности к Unity. - -1. Настраивается схема `ContainerConfigurationSchema` -2. Загружается документ и десереализуется в виде `ContainerElement` -3. Создается `ContainerBuilder` при помощи которого применяются настройки из `ContainerElement` - -`ContainerConfigurationSchema` - определяет элементы которые могут быть использованы в конфигурации контейнера, предоставляет настроенный `XmlSerializer`. - -`ContainerBuilder` - основной класс, который используется для применения конфигурации к контейнеру, добавляет записи регистрации сервисов. - -`ContainerElement` - Корневой элемент конфигурации, который загружается из документа. - -Классы заканчивающиеся словом `Builder` используются для применения конфигурации к контейнеру, -классы заканчивающиеся на `Element` содержат информацию о конфигурации и десериализуются из исходного документа. - -## Структура конфигурации контейнера - -Элемент верхнего уровня всегда `ContainerElemnt`, он используется для хранения набора элеметов, которые распознаются и выполняются `ContainerBuilder`. - -Все элементы, входящие в контейнер наследуются от абстрактного класса `ContainerItemElement`, -который позволяет получить текущий `ContainerBuilder` и выполнить над ним какие-либо действия. - -Примерами элементов контейнера могут быть - -- `<include href='config.xml'/>` - включение конфигурации из указанного места -- `<namespace name='My.App'/>` - добавление пространства имен для поиска типов -- `<register type='MyGenericType{}'/>` - добавление в контейнер регистрации типа ``My.App.MyGenericType`1`` - -Полный набор элементов, доступных для использования в контейнере определяет схема `ContainerConfigurationSchema`, -разработчик может расширить контейнер совими собственными элементами зарегистрировав их в схеме. - -Например, мы используем компоненту `MyHttpClient` для загрузки данных - -```xml - <register type="IClient" mapTo="MyHttpClient"> - <property name="proxy"> - <value>socks5://proxy1.my.company</value> - </property> - </register> -``` - -При частом использовании можно сделать описание конфигурации несколько проще, -описав новый эелемент конфигурации - -```csharp -public class MyHttpClientElement : ContainerItemElement { - - public string Proxy { get; set; } - - public override void Visit(ContainerBuilder builder) { - // создаем описание элемента - var registration = new RegistrationElement { - RegistrationType = "IClient", - ImplementationType = "My.App.MyHttpClient", - Injectors = new [] { - new PropertyInjectionElement { - Name = "Proxy", - Value = ValueParameterElement { - Value = Proxy - } - } - } - }; - - // применяем созданное описание к контейнеру - builder.Visit(registration) - } -} -``` - -Регистрируем новый элемент в схеме - -```csharp -schema.RegisterContainerElement<MyHttpClientElement>("http"); -``` - -Используем новый элемент в конфигурации - -```xml - <http> - <proxy>socks5://proxy1.my.company</propxy> - </http> -``` \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/docs/XmlConfiguration.md Sat Apr 28 18:48:09 2018 +0300 @@ -0,0 +1,100 @@ +# XML Конфигурация IoC контейнера + +Данная библиоетка создавалась для загрузки и применения конфигурации к IoC контейнеру, она не предназначена для конфигурирования +контейнера во время выполнения, большинство контейнеров уже обладают данным функционалом. + +На данный момент поддерживается единственный вид контейнера - [Unity Container](https://unitycontainer.github.io/) + +Unity уже обладает средствами загрузки конфигурации из XML, данная библиотека позволяет решать следующие задачи + +- Конфигурация является простым Xml документом, который можно получить из любого источика и не ограничивается `.config` файлами приложения +- Включение существующе конфигурации `<include href='config.xml'/>` в текущую +- Поддержка сериализованных объектов в качестве регистраций сервисов и параметров +- Описание зависимостей может быть расширено собсвтенными элементами с произвольной структурой +- Поддержка специфиакции генериков любой вложенности `Dictionary{Foo,Bar{Int32}}` + +## Общая архитектура + +`Implab.ServiceHost.Unity` содержит в себе классы для загрузки и применения XML конфигурации к IoC контейнеру, в частности к Unity. + +1. Настраивается схема `ContainerConfigurationSchema` +2. Загружается документ и десереализуется в виде `ContainerElement` +3. Создается `ContainerBuilder` при помощи которого применяются настройки из `ContainerElement` + +`ContainerConfigurationSchema` - определяет элементы которые могут быть использованы в конфигурации контейнера, предоставляет настроенный `XmlSerializer`. + +`ContainerBuilder` - основной класс, который используется для применения конфигурации к контейнеру, добавляет записи регистрации сервисов. + +`ContainerElement` - Корневой элемент конфигурации, который загружается из документа. + +Классы заканчивающиеся словом `Builder` используются для применения конфигурации к контейнеру, +классы заканчивающиеся на `Element` содержат информацию о конфигурации и десериализуются из исходного документа. + +## Структура конфигурации контейнера + +Элемент верхнего уровня всегда `ContainerElemnt`, он используется для хранения набора элеметов, которые распознаются и выполняются `ContainerBuilder`. + +Все элементы, входящие в контейнер наследуются от абстрактного класса `ContainerItemElement`, +который позволяет получить текущий `ContainerBuilder` и выполнить над ним какие-либо действия. + +Примерами элементов контейнера могут быть + +- `<include href='config.xml'/>` - включение конфигурации из указанного места +- `<namespace name='My.App'/>` - добавление пространства имен для поиска типов +- `<register type='MyGenericType{}'/>` - добавление в контейнер регистрации типа ``My.App.MyGenericType`1`` + +Полный набор элементов, доступных для использования в контейнере определяет схема `ContainerConfigurationSchema`, +разработчик может расширить контейнер совими собственными элементами зарегистрировав их в схеме. + +Например, мы используем компоненту `MyHttpClient` для загрузки данных + +```xml + <register type="IClient" mapTo="MyHttpClient"> + <property name="proxy"> + <value>socks5://proxy1.my.company</value> + </property> + </register> +``` + +При частом использовании можно сделать описание конфигурации несколько проще, +описав новый эелемент конфигурации + +```csharp +public class MyHttpClientElement : ContainerItemElement { + + public string Proxy { get; set; } + + public override void Visit(ContainerBuilder builder) { + // создаем описание элемента + var registration = new RegistrationElement { + RegistrationType = "IClient", + ImplementationType = "My.App.MyHttpClient", + Injectors = new [] { + new PropertyInjectionElement { + Name = "Proxy", + Value = ValueParameterElement { + Value = Proxy + } + } + } + }; + + // применяем созданное описание к контейнеру + builder.Visit(registration) + } +} +``` + +Регистрируем новый элемент в схеме + +```csharp +schema.RegisterContainerElement<MyHttpClientElement>("http"); +``` + +Используем новый элемент в конфигурации + +```xml + <http> + <proxy>socks5://proxy1.my.company</propxy> + </http> +``` \ No newline at end of file