# HG changeset patch # User cin # Date 2018-04-25 01:44:40 # Node ID ade80d94dfb5f48e774c6dabb16645cc617d928a # Parent ff581cff700324d0f69372982cb6d171ea0898ed Working on Unity container xml configuration diff --git a/Implab.Playground/Program.cs b/Implab.Playground/Program.cs --- a/Implab.Playground/Program.cs +++ b/Implab.Playground/Program.cs @@ -14,6 +14,8 @@ namespace Implab.Playground { } + public string Name { get; set; } + public int IntValue { get; set; } public string StringValue { get; set; } @@ -30,6 +32,10 @@ namespace Implab.Playground { } public T Instance { get; set; } + + public void SetInstance(T value) { + Instance = value; + } } public class Program { @@ -39,7 +45,7 @@ namespace Implab.Playground { var conf = SerializationHelpers.DeserializeFromFile("data/sample.xml"); - Console.WriteLine($"Registrations: {conf.Registrations.Count}"); + Console.WriteLine($"Registrations: {conf.Items.Count}"); } 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,10 +1,38 @@ + + + + FOO! + + + GOOD + + + 2 + + + + + + + + + + + + + + + Hello! + + + \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/AbstractInjectionElement.cs b/Implab.ServiceHost/Unity/AbstractInjectionElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/AbstractInjectionElement.cs @@ -0,0 +1,7 @@ +namespace Implab.ServiceHost.Unity +{ + public abstract class AbstractInjectionElement + { + + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/AbstractInjectorElement.cs b/Implab.ServiceHost/Unity/AbstractInjectorElement.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/AbstractInjectorElement.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Implab.ServiceHost.Unity -{ - public abstract class AbstractInjectorElement - { - - } -} \ 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 @@ -16,7 +16,7 @@ namespace Implab.ServiceHost.Unity } public void Visit(ConfigurationContext context) { - context.Visist(this); + context.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 @@ -8,7 +8,7 @@ namespace Implab.ServiceHost.Unity public string AssemblyName { get; set; } public void Visit(ConfigurationContext context) { - throw new System.NotImplementedException(); + context.Visit(this); } } } \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/ConfigurationContext.cs b/Implab.ServiceHost/Unity/ConfigurationContext.cs --- a/Implab.ServiceHost/Unity/ConfigurationContext.cs +++ b/Implab.ServiceHost/Unity/ConfigurationContext.cs @@ -8,93 +8,56 @@ namespace Implab.ServiceHost.Unity { using System.Reflection; using System.Text; using global::Unity; + using Implab.Xml; using static Trace; public class ConfigurationContext { - Regex _nsRx = new Regex(@"^\w+(\.\w+)*$", RegexOptions.Compiled); - readonly LinkedList m_namespases = new LinkedList(); - LinkedListNode m_insertAt; + readonly TypeResolver m_resolver; + + readonly UnityContainer m_container; - + public ConfigurationContext(UnityContainer container) { m_container = container ?? new UnityContainer(); - m_insertAt = new LinkedListNode(string.Empty); - m_namespases.AddFirst(m_insertAt); - } - - public void AddNamespace(string ns) { - Safe.ArgumentMatch(ns, nameof(ns), _nsRx); - if (m_insertAt != null) - m_namespases.AddAfter(m_insertAt, ns); - else - m_namespases.AddFirst(ns); + m_resolver = new TypeResolver(); } - public Type Resolve(TypeReference reference) { - Safe.ArgumentNotNull(reference, nameof(reference)); - var args = reference.IsGeneric && !reference.IsOpenGeneric ? reference.GenericParameters?.Select(Resolve).ToArray() : null; - var argc = reference.IsGeneric ? reference.GenericParameters.Length : 0; - - foreach (var ns in m_namespases) { - var typeName = FormatName(new [] { ns, reference.Namespace, reference.TypeName}, argc, args, reference.IsArray); - - var resolved = ProbeType(typeName); - if (resolved != null) { - Log("Probe succeed {0} in '{1}': {2} -> {3}", reference, ns, typeName, resolved.AssemblyQualifiedName); - return resolved; - } else { - Log("Probe failed {0} in '{1}': {2}", reference, ns, typeName); - } - } - - throw new Exception($"Failed to resolve: {reference}"); + public Type Resolve(string typeReference) { + return m_resolver.Resolve(TypeReference.Parse(typeReference)); } - Type ProbeType(string typeName) { - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - - foreach(var assembly in assemblies) { - var type = assembly.GetType(typeName); - if (type != null) - return type; - } - return null; - } - - string FormatName(string[] parts, int argc, Type[] args, bool isArray) { - 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(); - } - - public Type Resolve(string typeReference) { - return Resolve(TypeReference.Parse(typeReference)); - } - - public void Visist(AbstractRegistration descriptor) { + internal void Visit(AbstractRegistration descriptor) { } + internal void Visit(NamespaceElement namespaceElement) { + m_resolver.AddNamespace(namespaceElement.Name); + } + + internal void Visit(AssemblyElement assemblyElement) { + Assembly.Load(assemblyElement.AssemblyName); + } + + internal void Visit(IncludeElement includeElement) { + Include(includeElement.Href); + } + public void Include(string file) { + var includeContext = new ConfigurationContext(m_container); + includeContext.LoadConfig(file); + } + public void LoadConfig(string file) { + var config = SerializationHelpers.DeserializeFromFile(file); + Visit(config); + } + + public void Visit(ContainerElement containerElement) { + foreach (var item in containerElement.Items) + item.Visit(this); } } diff --git a/Implab.ServiceHost/Unity/ConstructorInjectionElement.cs b/Implab.ServiceHost/Unity/ConstructorInjectionElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/ConstructorInjectionElement.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace Implab.ServiceHost.Unity { + public class ConstructorInjectionElement : AbstractInjectionElement { + + [XmlElement("dependency", typeof(DependencyParameterElement))] + [XmlElement("value", typeof(ValueParameterElement))] + [XmlElement("serialized", typeof(SerializedParameterElement))] + [XmlElement("default", typeof(DefaultParameterElement))] + public InjectionParameterElement[] Parameters { get; set; } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/ConstructorInjectorElement.cs b/Implab.ServiceHost/Unity/ConstructorInjectorElement.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/ConstructorInjectorElement.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public class ConstructorInjectorElement : AbstractInjectorElement { - - } -} \ No newline at end of file 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 @@ -8,7 +8,7 @@ namespace Implab.ServiceHost.Unity { [XmlRoot("container", Namespace = Schema.ContainerConfigurationNamespace)] public class ContainerElement : IXmlSerializable { - public List Registrations {get; set; } = new List(); + public List Items {get; set; } = new List(); public XmlSchema GetSchema() { return null; @@ -17,7 +17,7 @@ namespace Implab.ServiceHost.Unity { public void ReadXml(XmlReader reader) { while(reader.Read() && reader.NodeType != XmlNodeType.EndElement) { var registration = ConfigurationSchema.Default.Deserialize(reader); - Registrations.Add(registration); + Items.Add(registration); } } diff --git a/Implab.ServiceHost/Unity/DefaultParameterElement.cs b/Implab.ServiceHost/Unity/DefaultParameterElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/DefaultParameterElement.cs @@ -0,0 +1,7 @@ +namespace Implab.ServiceHost.Unity +{ + public class DefaultParameterElement : InjectionParameterElement + { + + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/DependencyParameterElement.cs b/Implab.ServiceHost/Unity/DependencyParameterElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/DependencyParameterElement.cs @@ -0,0 +1,7 @@ +namespace Implab.ServiceHost.Unity +{ + public class DependencyParameterElement : InjectionParameterElement + { + + } +} \ 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 @@ -7,7 +7,7 @@ namespace Implab.ServiceHost.Unity { public string Href { get; set; } public void Visit(ConfigurationContext context) { - context.Include(Href); + context.Visit(this); } } } \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/InjectionParameterElement.cs b/Implab.ServiceHost/Unity/InjectionParameterElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/InjectionParameterElement.cs @@ -0,0 +1,9 @@ +using System.Xml.Serialization; + +namespace Implab.ServiceHost.Unity { + public class InjectionParameterElement { + + [XmlAttribute("type")] + public string TypeName { get; set; } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/MethodInjectionElement.cs b/Implab.ServiceHost/Unity/MethodInjectionElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/MethodInjectionElement.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace Implab.ServiceHost.Unity { + public class MethodInjectionElement : AbstractInjectionElement { + + [XmlAttribute("name")] + public string Name { get; set; } + + [XmlElement("dependency", typeof(DependencyParameterElement))] + [XmlElement("value", typeof(ValueParameterElement))] + [XmlElement("serialized", typeof(SerializedParameterElement))] + [XmlElement("default", typeof(DefaultParameterElement))] + public InjectionParameterElement[] Parameters { get; set; } + + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/MethodInjectorElement.cs b/Implab.ServiceHost/Unity/MethodInjectorElement.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/MethodInjectorElement.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Xml.Serialization; - -namespace Implab.ServiceHost.Unity { - public class MethodInjectorElement : AbstractInjectorElement { - - [XmlAttribute("name")] - public string Name { get; set; } - - [XmlElement("params")] - public MethodInjectorParameter[] Parameters { get; set; } - - } -} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/MethodInjectorParameter.cs b/Implab.ServiceHost/Unity/MethodInjectorParameter.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/MethodInjectorParameter.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Implab.ServiceHost.Unity -{ - public class MethodInjectorParameter - { - - } -} \ No newline at end of file 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 @@ -9,7 +9,7 @@ namespace Implab.ServiceHost.Unity public string Name { get; set; } public void Visit(ConfigurationContext context) { - throw new System.NotImplementedException(); + context.Visit(this); } } } \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/PropertyInjectionElement.cs b/Implab.ServiceHost/Unity/PropertyInjectionElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/PropertyInjectionElement.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace Implab.ServiceHost.Unity { + public class PropertyInjectionElement : AbstractInjectionElement { + + [XmlElement("dependency", typeof(DependencyParameterElement))] + [XmlElement("value", typeof(ValueParameterElement))] + [XmlElement("serialized", typeof(SerializedParameterElement))] + [XmlElement("default", typeof(DefaultParameterElement))] + public InjectionParameterElement Value { get; set; } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/PropertyInjectorElement.cs b/Implab.ServiceHost/Unity/PropertyInjectorElement.cs deleted file mode 100644 --- a/Implab.ServiceHost/Unity/PropertyInjectorElement.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Implab.ServiceHost.Unity { - public class PropertyInjectorElement : AbstractInjectorElement { - - - } -} \ No newline at end of file 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 @@ -27,10 +27,10 @@ namespace Implab.ServiceHost.Unity { [XmlElement("hierarchy", typeof(HierarchicalLifetimeElement))] public LifetimeElement Lifetime {get; set;} - [XmlElement("constructor", typeof(ConstructorInjectorElement))] - [XmlElement("property", typeof(PropertyInjectorElement))] - [XmlElement("method", typeof(MethodInjectorElement))] - public AbstractInjectorElement[] Injectors { get; set; } + [XmlElement("constructor", typeof(ConstructorInjectionElement))] + [XmlElement("property", typeof(PropertyInjectionElement))] + [XmlElement("method", typeof(MethodInjectionElement))] + public AbstractInjectionElement[] Injectors { get; set; } } } \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/SerializedParameterElement.cs b/Implab.ServiceHost/Unity/SerializedParameterElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/SerializedParameterElement.cs @@ -0,0 +1,7 @@ +namespace Implab.ServiceHost.Unity +{ + public class SerializedParameterElement : InjectionParameterElement + { + + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/TypeResolver.cs b/Implab.ServiceHost/Unity/TypeResolver.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/TypeResolver.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Implab.Diagnostics; + +namespace Implab.ServiceHost.Unity +{ + using static Trace; + public class TypeResolver + { + readonly Dictionary m_cache = new Dictionary(); + + Regex _nsRx = new Regex(@"^\w+(\.\w+)*$", RegexOptions.Compiled); + readonly LinkedList m_namespases = new LinkedList(); + + LinkedListNode m_insertAt; + + readonly TypeResolver m_parent; + + public TypeResolver() : this(null) { + + } + + public TypeResolver(TypeResolver parent) { + m_insertAt = new LinkedListNode(string.Empty); + m_namespases.AddFirst(m_insertAt); + } + + public void AddNamespace(string ns) { + Safe.ArgumentMatch(ns, nameof(ns), _nsRx); + if (m_insertAt != null) + m_namespases.AddAfter(m_insertAt, ns); + else + m_namespases.AddFirst(ns); + } + + public void AddMapping(string typeName, Type type) { + Safe.ArgumentNotEmpty(typeName, nameof(typeName)); + Safe.ArgumentNotNull(type, nameof(type)); + + m_cache[typeName] = type; + } + + public Type Resolve(TypeReference reference) { + var args = reference.IsGeneric && !reference.IsOpenGeneric ? reference.GenericParameters?.Select(Resolve).ToArray() : null; + var argc = reference.IsGeneric ? reference.GenericParameters.Length : 0; + + Type resolved; + if(!m_cache.TryGetValue(reference.ToString(), out resolved)) { + resolved = ResolveInternal(reference, args, argc); + if (resolved == null) + throw new Exception($"Failed to resolve {reference}"); + m_cache[reference.ToString()] = resolved; + } + + return resolved; + } + + Type ResolveInternal(TypeReference reference, Type[] args, int argc) { + var resolved = ProbeInNamespaces( + String.Join(".", new [] { reference.Namespace, reference.TypeName }.Where(x => !string.IsNullOrEmpty(x)) ), + args, + argc, + reference.IsArray, + reference.ToString() + ); + + if (resolved == null && m_parent != null) + resolved = m_parent.Resolve(reference); + + 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 resolved = Probe(typeName); + if (resolved != null) { + Log("Probe succeed {0} in '{1}': {2} -> {3}", referenceName, ns, typeName, resolved.AssemblyQualifiedName); + return resolved; + } else { + Log("Probe failed {0} in '{1}': {2}", referenceName, ns, typeName); + } + } + + return null; + } + + Type Probe(string typeName) { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + foreach(var assembly in assemblies) { + var type = assembly.GetType(typeName); + if (type != null) + return type; + } + return null; + } + + string FormatName(string[] parts, int argc, Type[] args, bool isArray) { + 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(); + } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/ValueParameterElement.cs b/Implab.ServiceHost/Unity/ValueParameterElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/ValueParameterElement.cs @@ -0,0 +1,7 @@ +namespace Implab.ServiceHost.Unity +{ + public class ValueParameterElement : InjectionParameterElement + { + + } +} \ No newline at end of file