diff --git a/Implab.Playground/Program.cs b/Implab.Playground/Program.cs --- a/Implab.Playground/Program.cs +++ b/Implab.Playground/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Implab.Diagnostics; @@ -22,6 +23,10 @@ namespace Implab.Playground { public string StringValue { get; set; } + public void AddRange(Foo[] items) { + Console.WriteLine($"AddRange: Foo[]"); + } + } public interface IContainer { @@ -42,30 +47,44 @@ namespace Implab.Playground { public void SetInstance(T value) { Instance = value; } + + public void AddRange(List items) { + Console.WriteLine($"AddRange: {typeof(List)}"); + } + + 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.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>(); Console.WriteLine($"Resolved1: {stopwatch.ElapsedMilliseconds}"); + + stopwatch.Restart(); var instace2 = container.Resolve>(); - Console.WriteLine($"Resolved2: {stopwatch.ElapsedMilliseconds}"); DisplayContainerRegistrations(container); diff --git a/Implab.Playground/data/sample.xml b/Implab.Playground/data/sample.xml --- a/Implab.Playground/data/sample.xml +++ b/Implab.Playground/data/sample.xml @@ -1,6 +1,7 @@ + @@ -21,6 +22,11 @@ + + + + + @@ -28,6 +34,15 @@ + + + + + + + + + @@ -46,4 +61,8 @@ !]]> + + name @#$%^&]]> + + \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/AbstractContainerItem.cs b/Implab.ServiceHost/Unity/AbstractContainerItem.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/AbstractContainerItem.cs @@ -0,0 +1,12 @@ +namespace Implab.ServiceHost.Unity { + + /// + /// Базовый класс для элеметов контейнера. + /// + /// + /// XmlSerializer требует использования классов при объявлении свойств, которые будут сериализованы + /// + public abstract class AbstractContainerItem { + public abstract void Visit(ContainerBuilder builder); + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/AbstractInjectionElement.cs b/Implab.ServiceHost/Unity/AbstractMemberInjection.cs rename from Implab.ServiceHost/Unity/AbstractInjectionElement.cs rename to Implab.ServiceHost/Unity/AbstractMemberInjection.cs --- a/Implab.ServiceHost/Unity/AbstractInjectionElement.cs +++ b/Implab.ServiceHost/Unity/AbstractMemberInjection.cs @@ -4,7 +4,7 @@ namespace Implab.ServiceHost.Unity { /// /// Base class for injections, each injection is applied to the type registration context. /// - public abstract class AbstractInjectionElement { + public abstract class AbstractMemberInjection { internal abstract void Visit(TypeRegistrationBuilder context); } } \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/AbstractRegistration.cs b/Implab.ServiceHost/Unity/AbstractRegistration.cs --- a/Implab.ServiceHost/Unity/AbstractRegistration.cs +++ b/Implab.ServiceHost/Unity/AbstractRegistration.cs @@ -5,7 +5,10 @@ using Unity.Registration; namespace Implab.ServiceHost.Unity { - public abstract class AbstractRegistration : ContainerItemElement { + /// + /// Базовая информаци о регистрации в контейнере: тип, имя и время жизни + /// + public abstract class AbstractRegistration : AbstractContainerItem { /// /// An optional name for a registration in the container @@ -15,7 +18,7 @@ namespace Implab.ServiceHost.Unity 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 @@ namespace Implab.ServiceHost.Unity [XmlAttribute("type")] public string RegistrationType { get; set; } + public virtual Type GetRegistrationType(Func resolver) { + return resolver(RegistrationType); + } + + public virtual void Visit(RegistrationBuilder builder) { + Lifetime?.Visit(builder); + } + } } \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/ArrayParameterElement.cs b/Implab.ServiceHost/Unity/ArrayParameterElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/ArrayParameterElement.cs @@ -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 diff --git a/Implab.ServiceHost/Unity/AssemblyElement.cs b/Implab.ServiceHost/Unity/AssemblyElement.cs --- a/Implab.ServiceHost/Unity/AssemblyElement.cs +++ b/Implab.ServiceHost/Unity/AssemblyElement.cs @@ -3,12 +3,12 @@ using System.Xml.Serialization; 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 diff --git a/Implab.ServiceHost/Unity/ConstructorInjectionElement.cs b/Implab.ServiceHost/Unity/ConstructorInjectionElement.cs --- a/Implab.ServiceHost/Unity/ConstructorInjectionElement.cs +++ b/Implab.ServiceHost/Unity/ConstructorInjectionElement.cs @@ -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) { diff --git a/Implab.ServiceHost/Unity/ContainerBuilder.cs b/Implab.ServiceHost/Unity/ContainerBuilder.cs --- a/Implab.ServiceHost/Unity/ContainerBuilder.cs +++ b/Implab.ServiceHost/Unity/ContainerBuilder.cs @@ -39,9 +39,10 @@ namespace Implab.ServiceHost.Unity { 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 @@ namespace Implab.ServiceHost.Unity { 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 @@ namespace Implab.ServiceHost.Unity { 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 diff --git a/Implab.ServiceHost/Unity/ContainerConfigurationSchema.cs b/Implab.ServiceHost/Unity/ContainerConfigurationSchema.cs --- a/Implab.ServiceHost/Unity/ContainerConfigurationSchema.cs +++ b/Implab.ServiceHost/Unity/ContainerConfigurationSchema.cs @@ -33,15 +33,15 @@ namespace Implab.ServiceHost.Unity { 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(string name) where T : ContainerItemElement { + public void RegisterContainerElement(string name) where T : AbstractContainerItem { RegisterContainerElement(typeof(T), name); } diff --git a/Implab.ServiceHost/Unity/ContainerElement.cs b/Implab.ServiceHost/Unity/ContainerElement.cs --- a/Implab.ServiceHost/Unity/ContainerElement.cs +++ b/Implab.ServiceHost/Unity/ContainerElement.cs @@ -6,12 +6,14 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { [XmlRoot("container", Namespace = Schema.ContainerConfigurationNamespace)] - public class ContainerElement : ContainerItemElement { + public class ContainerElement : AbstractContainerItem { - public List Items { get; set; } = new List(); + public List Items { get; set; } = new List(); 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 diff --git a/Implab.ServiceHost/Unity/ContainerItemElement.cs b/Implab.ServiceHost/Unity/ContainerItemElement.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/ContainerItemElement.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public abstract class ContainerItemElement { - public abstract void Visit(ContainerBuilder context); - } -} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/ContainerLifetimeElement.cs b/Implab.ServiceHost/Unity/ContainerLifetimeElement.cs --- a/Implab.ServiceHost/Unity/ContainerLifetimeElement.cs +++ b/Implab.ServiceHost/Unity/ContainerLifetimeElement.cs @@ -3,8 +3,8 @@ using Unity.Lifetime; 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 diff --git a/Implab.ServiceHost/Unity/ContextLifetimeElement.cs b/Implab.ServiceHost/Unity/ContextLifetimeElement.cs --- a/Implab.ServiceHost/Unity/ContextLifetimeElement.cs +++ b/Implab.ServiceHost/Unity/ContextLifetimeElement.cs @@ -3,8 +3,8 @@ using Unity.Lifetime; 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 diff --git a/Implab.ServiceHost/Unity/DefaultParameterElement.cs b/Implab.ServiceHost/Unity/DefaultParameterElement.cs --- a/Implab.ServiceHost/Unity/DefaultParameterElement.cs +++ b/Implab.ServiceHost/Unity/DefaultParameterElement.cs @@ -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 diff --git a/Implab.ServiceHost/Unity/DependencyParameterElement.cs b/Implab.ServiceHost/Unity/DependencyParameterElement.cs --- a/Implab.ServiceHost/Unity/DependencyParameterElement.cs +++ b/Implab.ServiceHost/Unity/DependencyParameterElement.cs @@ -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 @@ namespace Implab.ServiceHost.Unity { 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 diff --git a/Implab.ServiceHost/Unity/FactoryAbstractRegistration.cs b/Implab.ServiceHost/Unity/FactoryAbstractRegistration.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/FactoryAbstractRegistration.cs @@ -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 diff --git a/Implab.ServiceHost/Unity/FactoryInjectionElement.cs b/Implab.ServiceHost/Unity/FactoryInjectionElement.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/FactoryInjectionElement.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public class FactoryInjectionElement : AbstractInjectionElement { - internal override void Visit(TypeRegistrationBuilder context) { - - } - } -} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/FactoryRegistrationBuilder.cs b/Implab.ServiceHost/Unity/FactoryRegistrationBuilder.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/FactoryRegistrationBuilder.cs @@ -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(string dependencyName, Func factory) { + Safe.ArgumentNotNull(factory, nameof(factory)); + + Factory = new InjectionFactory((c,t,name) => { + var backend = c.Resolve(dependencyName); + return factory(backend); + }); + } + + public void SetFactoryDependency(string dependencyName) where TFac : IFactory { + + Factory = new InjectionFactory(c => c.Resolve(dependencyName).Create()); + } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/HierarchicalLifetimeElement.cs b/Implab.ServiceHost/Unity/HierarchicalLifetimeElement.cs --- a/Implab.ServiceHost/Unity/HierarchicalLifetimeElement.cs +++ b/Implab.ServiceHost/Unity/HierarchicalLifetimeElement.cs @@ -3,8 +3,8 @@ using Unity.Lifetime; 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 diff --git a/Implab.ServiceHost/Unity/IDependencyReference.cs b/Implab.ServiceHost/Unity/IDependencyReference.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/IDependencyReference.cs +++ /dev/null @@ -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 diff --git a/Implab.ServiceHost/Unity/IInjectionArray.cs b/Implab.ServiceHost/Unity/IInjectionArray.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/IInjectionArray.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace Implab.ServiceHost.Unity -{ - public interface IArrayInjectionParameter { - - string TypeName { get; } - - IEnumerable Items { get; } - - } -} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/IInjectionParameter.cs b/Implab.ServiceHost/Unity/IInjectionParameter.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/IInjectionParameter.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public interface IInjectionParameter { - void Visit(InjectionValueBuilder builder); - } -} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/ISerializedValue.cs b/Implab.ServiceHost/Unity/ISerializedValue.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/ISerializedValue.cs +++ /dev/null @@ -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 diff --git a/Implab.ServiceHost/Unity/ITextValue.cs b/Implab.ServiceHost/Unity/ITextValue.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/ITextValue.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public interface ITextValue { - - string TypeName { get; } - - string Value { get; } - - } -} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/IncludeElement.cs b/Implab.ServiceHost/Unity/IncludeElement.cs --- a/Implab.ServiceHost/Unity/IncludeElement.cs +++ b/Implab.ServiceHost/Unity/IncludeElement.cs @@ -2,7 +2,7 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { [XmlRoot("include", Namespace = Schema.ContainerConfigurationNamespace)] - public class IncludeElement : ContainerItemElement { + public class IncludeElement : AbstractContainerItem { [XmlAttribute("href")] public string Href { get; set; } diff --git a/Implab.ServiceHost/Unity/InjectionParameterElement.cs b/Implab.ServiceHost/Unity/InjectionParameterElement.cs --- a/Implab.ServiceHost/Unity/InjectionParameterElement.cs +++ b/Implab.ServiceHost/Unity/InjectionParameterElement.cs @@ -2,7 +2,7 @@ using System; using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { - public abstract class InjectionParameterElement : IInjectionParameter { + public abstract class InjectionParameterElement { [XmlAttribute("type")] public string TypeName { get; set; } diff --git a/Implab.ServiceHost/Unity/InjectionValueBuilder.cs b/Implab.ServiceHost/Unity/InjectionValueBuilder.cs --- a/Implab.ServiceHost/Unity/InjectionValueBuilder.cs +++ b/Implab.ServiceHost/Unity/InjectionValueBuilder.cs @@ -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 @@ namespace Implab.ServiceHost.Unity { 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 @@ namespace Implab.ServiceHost.Unity { } } - 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 @@ namespace Implab.ServiceHost.Unity { 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 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 diff --git a/Implab.ServiceHost/Unity/InstanceAbstractRegistration.cs b/Implab.ServiceHost/Unity/InstanceAbstractRegistration.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/InstanceAbstractRegistration.cs @@ -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 diff --git a/Implab.ServiceHost/Unity/InstanceRegistrationBuilder.cs b/Implab.ServiceHost/Unity/InstanceRegistrationBuilder.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/InstanceRegistrationBuilder.cs @@ -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 diff --git a/Implab.ServiceHost/Unity/LifetimeElement.cs b/Implab.ServiceHost/Unity/LifetimeElement.cs --- a/Implab.ServiceHost/Unity/LifetimeElement.cs +++ b/Implab.ServiceHost/Unity/LifetimeElement.cs @@ -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 diff --git a/Implab.ServiceHost/Unity/MethodInjectionElement.cs b/Implab.ServiceHost/Unity/MethodInjectionElement.cs --- a/Implab.ServiceHost/Unity/MethodInjectionElement.cs +++ b/Implab.ServiceHost/Unity/MethodInjectionElement.cs @@ -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 @@ namespace Implab.ServiceHost.Unity { [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) { diff --git a/Implab.ServiceHost/Unity/NamespaceElement.cs b/Implab.ServiceHost/Unity/NamespaceElement.cs --- a/Implab.ServiceHost/Unity/NamespaceElement.cs +++ b/Implab.ServiceHost/Unity/NamespaceElement.cs @@ -3,7 +3,7 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { [XmlRoot("namespace", Namespace = Schema.ContainerConfigurationNamespace)] - public class NamespaceElement : ContainerItemElement { + public class NamespaceElement : AbstractContainerItem { [XmlAttribute("name")] public string Name { get; set; } diff --git a/Implab.ServiceHost/Unity/PropertyInjectionElement.cs b/Implab.ServiceHost/Unity/PropertyInjectionElement.cs --- a/Implab.ServiceHost/Unity/PropertyInjectionElement.cs +++ b/Implab.ServiceHost/Unity/PropertyInjectionElement.cs @@ -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 @@ namespace Implab.ServiceHost.Unity { [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) { diff --git a/Implab.ServiceHost/Unity/RegisterElement.cs b/Implab.ServiceHost/Unity/RegisterElement.cs --- a/Implab.ServiceHost/Unity/RegisterElement.cs +++ b/Implab.ServiceHost/Unity/RegisterElement.cs @@ -6,7 +6,7 @@ using Unity.Registration; namespace Implab.ServiceHost.Unity { [XmlRoot("register", Namespace = Schema.ContainerConfigurationNamespace)] - public class RegisterElement : AbstractRegistration { + public class RegisterElement : TypeAbstractRegistration { /// /// An optional type which is registered as a service in the container, must be assignable to . @@ -18,11 +18,19 @@ namespace Implab.ServiceHost.Unity { [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 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 diff --git a/Implab.ServiceHost/Unity/RegistrationBuilder.cs b/Implab.ServiceHost/Unity/RegistrationBuilder.cs --- a/Implab.ServiceHost/Unity/RegistrationBuilder.cs +++ b/Implab.ServiceHost/Unity/RegistrationBuilder.cs @@ -5,17 +5,39 @@ using System.Linq; using System.Xml.Serialization; using Implab.Xml; using Unity.Injection; +using Unity.Lifetime; using Unity.Registration; namespace Implab.ServiceHost.Unity { + /// + /// Базовый класс для формирования записей в контейнере, созволяет указать время жизни для записи + /// 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 diff --git a/Implab.ServiceHost/Unity/SerializedElement.cs b/Implab.ServiceHost/Unity/SerializedElement.cs --- a/Implab.ServiceHost/Unity/SerializedElement.cs +++ b/Implab.ServiceHost/Unity/SerializedElement.cs @@ -4,7 +4,7 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { - public class SerializedElement : AbstractRegistration, ISerializedValue { + public class SerializedElement : InstanceAbstractRegistration { [XmlAttribute("href")] public string Location { get; set; } @@ -15,29 +15,15 @@ namespace Implab.ServiceHost.Unity [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 diff --git a/Implab.ServiceHost/Unity/SerializedParameterElement.cs b/Implab.ServiceHost/Unity/SerializedParameterElement.cs --- a/Implab.ServiceHost/Unity/SerializedParameterElement.cs +++ b/Implab.ServiceHost/Unity/SerializedParameterElement.cs @@ -5,7 +5,7 @@ using System.Xml.Serialization; namespace Implab.ServiceHost.Unity { - public class SerializedParameterElement : InjectionParameterElement, ISerializedValue { + public class SerializedParameterElement : InjectionParameterElement { [XmlAttribute("href")] public string Location { get; set; } @@ -22,7 +22,12 @@ namespace Implab.ServiceHost.Unity } 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 diff --git a/Implab.ServiceHost/Unity/SimgletonLifetimeElement.cs b/Implab.ServiceHost/Unity/SingletonLifetimeElement.cs rename from Implab.ServiceHost/Unity/SimgletonLifetimeElement.cs rename to Implab.ServiceHost/Unity/SingletonLifetimeElement.cs --- a/Implab.ServiceHost/Unity/SimgletonLifetimeElement.cs +++ b/Implab.ServiceHost/Unity/SingletonLifetimeElement.cs @@ -1,10 +1,8 @@ -using Unity.Lifetime; - namespace Implab.ServiceHost.Unity { - public class SimgletonLifetimeElement : LifetimeElement { - public override LifetimeManager GetLifetimeManager(ContainerBuilder ctx) { - return new SingletonLifetimeManager(); + public class SingletonLifetimeElement : LifetimeElement { + public override void Visit(RegistrationBuilder builder) { + builder.Visit(this); } } } \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/TypeAbstractRegistration.cs b/Implab.ServiceHost/Unity/TypeAbstractRegistration.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/TypeAbstractRegistration.cs @@ -0,0 +1,17 @@ +using System; + +namespace Implab.ServiceHost.Unity +{ + public abstract class TypeAbstractRegistration : AbstractRegistration { + + public abstract Type GetImplementationType(Func 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 diff --git a/Implab.ServiceHost/Unity/TypeReferenceParser.cs b/Implab.ServiceHost/Unity/TypeReferenceParser.cs --- a/Implab.ServiceHost/Unity/TypeReferenceParser.cs +++ b/Implab.ServiceHost/Unity/TypeReferenceParser.cs @@ -17,10 +17,14 @@ namespace Implab.ServiceHost.Unity { 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 @@ namespace Implab.ServiceHost.Unity { case ",": m_token = TokenType.Comma; break; + case "[": + m_token = TokenType.OpenArray; + break; + case "]": + m_token = TokenType.CloseArray; + break; } } return true; @@ -82,7 +92,7 @@ namespace Implab.ServiceHost.Unity { public TypeReference Parse() { var result = ReadTypeReference(); - if (ReadToken()) + if (Token != TokenType.Eof) ThrowUnexpectedToken(); return result; } @@ -142,6 +152,10 @@ namespace Implab.ServiceHost.Unity { return typeReference; } + int CountDimentions() { + return 0; + } + TypeReference[] ReadTypeReferenceList() { var list = new List(); diff --git a/Implab.ServiceHost/Unity/TypeRegistrationBuilder.cs b/Implab.ServiceHost/Unity/TypeRegistrationBuilder.cs --- a/Implab.ServiceHost/Unity/TypeRegistrationBuilder.cs +++ b/Implab.ServiceHost/Unity/TypeRegistrationBuilder.cs @@ -11,7 +11,7 @@ namespace Implab.ServiceHost.Unity { readonly List m_injections = new List(); - public InjectionMember[] Injections { get { return m_injections.ToArray(); } } + internal InjectionMember[] Injections { get { return m_injections.ToArray(); } } public Type ImplementationType { get; diff --git a/Implab.ServiceHost/Unity/TypeResolver.cs b/Implab.ServiceHost/Unity/TypeResolver.cs --- a/Implab.ServiceHost/Unity/TypeResolver.cs +++ b/Implab.ServiceHost/Unity/TypeResolver.cs @@ -5,11 +5,9 @@ using System.Text; using System.Text.RegularExpressions; using Implab.Diagnostics; -namespace Implab.ServiceHost.Unity -{ +namespace Implab.ServiceHost.Unity { using static Trace; - public class TypeResolver - { + public class TypeResolver { readonly Dictionary m_cache = new Dictionary(); Regex _nsRx = new Regex(@"^\w+(\.\w+)*$", RegexOptions.Compiled); @@ -49,7 +47,7 @@ namespace Implab.ServiceHost.Unity 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 @@ namespace Implab.ServiceHost.Unity 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 @@ namespace Implab.ServiceHost.Unity ); 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 @@ namespace Implab.ServiceHost.Unity 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 @@ namespace Implab.ServiceHost.Unity 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 diff --git a/Implab.ServiceHost/Unity/ValueElement.cs b/Implab.ServiceHost/Unity/ValueElement.cs --- a/Implab.ServiceHost/Unity/ValueElement.cs +++ b/Implab.ServiceHost/Unity/ValueElement.cs @@ -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 @@ namespace Implab.ServiceHost.Unity { [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 @@ namespace Implab.ServiceHost.Unity { } } - 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 diff --git a/Implab.ServiceHost/Unity/ValueParameterElement.cs b/Implab.ServiceHost/Unity/ValueParameterElement.cs --- a/Implab.ServiceHost/Unity/ValueParameterElement.cs +++ b/Implab.ServiceHost/Unity/ValueParameterElement.cs @@ -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 diff --git a/Implab.ServiceHost/Unity/readme.md b/Implab.ServiceHost/docs/XmlConfiguration.md rename from Implab.ServiceHost/Unity/readme.md rename to Implab.ServiceHost/docs/XmlConfiguration.md