# HG changeset patch # User cin # Date 2018-05-03 06:59:44 # Node ID 6691aff01de16681f54c601bad14cdeacefda99b # Parent 963b17c275be5efd802e9cefcd92664594e143d5 Implab: added XmlDefaultSeializer (SerializersPool is now obsolete) Implab.ServiceHost: rewritten TypeReference (added support for nested types), stable API diff --git a/Implab.Playground/Program.cs b/Implab.Playground/Program.cs --- a/Implab.Playground/Program.cs +++ b/Implab.Playground/Program.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Implab.Components; using Implab.Diagnostics; using Implab.ServiceHost.Unity; using Implab.Xml; @@ -29,11 +30,39 @@ namespace Implab.Playground { } + public class FooFactory : IFactory, IFactory { + + public bool UseSsl { get; set; } + + public string Connection { get; set; } + + public Foo Create() { + return new Foo() { + Name = "AutoFac" + }; + } + + Foo.Bar IFactory.Create() { + return new Foo.Bar(); + } + } + public interface IContainer { T Instance { get; set; } } public class Container : IContainer { + public class Bar { + + } + + public class Bar { + public class Baz { + + } + + } + public Container() { } @@ -65,29 +94,22 @@ namespace Implab.Playground { 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 resolver = new TypeResolver(); + resolver.AddNamespace("System"); + resolver.AddNamespace("System.Collections.Generic"); + resolver.AddNamespace("Implab.Playground"); + resolver.AddMapping("string", typeof(string)); + resolver.AddMapping("listOf`1", typeof(List<>)); - var container = ctx.Container; + var spec = TypeReference.Parse("Container{listOf{string}}+Bar{List{string}}"); - stopwatch.Restart(); - var instace1 = container.Resolve>(); - Console.WriteLine($"Resolved1: {stopwatch.ElapsedMilliseconds}"); + var t = resolver.Resolve(spec, true); - stopwatch.Restart(); - var instace2 = container.Resolve>(); - Console.WriteLine($"Resolved2: {stopwatch.ElapsedMilliseconds}"); - - DisplayContainerRegistrations(container); + Console.WriteLine("{0}", t); + Console.WriteLine("Spec: {0}", spec); + Console.WriteLine("IsGenericType: {0}", t.IsGenericType); + Console.WriteLine("IsGenericTypeDefinition: {0}", t.IsGenericTypeDefinition); + Console.WriteLine("ContainsGenericParameters: {0}", t.ContainsGenericParameters); } static void DisplayContainerRegistrations(IUnityContainer theContainer) { 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 @@ -2,6 +2,7 @@ + @@ -36,7 +37,7 @@ - + @@ -61,8 +62,13 @@ !]]> - - name @#$%^&]]> + + + name @#$%^&]]> + + + false + \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/ArrayParameterElement.cs b/Implab.ServiceHost/Unity/ArrayParameterElement.cs --- a/Implab.ServiceHost/Unity/ArrayParameterElement.cs +++ b/Implab.ServiceHost/Unity/ArrayParameterElement.cs @@ -13,7 +13,7 @@ namespace Implab.ServiceHost.Unity [XmlElement("default", typeof(DefaultParameterElement))] public InjectionParameterElement[] Items { get; set; } - public override void Visit(InjectionValueBuilder builder) { + public override void Visit(InjectionParameterBuilder builder) { builder.Visit(this); } } diff --git a/Implab.ServiceHost/Unity/ArrayTypeReference.cs b/Implab.ServiceHost/Unity/ArrayTypeReference.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/ArrayTypeReference.cs @@ -0,0 +1,57 @@ +using System; +using System.Text; + +namespace Implab.ServiceHost.Unity { + public class ArrayTypeReference : TypeReference { + public int Rank { get; private set; } + + public TypeReference ItemsType { get; private set; } + + public override string Name { + get { + return ItemsType.Name; + } + } + + public override string ClrName { + get { + return new StringBuilder() + .Append(ItemsType.ClrName) + .Append("[") + .Append(',', Rank - 1) + .Append("]") + .ToString(); + } + } + + public override string Namespace { + get { + return ItemsType.Namespace; + } + } + + public override int GenericParametersCount { + get { + return 0; + } + } + + internal ArrayTypeReference(TypeReference itemsType, int rank) { + ItemsType = itemsType; + Rank = rank; + } + + internal override void Visit(TypeResolutionContext visitor) { + visitor.Visit(this); + } + + override public string ToString() { + return new StringBuilder() + .Append(ItemsType.ToString()) + .Append('[') + .Append(',', Rank - 1) + .Append(']') + .ToString(); + } + } +} \ 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 override void Visit(ContainerBuilder builder) { - builder.Visit(this); + builder.AddAssembly(AssemblyName); } } } \ No newline at end of file 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 @@ -1,17 +1,8 @@ using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using Implab.Diagnostics; +using System.Reflection; +using Unity; namespace Implab.ServiceHost.Unity { - using System.Linq; - using System.Reflection; - using System.Text; - using global::Unity; - using global::Unity.Registration; - using Implab.Xml; - using static Trace; - public class ContainerBuilder { readonly TypeResolver m_resolver; @@ -36,7 +27,7 @@ namespace Implab.ServiceHost.Unity { } public Type ResolveType(string typeReference) { - return m_resolver.Resolve(typeReference); + return m_resolver.Resolve(typeReference, true); } @@ -91,16 +82,85 @@ namespace Implab.ServiceHost.Unity { ); } - internal void Visit(NamespaceElement namespaceElement) { - m_resolver.AddNamespace(namespaceElement.Name); + public void Visit(ITypeRegistration registration) { + Safe.ArgumentNotNull(registration, nameof(registration)); + + var registrationType = registration.GetRegistrationType(this); + var implementationType = registration.GetImplementationType(this) ?? registrationType; + + if (registrationType == null) + throw new Exception($"A type must be specified for the registration {registration.Name}"); + + var builder = new TypeRegistrationBuilder( + m_resolver, + registrationType, + implementationType + ); + + builder.Lifetime = registration.GetLifetime(this); + + if (registration.MemberInjections != null) { + foreach(var member in registration.MemberInjections) + member.Visit(builder); + } + + m_container.RegisterType( + builder.RegistrationType, + builder.ImplementationType, + registration.Name, + builder.Lifetime, + builder.Injections + ); } - internal void Visit(AssemblyElement assemblyElement) { - Assembly.Load(assemblyElement.AssemblyName); + public void Visit(IInstanceRegistration registration) { + Safe.ArgumentNotNull(registration, nameof(registration)); + + var registrationType = registration.GetRegistrationType(this); + + var builder = new InstanceRegistrationBuilder ( + m_resolver, + registrationType + ); + + builder.Lifetime = registration.GetLifetime(this); + + if (registration.MemberInjections != null) { + foreach(var member in registration.MemberInjections) + member.Visit(builder.ValueBuilder); + } + + if (builder.RegistrationType == null && builder.ValueBuilder.ValueType == null) + throw new Exception($"A type must be specified for the registration {registration.Name}"); + + m_container.RegisterInstance( + builder.RegistrationType ?? builder.ValueBuilder.ValueType, + registration.Name, + builder.ValueBuilder.Injection, + builder.Lifetime + ); } - internal void Visit(IncludeElement includeElement) { - Include(includeElement.Href); + public void Visit(IFactoryRegistration registration) { + Safe.ArgumentNotNull(registration, nameof(registration)); + + var registrationType = registration.GetRegistrationType(this); + + var builder = new FactoryRegistrationBuilder(registrationType); + + if (registration.MemberInjections != null) { + foreach(var member in registration.MemberInjections) + member?.Visit(builder); + } + + } + + public void AddNamespace(string ns) { + m_resolver.AddNamespace(ns); + } + + public void AddAssembly(string assembly) { + } public void Include(string file) { @@ -110,9 +170,8 @@ namespace Implab.ServiceHost.Unity { public void LoadConfig(string file) { var config = m_schema.LoadFile(file); - config.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 @@ -55,6 +55,7 @@ namespace Implab.ServiceHost.Unity { var schema = new ContainerConfigurationSchema(); schema.RegisterContainerElement("register"); + schema.RegisterContainerElement("factory"); schema.RegisterContainerElement("serialized"); schema.RegisterContainerElement("value"); schema.RegisterContainerElement("include"); 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 @@ -5,7 +5,7 @@ namespace Implab.ServiceHost.Unity get { return null; } } - public override void Visit(InjectionValueBuilder builder) { + public override void Visit(InjectionParameterBuilder builder) { var type = builder.ResolveInjectedValueType(TypeName); builder.SetValue(type, Safe.CreateDefaultValue(type)); } 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 @@ -9,7 +9,7 @@ namespace Implab.ServiceHost.Unity { [XmlAttribute("optional")] public bool Optional { get; set; } - public override void Visit(InjectionValueBuilder builder) { + public override void Visit(InjectionParameterBuilder builder) { var type = builder.ResolveInjectedValueType(TypeName); builder.SetDependencyReference(type, DependencyName, Optional); } diff --git a/Implab.ServiceHost/Unity/FactoryActivator.cs b/Implab.ServiceHost/Unity/FactoryActivator.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/FactoryActivator.cs @@ -0,0 +1,29 @@ +using System; + +namespace Implab.ServiceHost.Unity { + public class FactoryActivator : FactoryAbstractRegistratrion { + + public Type FactoryType { get; set; } + + public string FactoryName { get; set; } + + public new Type RegistrationType { get; set; } + + public override void Visit(FactoryRegistrationBuilder builder) { + base.Visit(builder); + + builder.GetType() + .GetMethod( + nameof(FactoryRegistrationBuilder.SetFactoryDependency) + , new[] { typeof(string) } + ) + .MakeGenericMethod(FactoryType, RegistrationType) + .Invoke(builder, new[] { FactoryName }); + } + + public override Type GetRegistrationType(Func resolver) { + return RegistrationType; + } + + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/FactoryElement.cs b/Implab.ServiceHost/Unity/FactoryElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/FactoryElement.cs @@ -0,0 +1,72 @@ +using System; +using System.Xml.Serialization; +using Implab.Components; + +namespace Implab.ServiceHost.Unity { + /// + /// Расширяет стандартную регистрацию типа до фабрики, вместе с регистрацией + /// самой фабрики создаются регистрации сервисов, которые она предоставляет. + /// + public class FactoryElement : RegisterElement { + + /// + /// Записи о сервисах, которые создаются данной фабрикой. + /// + /// + /// Сервисы, которые указаны в регистарциях они должны соответсвовать тому, + /// что фабрика возвращает, но это остается на откуп контейнера + /// + [XmlElement("provides")] + public ProvidesElement[] Provides { get; set; } + + /// + /// Переопределяет стандарное поведение регистрации типа в контейнере, + /// дополняя его регистрацией фабричных методов для получения типов. + /// + /// Объект для конфигурирования контейнера. + public override void Visit(ContainerBuilder builder) { + var factoryType = GetRegistrationType(builder.ResolveType); + + base.Visit(builder); + + if (Provides != null && Provides.Length > 0) { + // если регистрации явно заданы, используеися информация из них + foreach(var item in Provides) { + var activator = new FactoryActivator { + Name = item.RegistrationName, + RegistrationType = builder.ResolveType(item.RegistrationType), + FactoryName = Name, + FactoryType = factoryType + }; + activator.Visit(builder); + } + } else { + // если регистрация явно не задана, в качестве сервиса для регистрации + // используется тип создаваемый фабрикой, который будет добавлен в контейнер + // с темже именем, что и сама фабрика (разные типы могут иметь одно имя для регистрации) + var providedType = ( + factoryType.IsGenericType && factoryType.GetGenericTypeDefinition() == typeof(IFactory<>) ? + factoryType : + factoryType.GetInterface(typeof(IFactory<>).FullName) + )? + .GetGenericArguments()[0]; + + // не удалось определеить тип + if (providedType == null) + throw new ArgumentException("Failed to determine a type provided by the factory"); + + if (providedType.IsGenericParameter) + throw new ArgumentException("Can't register a generic type paramter as a service"); + + var activator = new FactoryActivator { + Name = Name, + RegistrationType = providedType, + FactoryName = Name, + FactoryType = factoryType + }; + + activator.Visit(builder); + } + } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/FactoryRegistrationBuilder.cs b/Implab.ServiceHost/Unity/FactoryRegistrationBuilder.cs --- a/Implab.ServiceHost/Unity/FactoryRegistrationBuilder.cs +++ b/Implab.ServiceHost/Unity/FactoryRegistrationBuilder.cs @@ -13,12 +13,25 @@ namespace Implab.ServiceHost.Unity 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)); @@ -28,6 +41,11 @@ namespace Implab.ServiceHost.Unity }); } + /// + /// Указывает зависимость, реализующую интерфейс , + /// которая будет использоваться в качестве фабрики для создания объектов + /// + /// public void SetFactoryDependency(string dependencyName) where TFac : IFactory { Factory = new InjectionFactory(c => c.Resolve(dependencyName).Create()); diff --git a/Implab.ServiceHost/Unity/IFactoryMemberInjection.cs b/Implab.ServiceHost/Unity/IFactoryMemberInjection.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/IFactoryMemberInjection.cs @@ -0,0 +1,5 @@ +namespace Implab.ServiceHost.Unity { + public interface IFactoryMemberInjection { + void Visit(FactoryRegistrationBuilder builder); + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/IFactoryRegistration.cs b/Implab.ServiceHost/Unity/IFactoryRegistration.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/IFactoryRegistration.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; + +namespace Implab.ServiceHost.Unity { + public interface IFactoryRegistration : IRegistration { + IEnumerable MemberInjections { get; } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/IInjectionParameter.cs b/Implab.ServiceHost/Unity/IInjectionParameter.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/IInjectionParameter.cs @@ -0,0 +1,5 @@ +namespace Implab.ServiceHost.Unity { + public interface IInjectionParameter { + void Visit(InjectionParameterBuilder builder); + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/IInstanceRegistration.cs b/Implab.ServiceHost/Unity/IInstanceRegistration.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/IInstanceRegistration.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Implab.ServiceHost.Unity { + public interface IInstanceRegistration : IRegistration { + + IEnumerable MemberInjections { get; } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/IRegistration.cs b/Implab.ServiceHost/Unity/IRegistration.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/IRegistration.cs @@ -0,0 +1,12 @@ +using System; +using Unity.Lifetime; + +namespace Implab.ServiceHost.Unity { + public interface IRegistration { + string Name { get; } + + Type GetRegistrationType(ContainerBuilder builder); + + LifetimeManager GetLifetime(ContainerBuilder builder); + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/ITypeMemberInjection.cs b/Implab.ServiceHost/Unity/ITypeMemberInjection.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/ITypeMemberInjection.cs @@ -0,0 +1,6 @@ +namespace Implab.ServiceHost.Unity +{ + public interface ITypeMemberInjection { + void Visit(TypeRegistrationBuilder builder); + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/ITypeRegistration.cs b/Implab.ServiceHost/Unity/ITypeRegistration.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/ITypeRegistration.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace Implab.ServiceHost.Unity { + public interface ITypeRegistration : IRegistration { + Type GetImplementationType(ContainerBuilder builder); + + IEnumerable MemberInjections { 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 @@ -6,8 +6,8 @@ namespace Implab.ServiceHost.Unity { [XmlAttribute("href")] public string Href { get; set; } - public override void Visit(ContainerBuilder context) { - context.Visit(this); + public override void Visit(ContainerBuilder builder) { + builder.Include(Href); } } } \ No newline at end of file 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 @@ -7,6 +7,6 @@ namespace Implab.ServiceHost.Unity { [XmlAttribute("type")] public string TypeName { get; set; } - public abstract void Visit(InjectionValueBuilder builder); + public abstract void Visit(InjectionParameterBuilder builder); } } \ No newline at end of file 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 @@ -7,7 +7,7 @@ using Unity.Injection; namespace Implab.ServiceHost.Unity { - public class InjectionValueBuilder { + public class InjectionParameterBuilder { readonly TypeResolver m_resolver; @@ -26,7 +26,7 @@ namespace Implab.ServiceHost.Unity { } } - internal InjectionValueBuilder(TypeResolver resolver, Type defaultType) { + internal InjectionParameterBuilder(TypeResolver resolver, Type defaultType) { m_resolver = resolver; DefaultType = defaultType; } @@ -37,11 +37,11 @@ namespace Implab.ServiceHost.Unity { throw new Exception("The type must be specified"); return DefaultType; } - return m_resolver.Resolve(typeSpec); + return m_resolver.Resolve(typeSpec, true); } public Type ResolveType(string typeSpec) { - return m_resolver.Resolve(typeSpec); + return m_resolver.Resolve(typeSpec, true); } public void SetValue(Type type, object value) { @@ -75,13 +75,16 @@ namespace Implab.ServiceHost.Unity { InjectionParameterValue[] injections = (arrayParameter.Items ?? new InjectionParameterElement[0]) .Select(x => { - var builder = new InjectionValueBuilder(m_resolver, itemsType); + var builder = new InjectionParameterBuilder(m_resolver, itemsType); x.Visit(builder); return builder.Injection; }) .ToArray(); - var array = itemsType.IsGenericParameter ? (object)new GenericResolvedArrayParameter(itemsType.Name, injections) : new ResolvedArrayParameter(itemsType, injections); + var array = itemsType.IsGenericParameter ? + (object)new GenericResolvedArrayParameter(itemsType.Name, injections) : + new ResolvedArrayParameter(itemsType, injections); + ValueType = arrayType; Value = array; } diff --git a/Implab.ServiceHost/Unity/InstanceRegistrationBuilder.cs b/Implab.ServiceHost/Unity/InstanceRegistrationBuilder.cs --- a/Implab.ServiceHost/Unity/InstanceRegistrationBuilder.cs +++ b/Implab.ServiceHost/Unity/InstanceRegistrationBuilder.cs @@ -4,10 +4,10 @@ namespace Implab.ServiceHost.Unity { public class InstanceRegistrationBuilder : RegistrationBuilder { - public InjectionValueBuilder ValueBuilder { get; private set; } + public InjectionParameterBuilder ValueBuilder { get; private set; } internal InstanceRegistrationBuilder(TypeResolver typeResolver, Type registrationType) : base(registrationType) { - ValueBuilder = new InjectionValueBuilder(typeResolver, registrationType); + ValueBuilder = new InjectionParameterBuilder(typeResolver, registrationType); } } } \ 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 @@ -8,8 +8,8 @@ namespace Implab.ServiceHost.Unity [XmlAttribute("name")] public string Name { get; set; } - public override void Visit(ContainerBuilder context) { - context.Visit(this); + public override void Visit(ContainerBuilder builder) { + builder.AddNamespace(Name); } } } \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/NestedTypeReference.cs b/Implab.ServiceHost/Unity/NestedTypeReference.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/NestedTypeReference.cs @@ -0,0 +1,48 @@ +using System; +using System.Text; + +namespace Implab.ServiceHost.Unity { + public class NestedTypeReference : TypeReference { + + readonly string m_name; + + readonly int m_genericParametersCount; + + public TypeReference DeclaringType { get; private set; } + + public override string Name { + get { + return m_name; + } + } + + public override string Namespace { + get { + return DeclaringType.Namespace; + } + } + + public override int GenericParametersCount { + get { + return m_genericParametersCount; + } + } + + internal NestedTypeReference(TypeReference declaringType, string name, int parametersCount) { + DeclaringType = declaringType; + m_name = name; + m_genericParametersCount = parametersCount; + } + + internal override void Visit(TypeResolutionContext visitor) { + visitor.Visit(this); + } + + internal override void WriteTypeName(StringBuilder builder) { + builder + .Append(DeclaringType) + .Append('+') + .Append(Name); + } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/ProvidesElement.cs b/Implab.ServiceHost/Unity/ProvidesElement.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/ProvidesElement.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace Implab.ServiceHost.Unity { + public class ProvidesElement { + [XmlAttribute("type")] + public string RegistrationType { get; set; } + + [XmlAttribute("name")] + public string RegistrationName { get; set; } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/RootTypeReference.cs b/Implab.ServiceHost/Unity/RootTypeReference.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/RootTypeReference.cs @@ -0,0 +1,36 @@ +using System; + +namespace Implab.ServiceHost.Unity +{ + public class RootTypeReference : TypeReference { + readonly string m_name; + + readonly string m_namespace; + + readonly int m_genericParametersCount; + + public override string Name { + get { return m_name; } + } + + public override string Namespace { + get { return m_namespace; } + } + + public override int GenericParametersCount { + get { return m_genericParametersCount; } + } + + internal RootTypeReference(string ns, string name, int genericParameters) { + m_name = name; + m_genericParametersCount = genericParameters; + m_namespace = ns; + } + + internal override void Visit(TypeResolutionContext visitor) { + visitor.Visit(this); + } + + + } +} \ 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 @@ -21,7 +21,7 @@ namespace Implab.ServiceHost.Unity throw new Exception("No content found, expected XML document"); } - public override void Visit(InjectionValueBuilder builder) { + public override void Visit(InjectionParameterBuilder builder) { var type = builder.ResolveInjectedValueType(TypeName); var serializer = new XmlSerializer(type); diff --git a/Implab.ServiceHost/Unity/SpecializedTypeReference.cs b/Implab.ServiceHost/Unity/SpecializedTypeReference.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/SpecializedTypeReference.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq; +using System.Text; + +namespace Implab.ServiceHost.Unity { + public class SpecializedTypeReference : TypeReference { + public override string Name { + get { + return GenericType.Name; + } + } + + public override string Namespace { + get { + return GenericType.Namespace; + } + } + + public override int GenericParametersCount { + get { + return GenericParameters.Length; + } + } + + public TypeReference GenericType { get; private set; } + + public TypeReference[] GenericParameters { get; private set; } + + internal SpecializedTypeReference(TypeReference genericType, TypeReference[] genericParameters) { + GenericType = genericType; + GenericParameters = genericParameters; + } + + internal override void Visit(TypeResolutionContext visitor) { + visitor.Visit(this); + } + + internal override void WriteTypeName(StringBuilder builder) { + GenericType.WriteTypeName(builder); + } + + internal override void WriteTypeParams(StringBuilder builder) { + builder.Append('{'); + for (var i = 0; i < GenericParameters.Length; i++) { + if (i > 0) + builder.Append(','); + builder.Append(GenericParameters[i]); + } + builder.Append('}'); + } + } +} \ No newline at end of file diff --git a/Implab.ServiceHost/Unity/TypeReference.cs b/Implab.ServiceHost/Unity/TypeReference.cs --- a/Implab.ServiceHost/Unity/TypeReference.cs +++ b/Implab.ServiceHost/Unity/TypeReference.cs @@ -3,56 +3,180 @@ using System.Linq; using System.Text; namespace Implab.ServiceHost.Unity { - public class TypeReference { - public string TypeName { get; set; } - - public string Namespace { get; set; } - - public TypeReference[] GenericParameters { get; set; } - - public bool IsArray { get; set; } + /// + /// Ссылка на тип, является абстрактной записью имени CLR типа. + /// + /// + /// Ссылка на тип содержит сокращенную информацию о типе и для ее интерпретации + /// требуется некоторый контекст, который позволит превратить ее в полноценный + /// . Ссылки на тип позволяют записать: + /// + /// общие типы, их специализации + /// вложенные типы + /// массивы + /// + /// + /// Для получения из ссылки на тип конкретного CLR типа используется . + /// + /// + /// Ссылку на тип можно создать либо програмно при помощи методов , + /// , , , + /// либо разобрав строку со спецификацией при помощи метода . + /// + /// + /// Спецификация ссыдки на тип имеет следующий вид Name.Space.MyType+Nested{String}[][], где: + /// + /// + /// . + /// Разделяет элементы пространства имен + /// + /// + /// + + /// Разделяет вложенные типы + /// + /// + /// [], [,,,] + /// Указывает на то, что тип является массивом, также указывается его размерность + /// + /// + /// {}, {,,}, {Int32,String} + /// Указывает на то, что тип является общим, также + /// указывается количество параметров, либо конкретные типы для + /// специализации + /// + /// + /// + /// + public abstract class TypeReference { - public bool IsOpenGeneric { + /// + /// Имя типа без дополнительных элементов, указывающих на то, что он общий или массив. + /// + /// + /// Для массивов это имя его элементов. + /// + public abstract string Name { get; } + + /// + /// Пространство имен в котором нахожится тип. + /// + /// + /// Для вложенных типов это пространтство имен типа самого верхнего уровня, + /// для массивов - пространство имен его элементов. + /// + public abstract string Namespace { get; } + + /// + /// Количество параметров общего типа. + /// + /// + /// + /// Вложенные типы неявно получают параметры от типов в которых они объявлены, + /// данное свойство это не учитывает, возвращается только количество собственных + /// параметров. + /// + /// + /// Данное свойство используется для получения CRL имени типа. + /// + /// + public abstract int GenericParametersCount { get; } + + public virtual string ClrName { get { - return GenericParameters!=null && GenericParameters.Contains(null); - } - } - - public bool IsGeneric { - get { - return GenericParameters != null && GenericParameters.Length > 0; + return GenericParametersCount != 0 ? $"{Name}`{GenericParametersCount}" : Name; } } + /// + /// Создает ссылку на специализацию текущего типа. + /// + /// Ссылки на типы, которые будут использоваться для специализации текущего типа. + /// Специализация данного типа. + public virtual SpecializedTypeReference MakeGenericType(TypeReference[] genericParameters) { + if (GenericParametersCount == 0) + throw new InvalidOperationException("Can't specialize a non-geneic type"); + + if (genericParameters == null || GenericParametersCount != genericParameters.Length) + throw new InvalidOperationException("Generic parameters count mismatch"); + + return new SpecializedTypeReference(this, genericParameters); + } + + /// + /// Создает ссылку на тип массива указанной размерности, элементами которого являются экземпаляры даннго типа. + /// + /// Размерность, если размерность 1 создается вектор (). + /// Ссылка на тип массива + public ArrayTypeReference MakeArrayType(int rank) { + Safe.ArgumentInRange(rank > 0, nameof(rank)); + + return new ArrayTypeReference(this, rank); + } + + /// + /// Создает ссылку на вложенный тип. + /// + /// Имя типа + /// Количество параметров, если это общий тип, иначе 0. + /// Ссылка на вложенный тип. + public TypeReference Create(string name, int genericParameters) { + Safe.ArgumentNotEmpty(name, nameof(name)); + Safe.ArgumentInRange(genericParameters >= 0, nameof(genericParameters)); + + return new NestedTypeReference(this, name, genericParameters); + } + + /// + /// Возвращает строковое представление ссылки на тип. + /// + /// public override string ToString() { var builder = new StringBuilder(); - - if (!string.IsNullOrEmpty(Namespace)) { - builder.Append(Namespace); - builder.Append('.'); - } - - if (!string.IsNullOrEmpty(TypeName)) { - builder.Append(TypeName); - } else { - builder.Append("__unnamed__"); - } - - if (GenericParameters != null && GenericParameters.Length > 0) { - builder.Append('{'); - for(var i = 0; i < GenericParameters.Length; i++) { - if (i > 0) - builder.Append(','); - builder.Append(GenericParameters[i]); - } - builder.Append('}'); - } - + WriteTypeName(builder); + WriteTypeParams(builder); return builder.ToString(); } - public static TypeReference Parse(string text) { - var parser = new TypeReferenceParser(text); + + internal virtual void WriteTypeName(StringBuilder builder) { + if (!string.IsNullOrEmpty(Namespace)) + builder + .Append(Namespace) + .Append('.'); + builder.Append(Name); + } + + internal virtual void WriteTypeParams(StringBuilder builder) { + if (GenericParametersCount > 0) + builder + .Append('{') + .Append(',', GenericParametersCount-1) + .Append('}'); + } + + internal abstract void Visit(TypeResolutionContext visitor); + + /// + /// Создает ссылку на тип. + /// + /// Пространство имен, либо его фрагмент. + /// Имя типа без указания на количество параметров, либо на то, что это массив. + /// Количество параметров типа, если это общий тип, иначе 0. + /// Ссылка на тип. + public static TypeReference Create(string ns, string name, int genericParameters) { + Safe.ArgumentNotEmpty(name, nameof(name)); + Safe.ArgumentInRange(genericParameters >= 0, nameof(genericParameters)); + return new RootTypeReference(ns, name, genericParameters); + } + + /// + /// Разирает строковую запись ссылки на тип. + /// + /// Строковая запись ссылки на тип, например Dictionary{String,String} + /// Ссылка на тип. + public static TypeReference Parse(string typeSpec) { + var parser = new TypeReferenceParser(typeSpec); return parser.Parse(); } + } } \ 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 @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.RegularExpressions; namespace Implab.ServiceHost.Unity { @@ -21,10 +22,12 @@ namespace Implab.ServiceHost.Unity { CloseArray, + Plus, + Eof } - readonly Regex _tokens = new Regex(@"([\w\+]+)|\s*([\.{},\[\]])\s*"); + readonly Regex _tokens = new Regex(@"\G(?:([\w]+)|\s*([\+\.{},\[\]])\s*)", RegexOptions.Compiled); TokenType m_token; @@ -83,6 +86,9 @@ namespace Implab.ServiceHost.Unity { case "]": m_token = TokenType.CloseArray; break; + case "+": + m_token = TokenType.Plus; + break; } } return true; @@ -97,7 +103,7 @@ namespace Implab.ServiceHost.Unity { return result; } - string[] ReadTypeName() { + string[] ReadQTypeName() { var parts = new List(); string current = null; @@ -129,31 +135,95 @@ namespace Implab.ServiceHost.Unity { return parts.ToArray(); } + string ReadNQTypeName() { + ReadToken(); + if (Token != TokenType.Word) + ThrowUnexpectedToken(); + return TokenValue; + } + TypeReference ReadTypeReference() { - var parts = ReadTypeName(); + var parts = ReadQTypeName(); if (parts == null) return null; - var typeReference = new TypeReference { - Namespace = string.Join(".", parts, 0, parts.Length - 1), - TypeName = parts[parts.Length - 1] - }; + var genericParameters = ReadGenericParams(); + + var typeReference = TypeReference.Create( + string.Join(".", parts, 0, parts.Length - 1), + parts[parts.Length - 1], + genericParameters.Length + ); + + if (genericParameters.Length > 0 && genericParameters.All(x => x != null)) + typeReference = typeReference.MakeGenericType(genericParameters); + + typeReference = ReadArraySpec(typeReference); + + if(Token == TokenType.Plus) + return ReadNestedType(typeReference); + + return typeReference; + } + + TypeReference ReadNestedType(TypeReference declaringType) { + var name = ReadNQTypeName(); + if(string.IsNullOrEmpty(name)) + throw new FormatException("Nested type name can't be empty"); + ReadToken(); + + var genericParameters = ReadGenericParams(); + + var typeReference = declaringType.Create( + name, + genericParameters.Length + ); - switch (Token) { - case TokenType.OpenList: - typeReference.GenericParameters = ReadTypeReferenceList(); - if (Token != TokenType.CloseList) - ThrowUnexpectedToken(); - ReadToken(); - break; + if (genericParameters.Length > 0 && genericParameters.All(x => x != null)) + typeReference = typeReference.MakeGenericType(genericParameters); + + typeReference = ReadArraySpec(typeReference); + + if(Token == TokenType.Plus) + return ReadNestedType(typeReference); + + return typeReference; + } + + TypeReference[] ReadGenericParams() { + if (Token == TokenType.OpenList) { + var genericParameters = ReadTypeReferenceList(); + if (Token != TokenType.CloseList) + ThrowUnexpectedToken(); + ReadToken(); + + return genericParameters; + } + + return Array.Empty(); + } + + TypeReference ReadArraySpec(TypeReference typeReference) { + while (Token == TokenType.OpenArray) { + var rank = CountRank(); + if (Token != TokenType.CloseArray) + ThrowUnexpectedToken(); + + typeReference = typeReference.MakeArrayType(rank); + + ReadToken(); } return typeReference; } - int CountDimentions() { - return 0; + int CountRank() { + int rank = 0; + do { + rank++; + } while(ReadToken() && Token == TokenType.Comma); + return rank; } TypeReference[] ReadTypeReferenceList() { 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 @@ -37,7 +37,7 @@ namespace Implab.ServiceHost.Unity { var parameters = constructorInjection.Parameters? .Select(x => { - var valueBuilder = new InjectionValueBuilder(m_resolver, null); + var valueBuilder = new InjectionParameterBuilder(m_resolver, null); x.Visit(valueBuilder); return valueBuilder.Injection; }) @@ -48,11 +48,9 @@ namespace Implab.ServiceHost.Unity { } internal void Visit(MethodInjectionElement methodInjection) { - var valueContext = new InjectionValueBuilder(m_resolver, null); - var parameters = methodInjection.Parameters? .Select(x => { - var valueBuilder = new InjectionValueBuilder(m_resolver, null); + var valueBuilder = new InjectionParameterBuilder(m_resolver, null); x.Visit(valueBuilder); return valueBuilder.Injection; }) @@ -66,8 +64,8 @@ namespace Implab.ServiceHost.Unity { if (propertyInjection.Value == null) throw new Exception($"A value value must be specified for the property '{propertyInjection.Name}'"); - var propertyType = RegistrationType.GetProperty(propertyInjection.Name)?.PropertyType; - var valueContext = new InjectionValueBuilder(m_resolver, propertyType); + var propertyType = ImplementationType.GetProperty(propertyInjection.Name)?.PropertyType; + var valueContext = new InjectionParameterBuilder(m_resolver, propertyType); propertyInjection.Value.Visit(valueContext); var injection = new InjectionProperty(propertyInjection.Name, valueContext.Injection); diff --git a/Implab.ServiceHost/Unity/TypeResolutionContext.cs b/Implab.ServiceHost/Unity/TypeResolutionContext.cs new file mode 100644 --- /dev/null +++ b/Implab.ServiceHost/Unity/TypeResolutionContext.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using Implab.Diagnostics; + +namespace Implab.ServiceHost.Unity { + using static Trace; + + /// + /// Позволяет обойти вложенные типы и собрать цеочку из типов и параметров генериков, которые они предлагают + /// + internal class TypeResolutionContext { + readonly TypeResolver m_resolver; + + + readonly List m_genericParameters = new List(); + + public IEnumerable GenericParameters { get { return m_genericParameters; } } + + public Type ResolvedType { get; private set; } + + + public int ArrayRank { get; private set; } + + + public bool ThrowOnFail { get; private set; } + + public TypeResolutionContext(TypeResolver resolver, bool throwOnFail) { + m_resolver = resolver; + ThrowOnFail = throwOnFail; + } + + public Type MakeType() { + return m_genericParameters.Count > 0 ? + ResolvedType?.MakeGenericType(m_genericParameters.ToArray()) : + ResolvedType; + } + + public void Visit(SpecializedTypeReference typeReference) { + typeReference.GenericType.Visit(this); + + if (ResolvedType != null) { + foreach (var genericParamRef in typeReference.GenericParameters) { + var context = new TypeResolutionContext(m_resolver, ThrowOnFail); + genericParamRef.Visit(context); + m_genericParameters.Add(context.MakeType()); + } + } + } + + public void Visit(NestedTypeReference typeReference) { + typeReference.DeclaringType.Visit(this); + if (ResolvedType != null) + ResolvedType = ResolvedType?.GetNestedType(typeReference.ClrName) ?? Failed(typeReference); + } + + public void Visit(RootTypeReference typeReference) { + ResolvedType = m_resolver.Resolve(typeReference.Namespace, typeReference.ClrName) ?? Failed(typeReference); + } + + public void Visit(ArrayTypeReference typeReference) { + var context = new TypeResolutionContext(m_resolver, ThrowOnFail); + typeReference.ItemsType.Visit(context); + ResolvedType = typeReference.Rank == 1 ? + context.MakeType()?.MakeArrayType() : + context.MakeType()?.MakeArrayType(typeReference.Rank); + } + + Type Failed(TypeReference reference) { + Log($"Falied to resolve {reference}"); + if (ThrowOnFail) + throw new Exception($"Failed to resolve {reference}"); + return null; + } + } +} \ No newline at end of file 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 @@ -6,6 +6,7 @@ using System.Text.RegularExpressions; using Implab.Diagnostics; namespace Implab.ServiceHost.Unity { + using System.Diagnostics; using static Trace; public class TypeResolver { readonly Dictionary m_cache = new Dictionary(); @@ -13,12 +14,28 @@ namespace Implab.ServiceHost.Unity { Regex _nsRx = new Regex(@"^\w+(\.\w+)*$", RegexOptions.Compiled); readonly LinkedList m_namespases = new LinkedList(); + internal Type Resolve(string ns, string typeName) { + var fullName = string.IsNullOrEmpty(ns) ? typeName : $"{ns}.{typeName}"; + + return ProbeInNamespaces(fullName); + } + + public Type Resolve(TypeReference typeReference, bool throwOnFail) { + var context = new TypeResolutionContext(this, throwOnFail); + typeReference.Visit(context); + return context.MakeType(); + } + + public Type Resolve(string typeSpec, bool throwOnFail) { + var typeReference = TypeReference.Parse(typeSpec); + return Resolve(typeReference, throwOnFail); + } + LinkedListNode m_insertAt; readonly TypeResolver m_parent; public TypeResolver() : this(null) { - } public TypeResolver(TypeResolver parent) { @@ -42,63 +59,31 @@ namespace Implab.ServiceHost.Unity { 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 ProbeInNamespaces(string localName) { 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; + if (!m_cache.TryGetValue(localName, out resolved)) { + foreach (var ns in m_namespases) { + var typeName = string.IsNullOrEmpty(ns) ? localName : $"{ns}.{localName}"; + resolved = Probe(typeName); + if (resolved != null) { + Log($"Probe '{localName}' -> '{resolved.FullName}'"); + break; + } + } + + if (resolved == null && m_parent != null) + resolved = m_parent.ProbeInNamespaces(localName); + + if(resolved == null) + Log($"Probe '{localName}' failed"); + + m_cache[localName] = resolved; } return resolved; } - public Type Resolve(string typeSpec) { - return Resolve(TypeReference.Parse(typeSpec)); - } - - 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.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); - - 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 { - Log("Probe failed {0} in '{1}': {2}", referenceName, ns, typeName); - } - } - - return null; - } - Type Probe(string typeName) { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); @@ -109,17 +94,5 @@ namespace Implab.ServiceHost.Unity { } return null; } - - 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); - } - - return builder.ToString(); - } } } \ 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 @@ -13,7 +13,7 @@ namespace Implab.ServiceHost.Unity { return string.IsNullOrEmpty(Value) ? Text : Value; } - public override void Visit(InjectionValueBuilder builder) { + public override void Visit(InjectionParameterBuilder builder) { var type = builder.ResolveInjectedValueType(TypeName); builder.SetValue(type, TypeDescriptor.GetConverter(type).ConvertFromString(GetTextValue())); } diff --git a/Implab/Xml/SerializationHelpers.cs b/Implab/Xml/SerializationHelpers.cs --- a/Implab/Xml/SerializationHelpers.cs +++ b/Implab/Xml/SerializationHelpers.cs @@ -12,17 +12,17 @@ using System.Xml.Serialization; namespace Implab.Xml { public static class SerializationHelpers { public static string SerializeAsString(T obj) { - return SerializersPool.Instance.SerializeAsString(obj); + return XmlDefaultSerializer.Instance.SerializeAsString(obj); } public static void Serialize(XmlWriter writer, T obj) { - SerializersPool.Instance.Serialize(writer, obj); + XmlDefaultSerializer.Instance.Serialize(writer, obj); } public static XmlDocument SerializeAsXmlDocument(T obj) { var doc = new XmlDocument(); using (var writer = doc.CreateNavigator().AppendChild()) { - SerializersPool.Instance.Serialize(writer, obj); + XmlDefaultSerializer.Instance.Serialize(writer, obj); } return doc; } @@ -30,38 +30,35 @@ namespace Implab.Xml { public static XDocument SerializeAsXDocument(T obj) { var doc = new XDocument(); using (var writer = doc.CreateWriter()) { - SerializersPool.Instance.Serialize(writer, obj); + XmlDefaultSerializer.Instance.Serialize(writer, obj); } return doc; } public static void SerializeToFile(string file, T obj) { - using (var writer = File.CreateText(file)) - SerializersPool.Instance.Serialize(writer, obj); + XmlDefaultSerializer.Instance.SerializeToFile(obj, file); } public static void SerializeToElementChild(XmlElement element, T obj) { - using(var writer = element.CreateNavigator().AppendChild()) - SerializersPool.Instance.Serialize(writer, obj); + XmlDefaultSerializer.Instance.Serialize(obj, element); } public static T Deserialize(XmlReader reader) { - return SerializersPool.Instance.Deserialize(reader); + return (T)XmlDefaultSerializer.Instance.Deserialize(reader); } public static T DeserializeFromFile(string file) { - using(var reader = XmlReader.Create(File.OpenText(file))) - return Deserialize(reader); + return (T)XmlDefaultSerializer.Instance.DeserializeFromFile(file); } public static T DeserializeFromString(string data) { - return SerializersPool.Instance.DeserializeFromString(data); + return (T)XmlDefaultSerializer.Instance.DeserializeFromString(data); } public static T DeserializeFromXmlNode(XmlNode node) { Safe.ArgumentNotNull(node, nameof(node)); using (var reader = node.CreateNavigator().ReadSubtree()) - return SerializersPool.Instance.Deserialize(reader); + return (T)XmlDefaultSerializer.Instance.Deserialize(reader); } public static T DeserializeJson(TextReader textReader) { @@ -71,12 +68,12 @@ namespace Implab.Xml { FlattenArrays = true }; - using(var reader = JsonXmlReader.CreateJsonXmlReader(textReader, options)) + using (var reader = JsonXmlReader.CreateJsonXmlReader(textReader, options)) return Deserialize(reader); } public static T DeserializeJsonFromString(string data) { - using(var reader = new StringReader(data)) { + using (var reader = new StringReader(data)) { return DeserializeJson(reader); } } @@ -87,7 +84,7 @@ namespace Implab.Xml { } public static string SerializeJsonAsString(T obj) { - using(var writer = new StringWriter()) { + using (var writer = new StringWriter()) { SerializeJson(writer, obj); return writer.ToString(); } diff --git a/Implab/Xml/SerializersPool.cs b/Implab/Xml/SerializersPool.cs --- a/Implab/Xml/SerializersPool.cs +++ b/Implab/Xml/SerializersPool.cs @@ -9,6 +9,7 @@ using System.Xml; using System.Xml.Serialization; namespace Implab.Xml { + [Obsolete("this class will be removed, use XmlDefaultSerializer")] public class SerializersPool : ObjectPool { static readonly SerializersPool _instance = new SerializersPool(); diff --git a/Implab/Xml/XmlDefaultSerializer.cs b/Implab/Xml/XmlDefaultSerializer.cs new file mode 100644 --- /dev/null +++ b/Implab/Xml/XmlDefaultSerializer.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; +using Implab.Components; + +namespace Implab.Xml { + /// + /// This class used to get default serializer for the specified type . + /// + /// The type used to create + public static class XmlDefaultSerializer { + static readonly LazyAndWeak m_instance = new LazyAndWeak(() => new XmlSerializer(typeof(T))); + + public static XmlSerializer Instance { get { return m_instance.Value; } } + } +} \ No newline at end of file diff --git a/Implab/Xml/XmlSerializerExtensions.cs b/Implab/Xml/XmlSerializerExtensions.cs new file mode 100644 --- /dev/null +++ b/Implab/Xml/XmlSerializerExtensions.cs @@ -0,0 +1,89 @@ +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Serialization; + +namespace Implab.Xml { + public static class XmlSerializerExtensions { + + public static void Serialize(this XmlSerializer that, object obj, XmlElement element) { + Safe.ArgumentNotNull(that, nameof(that)); + using (var writer = element.CreateNavigator().AppendChild()) + that.Serialize(writer, obj); + } + + public static void Serialize(this XmlSerializer that, object obj, XElement element) { + Safe.ArgumentNotNull(that, nameof(that)); + using (var writer = element.CreateWriter()) + that.Serialize(writer, obj); + } + + public static XDocument SerializeAsXDocumnet(this XmlSerializer that, object obj) { + Safe.ArgumentNotNull(that, nameof(that)); + var doc = new XDocument(); + using (var writer = doc.CreateWriter()) { + that.Serialize(writer, obj); + } + return doc; + } + + public static XmlDocument SerializeAsXmlDocument(this XmlSerializer that, object obj) { + Safe.ArgumentNotNull(that, nameof(that)); + var doc = new XmlDocument(); + using (var writer = doc.CreateNavigator().AppendChild()) { + that.Serialize(writer, obj); + } + return doc; + } + public static string SerializeAsString(this XmlSerializer that, object obj) { + Safe.ArgumentNotNull(that, nameof(that)); + using (var writer = new StringWriter()) { + that.Serialize(writer, obj); + return writer.ToString(); + } + } + + public static void SerializeToFile(this XmlSerializer that, object obj, string file, Encoding encoding) { + Safe.ArgumentNotNull(that, nameof(that)); + using (var writer = new StreamWriter(File.Create(file),encoding)) + that.Serialize(writer, obj); + } + + public static void SerializeToFile(this XmlSerializer that, object obj, string file) { + SerializeToFile(that, obj, file, Encoding.UTF8); + } + + public static object Deserialize(this XmlSerializer that, XmlElement element) { + Safe.ArgumentNotNull(that, nameof(that)); + Safe.ArgumentNotNull(element, nameof(element)); + + using (var reader = element.CreateNavigator().ReadSubtree()) + return that.Deserialize(reader); + } + + public static object Deserialize(this XmlSerializer that, XElement element) { + Safe.ArgumentNotNull(that, nameof(that)); + Safe.ArgumentNotNull(element, nameof(element)); + + using (var reader = element.CreateReader()) + return that.Deserialize(reader); + } + + public static object DeserializeFromString(this XmlSerializer that, string text) { + Safe.ArgumentNotNull(that, nameof(that)); + + using(var reader = new StringReader(text)) + return that.Deserialize(reader); + } + + public static object DeserializeFromFile(this XmlSerializer that, string file) { + Safe.ArgumentNotNull(that, nameof(that)); + + using(var reader = File.OpenRead(file)) + return that.Deserialize(reader); + } + + + } +} \ No newline at end of file