Mercurial > pub > ImplabNet
changeset 278:6691aff01de1 v3
Implab: added XmlDefaultSeializer (SerializersPool is now obsolete)
Implab.ServiceHost: rewritten TypeReference (added support for nested types), stable API
line wrap: on
line diff
--- a/Implab.Playground/Program.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.Playground/Program.cs Thu May 03 09:59:44 2018 +0300 @@ -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 @@ } + public class FooFactory : IFactory<Foo>, IFactory<Foo.Bar> { + + public bool UseSsl { get; set; } + + public string Connection { get; set; } + + public Foo Create() { + return new Foo() { + Name = "AutoFac" + }; + } + + Foo.Bar IFactory<Foo.Bar>.Create() { + return new Foo.Bar(); + } + } + public interface IContainer<T> { T Instance { get; set; } } public class Container<T> : IContainer<T> { + public class Bar { + + } + + public class Bar<T2> { + public class Baz { + + } + + } + public Container() { } @@ -65,29 +94,22 @@ 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<IContainer<string>>(); - Console.WriteLine($"Resolved1: {stopwatch.ElapsedMilliseconds}"); + var t = resolver.Resolve(spec, true); - stopwatch.Restart(); - var instace2 = container.Resolve<IContainer<Foo>>(); - 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) {
--- a/Implab.Playground/data/sample.xml Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.Playground/data/sample.xml Thu May 03 09:59:44 2018 +0300 @@ -2,6 +2,7 @@ <container xmlns="http://implab.org/schemas/servicehost/unity.v1.xsd"> <namespace name="System"/> <namespace name="System.Collections.Generic"/> + <namespace name="Implab.Components"/> <namespace name="Implab.Playground"/> <!-- foo1 --> @@ -36,7 +37,7 @@ </method> <method name="AddRange"> <array itemsType="T"> - <dependency name="foo2"/> + <dependency name="foo2-bar"/> </array> </method> </register> @@ -61,8 +62,13 @@ <value name="connection1" type="String"><![CDATA[Connect me <here>!]]></value> <value name="name1" type="String" value="Hello!"/> - <factory name="foo3" type="FooFactory"> - <parameter name="FooName"><![CDATA[Wired "" objecty <> name @#$%^&]]></parameter> + <factory name="foo3" type="IFactory{Foo}" mapTo="FooFactory"> + <property name="Connection"> + <value><![CDATA[Wired "" objecty <> name @#$%^&]]></value> + </property> + <property name="UseSsl"> + <value>false</value> + </property> </factory> </container> \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ArrayParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/ArrayParameterElement.cs Thu May 03 09:59:44 2018 +0300 @@ -13,7 +13,7 @@ [XmlElement("default", typeof(DefaultParameterElement))] public InjectionParameterElement[] Items { get; set; } - public override void Visit(InjectionValueBuilder builder) { + public override void Visit(InjectionParameterBuilder builder) { builder.Visit(this); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/ArrayTypeReference.cs Thu May 03 09:59:44 2018 +0300 @@ -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
--- a/Implab.ServiceHost/Unity/AssemblyElement.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/AssemblyElement.cs Thu May 03 09:59:44 2018 +0300 @@ -8,7 +8,7 @@ public string AssemblyName { get; set; } public override void Visit(ContainerBuilder builder) { - builder.Visit(this); + builder.AddAssembly(AssemblyName); } } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ContainerBuilder.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/ContainerBuilder.cs Thu May 03 09:59:44 2018 +0300 @@ -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<ContainerBuilder>; - public class ContainerBuilder { readonly TypeResolver m_resolver; @@ -36,7 +27,7 @@ } public Type ResolveType(string typeReference) { - return m_resolver.Resolve(typeReference); + return m_resolver.Resolve(typeReference, true); } @@ -91,16 +82,85 @@ ); } - 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 @@ public void LoadConfig(string file) { var config = m_schema.LoadFile(file); - config.Visit(this); } - + } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/ContainerConfigurationSchema.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/ContainerConfigurationSchema.cs Thu May 03 09:59:44 2018 +0300 @@ -55,6 +55,7 @@ var schema = new ContainerConfigurationSchema(); schema.RegisterContainerElement<RegisterElement>("register"); + schema.RegisterContainerElement<FactoryElement>("factory"); schema.RegisterContainerElement<SerializedElement>("serialized"); schema.RegisterContainerElement<ValueElement>("value"); schema.RegisterContainerElement<IncludeElement>("include");
--- a/Implab.ServiceHost/Unity/DefaultParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/DefaultParameterElement.cs Thu May 03 09:59:44 2018 +0300 @@ -5,7 +5,7 @@ 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)); }
--- a/Implab.ServiceHost/Unity/DependencyParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/DependencyParameterElement.cs Thu May 03 09:59:44 2018 +0300 @@ -9,7 +9,7 @@ [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); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/FactoryActivator.cs Thu May 03 09:59:44 2018 +0300 @@ -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<string, Type> resolver) { + return RegistrationType; + } + + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/FactoryElement.cs Thu May 03 09:59:44 2018 +0300 @@ -0,0 +1,72 @@ +using System; +using System.Xml.Serialization; +using Implab.Components; + +namespace Implab.ServiceHost.Unity { + /// <summary> + /// Расширяет стандартную регистрацию типа до фабрики, вместе с регистрацией + /// самой фабрики создаются регистрации сервисов, которые она предоставляет. + /// </summary> + public class FactoryElement : RegisterElement { + + /// <summary> + /// Записи о сервисах, которые создаются данной фабрикой. + /// </summary> + /// <remarks> + /// Сервисы, которые указаны в регистарциях они должны соответсвовать тому, + /// что фабрика возвращает, но это остается на откуп контейнера + /// </remarks> + [XmlElement("provides")] + public ProvidesElement[] Provides { get; set; } + + /// <summary> + /// Переопределяет стандарное поведение регистрации типа в контейнере, + /// дополняя его регистрацией фабричных методов для получения типов. + /// </summary> + /// <param name="builder">Объект для конфигурирования контейнера.</param> + 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
--- a/Implab.ServiceHost/Unity/FactoryRegistrationBuilder.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/FactoryRegistrationBuilder.cs Thu May 03 09:59:44 2018 +0300 @@ -13,12 +13,25 @@ internal FactoryRegistrationBuilder(Type registrationType) : base(registrationType) { } + /// <summary> + /// Задает делегат, который будет использоваться в качестве фабрики + /// для создания экземпляров, параметры делагата будет заполнены + /// соответствующими зависимостями. + /// </summary> + /// <param name="factory">Фабрика для создания экземпляров.</param> public void SetFactoryDelegate(Delegate factory) { Safe.ArgumentNotNull(factory, nameof(factory)); Factory = new DelegateInjectionFactory(factory); } + /// <summary> + /// Указывает зависимость типа <typeparamref name="T"/> с именем + /// <paramref name="dependencyName"/>, которая будет передана в качетве + /// параметра делегату <paramref name="factory"/> + /// </summary> + /// <param name="dependencyName">Имя зависимости</param> + /// <param name="factory">Фабрика для создания экземпляра</param> public void SetFactoryDependency<T>(string dependencyName, Func<T, object> factory) { Safe.ArgumentNotNull(factory, nameof(factory)); @@ -28,6 +41,11 @@ }); } + /// <summary> + /// Указывает зависимость, реализующую интерфейс <see cref="IFactory{TObj}"/>, + /// которая будет использоваться в качестве фабрики для создания объектов + /// </summary> + /// <param name="dependencyName"></param> public void SetFactoryDependency<TFac, TObj>(string dependencyName) where TFac : IFactory<TObj> { Factory = new InjectionFactory(c => c.Resolve<TFac>(dependencyName).Create());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/IFactoryMemberInjection.cs Thu May 03 09:59:44 2018 +0300 @@ -0,0 +1,5 @@ +namespace Implab.ServiceHost.Unity { + public interface IFactoryMemberInjection { + void Visit(FactoryRegistrationBuilder builder); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/IFactoryRegistration.cs Thu May 03 09:59:44 2018 +0300 @@ -0,0 +1,7 @@ +using System.Collections.Generic; + +namespace Implab.ServiceHost.Unity { + public interface IFactoryRegistration : IRegistration { + IEnumerable<IFactoryMemberInjection> MemberInjections { get; } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/IInjectionParameter.cs Thu May 03 09:59:44 2018 +0300 @@ -0,0 +1,5 @@ +namespace Implab.ServiceHost.Unity { + public interface IInjectionParameter { + void Visit(InjectionParameterBuilder builder); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/IInstanceRegistration.cs Thu May 03 09:59:44 2018 +0300 @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Implab.ServiceHost.Unity { + public interface IInstanceRegistration : IRegistration { + + IEnumerable<IInjectionParameter> MemberInjections { get; } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/IRegistration.cs Thu May 03 09:59:44 2018 +0300 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/ITypeMemberInjection.cs Thu May 03 09:59:44 2018 +0300 @@ -0,0 +1,6 @@ +namespace Implab.ServiceHost.Unity +{ + public interface ITypeMemberInjection { + void Visit(TypeRegistrationBuilder builder); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/ITypeRegistration.cs Thu May 03 09:59:44 2018 +0300 @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace Implab.ServiceHost.Unity { + public interface ITypeRegistration : IRegistration { + Type GetImplementationType(ContainerBuilder builder); + + IEnumerable<ITypeMemberInjection> MemberInjections { get; } + } +} \ No newline at end of file
--- a/Implab.ServiceHost/Unity/IncludeElement.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/IncludeElement.cs Thu May 03 09:59:44 2018 +0300 @@ -6,8 +6,8 @@ [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
--- a/Implab.ServiceHost/Unity/InjectionParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/InjectionParameterElement.cs Thu May 03 09:59:44 2018 +0300 @@ -7,6 +7,6 @@ [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
--- a/Implab.ServiceHost/Unity/InjectionValueBuilder.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/InjectionValueBuilder.cs Thu May 03 09:59:44 2018 +0300 @@ -7,7 +7,7 @@ namespace Implab.ServiceHost.Unity { - public class InjectionValueBuilder { + public class InjectionParameterBuilder { readonly TypeResolver m_resolver; @@ -26,7 +26,7 @@ } } - internal InjectionValueBuilder(TypeResolver resolver, Type defaultType) { + internal InjectionParameterBuilder(TypeResolver resolver, Type defaultType) { m_resolver = resolver; DefaultType = defaultType; } @@ -37,11 +37,11 @@ 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 @@ 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; }
--- a/Implab.ServiceHost/Unity/InstanceRegistrationBuilder.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/InstanceRegistrationBuilder.cs Thu May 03 09:59:44 2018 +0300 @@ -4,10 +4,10 @@ { 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
--- a/Implab.ServiceHost/Unity/NamespaceElement.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/NamespaceElement.cs Thu May 03 09:59:44 2018 +0300 @@ -8,8 +8,8 @@ [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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/NestedTypeReference.cs Thu May 03 09:59:44 2018 +0300 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/ProvidesElement.cs Thu May 03 09:59:44 2018 +0300 @@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/RootTypeReference.cs Thu May 03 09:59:44 2018 +0300 @@ -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
--- a/Implab.ServiceHost/Unity/SerializedParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/SerializedParameterElement.cs Thu May 03 09:59:44 2018 +0300 @@ -21,7 +21,7 @@ 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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/SpecializedTypeReference.cs Thu May 03 09:59:44 2018 +0300 @@ -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
--- a/Implab.ServiceHost/Unity/TypeReference.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/TypeReference.cs Thu May 03 09:59:44 2018 +0300 @@ -3,56 +3,180 @@ 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; } + /// <summary> + /// Ссылка на тип, является абстрактной записью имени CLR типа. + /// </summary> + /// <remarks> + /// Ссылка на тип содержит сокращенную информацию о типе и для ее интерпретации + /// требуется некоторый контекст, который позволит превратить ее в полноценный + /// <see cref="Type"/>. Ссылки на тип позволяют записать: + /// <list> + /// <item><description>общие типы, их специализации</description></item> + /// <item><description>вложенные типы</description></item> + /// <item><description>массивы</description></item> + /// </list> + /// <para> + /// Для получения из ссылки на тип конкретного CLR типа используется <see cref="TypeResolver.Resolve(TypeReference, bool)"/>. + /// </para> + /// <para> + /// Ссылку на тип можно создать либо програмно при помощи методов <see cref="Create(string, string, int)"/>, + /// <see cref="Create(string, int)"/>, <see cref="MakeArrayType(int)"/>, <see cref="MakeGenericType(TypeReference[])"/>, + /// либо разобрав строку со спецификацией при помощи метода <see cref="Parse(string)"/>. + /// </para> + /// <para> + /// Спецификация ссыдки на тип имеет следующий вид <c>Name.Space.MyType+Nested{String}[][]</c>, где: + /// <list type="table"> + /// <item> + /// <term><c>.</c></term> + /// <description>Разделяет элементы пространства имен</description> + /// <item> + /// <item> + /// <term><c>+</c></term> + /// <description>Разделяет вложенные типы</description> + /// <item> + /// <item> + /// <term><c>[]</c>, <c>[,,,]</c></term> + /// <description>Указывает на то, что тип является массивом, также указывается его размерность</description> + /// <item> + /// <item> + /// <term><c>{}</c>, <c>{,,}</c>, <c>{Int32,String}</c></term> + /// <description>Указывает на то, что тип является общим, также + /// указывается количество параметров, либо конкретные типы для + /// специализации</description> + /// <item> + /// </list> + /// </para> + /// </remarks> + public abstract class TypeReference { - public bool IsOpenGeneric { + /// <summary> + /// Имя типа без дополнительных элементов, указывающих на то, что он общий или массив. + /// </summary> + /// <remarks> + /// Для массивов это имя его элементов. + /// </remarks> + public abstract string Name { get; } + + /// <summary> + /// Пространство имен в котором нахожится тип. + /// </summary> + /// <remarks> + /// Для вложенных типов это пространтство имен типа самого верхнего уровня, + /// для массивов - пространство имен его элементов. + /// </remarks> + public abstract string Namespace { get; } + + /// <summary> + /// Количество параметров общего типа. + /// </summary> + /// <remarks> + /// <para> + /// Вложенные типы неявно получают параметры от типов в которых они объявлены, + /// данное свойство это не учитывает, возвращается только количество собственных + /// параметров. + /// </para> + /// <para> + /// Данное свойство используется для получения CRL имени типа. + /// </para> + /// </remarks> + 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; } } + /// <summary> + /// Создает ссылку на специализацию текущего типа. + /// </summary> + /// <param name="genericParameters">Ссылки на типы, которые будут использоваться для специализации текущего типа.</param> + /// <returns>Специализация данного типа.</returns> + 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); + } + + /// <summary> + /// Создает ссылку на тип массива указанной размерности, элементами которого являются экземпаляры даннго типа. + /// </summary> + /// <param name="rank">Размерность, если размерность <c>1</c> создается вектор (<see cref="Type.MakeArrayType()"/>).</param> + /// <returns>Ссылка на тип массива</returns> + public ArrayTypeReference MakeArrayType(int rank) { + Safe.ArgumentInRange(rank > 0, nameof(rank)); + + return new ArrayTypeReference(this, rank); + } + + /// <summary> + /// Создает ссылку на вложенный тип. + /// </summary> + /// <param name="name">Имя типа</param> + /// <param name="genericParameters">Количество параметров, если это общий тип, иначе 0.</param> + /// <returns>Ссылка на вложенный тип.</returns> + public TypeReference Create(string name, int genericParameters) { + Safe.ArgumentNotEmpty(name, nameof(name)); + Safe.ArgumentInRange(genericParameters >= 0, nameof(genericParameters)); + + return new NestedTypeReference(this, name, genericParameters); + } + + /// <summary> + /// Возвращает строковое представление ссылки на тип. + /// </summary> + /// <returns></returns> 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); + + /// <summary> + /// Создает ссылку на тип. + /// </summary> + /// <param name="ns">Пространство имен, либо его фрагмент.</param> + /// <param name="name">Имя типа без указания на количество параметров, либо на то, что это массив.</param> + /// <param name="genericParameters">Количество параметров типа, если это общий тип, иначе 0.</param> + /// <returns>Ссылка на тип.</returns> + 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); + } + + /// <summary> + /// Разирает строковую запись ссылки на тип. + /// </summary> + /// <param name="typeSpec">Строковая запись ссылки на тип, например <c>Dictionary{String,String}</c></param> + /// <returns>Ссылка на тип.</returns> + public static TypeReference Parse(string typeSpec) { + var parser = new TypeReferenceParser(typeSpec); return parser.Parse(); } + } } \ No newline at end of file
--- a/Implab.ServiceHost/Unity/TypeReferenceParser.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/TypeReferenceParser.cs Thu May 03 09:59:44 2018 +0300 @@ -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 @@ 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 @@ case "]": m_token = TokenType.CloseArray; break; + case "+": + m_token = TokenType.Plus; + break; } } return true; @@ -97,7 +103,7 @@ return result; } - string[] ReadTypeName() { + string[] ReadQTypeName() { var parts = new List<string>(); string current = null; @@ -129,31 +135,95 @@ 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>(); + } + + 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() {
--- a/Implab.ServiceHost/Unity/TypeRegistrationBuilder.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/TypeRegistrationBuilder.cs Thu May 03 09:59:44 2018 +0300 @@ -37,7 +37,7 @@ 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 @@ } 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 @@ 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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab.ServiceHost/Unity/TypeResolutionContext.cs Thu May 03 09:59:44 2018 +0300 @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using Implab.Diagnostics; + +namespace Implab.ServiceHost.Unity { + using static Trace<TypeResolver>; + + /// <summary> + /// Позволяет обойти вложенные типы и собрать цеочку из типов и параметров генериков, которые они предлагают + /// </summary> + internal class TypeResolutionContext { + readonly TypeResolver m_resolver; + + + readonly List<Type> m_genericParameters = new List<Type>(); + + public IEnumerable<Type> 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
--- a/Implab.ServiceHost/Unity/TypeResolver.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/TypeResolver.cs Thu May 03 09:59:44 2018 +0300 @@ -6,6 +6,7 @@ using Implab.Diagnostics; namespace Implab.ServiceHost.Unity { + using System.Diagnostics; using static Trace<TypeResolver>; public class TypeResolver { readonly Dictionary<string, Type> m_cache = new Dictionary<string, Type>(); @@ -13,12 +14,28 @@ Regex _nsRx = new Regex(@"^\w+(\.\w+)*$", RegexOptions.Compiled); readonly LinkedList<string> m_namespases = new LinkedList<string>(); + 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<string> m_insertAt; readonly TypeResolver m_parent; public TypeResolver() : this(null) { - } public TypeResolver(TypeResolver parent) { @@ -42,63 +59,31 @@ 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 @@ } 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
--- a/Implab.ServiceHost/Unity/ValueParameterElement.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab.ServiceHost/Unity/ValueParameterElement.cs Thu May 03 09:59:44 2018 +0300 @@ -13,7 +13,7 @@ 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())); }
--- a/Implab/Xml/SerializationHelpers.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab/Xml/SerializationHelpers.cs Thu May 03 09:59:44 2018 +0300 @@ -12,17 +12,17 @@ namespace Implab.Xml { public static class SerializationHelpers { public static string SerializeAsString<T>(T obj) { - return SerializersPool<T>.Instance.SerializeAsString(obj); + return XmlDefaultSerializer<T>.Instance.SerializeAsString(obj); } public static void Serialize<T>(XmlWriter writer, T obj) { - SerializersPool<T>.Instance.Serialize(writer, obj); + XmlDefaultSerializer<T>.Instance.Serialize(writer, obj); } public static XmlDocument SerializeAsXmlDocument<T>(T obj) { var doc = new XmlDocument(); using (var writer = doc.CreateNavigator().AppendChild()) { - SerializersPool<T>.Instance.Serialize(writer, obj); + XmlDefaultSerializer<T>.Instance.Serialize(writer, obj); } return doc; } @@ -30,38 +30,35 @@ public static XDocument SerializeAsXDocument<T>(T obj) { var doc = new XDocument(); using (var writer = doc.CreateWriter()) { - SerializersPool<T>.Instance.Serialize(writer, obj); + XmlDefaultSerializer<T>.Instance.Serialize(writer, obj); } return doc; } public static void SerializeToFile<T>(string file, T obj) { - using (var writer = File.CreateText(file)) - SerializersPool<T>.Instance.Serialize(writer, obj); + XmlDefaultSerializer<T>.Instance.SerializeToFile(obj, file); } public static void SerializeToElementChild<T>(XmlElement element, T obj) { - using(var writer = element.CreateNavigator().AppendChild()) - SerializersPool<T>.Instance.Serialize(writer, obj); + XmlDefaultSerializer<T>.Instance.Serialize(obj, element); } public static T Deserialize<T>(XmlReader reader) { - return SerializersPool<T>.Instance.Deserialize(reader); + return (T)XmlDefaultSerializer<T>.Instance.Deserialize(reader); } public static T DeserializeFromFile<T>(string file) { - using(var reader = XmlReader.Create(File.OpenText(file))) - return Deserialize<T>(reader); + return (T)XmlDefaultSerializer<T>.Instance.DeserializeFromFile(file); } public static T DeserializeFromString<T>(string data) { - return SerializersPool<T>.Instance.DeserializeFromString(data); + return (T)XmlDefaultSerializer<T>.Instance.DeserializeFromString(data); } public static T DeserializeFromXmlNode<T>(XmlNode node) { Safe.ArgumentNotNull(node, nameof(node)); using (var reader = node.CreateNavigator().ReadSubtree()) - return SerializersPool<T>.Instance.Deserialize(reader); + return (T)XmlDefaultSerializer<T>.Instance.Deserialize(reader); } public static T DeserializeJson<T>(TextReader textReader) { @@ -71,12 +68,12 @@ FlattenArrays = true }; - using(var reader = JsonXmlReader.CreateJsonXmlReader(textReader, options)) + using (var reader = JsonXmlReader.CreateJsonXmlReader(textReader, options)) return Deserialize<T>(reader); } public static T DeserializeJsonFromString<T>(string data) { - using(var reader = new StringReader(data)) { + using (var reader = new StringReader(data)) { return DeserializeJson<T>(reader); } } @@ -87,7 +84,7 @@ } public static string SerializeJsonAsString<T>(T obj) { - using(var writer = new StringWriter()) { + using (var writer = new StringWriter()) { SerializeJson(writer, obj); return writer.ToString(); }
--- a/Implab/Xml/SerializersPool.cs Sat Apr 28 18:48:09 2018 +0300 +++ b/Implab/Xml/SerializersPool.cs Thu May 03 09:59:44 2018 +0300 @@ -9,6 +9,7 @@ using System.Xml.Serialization; namespace Implab.Xml { + [Obsolete("this class will be removed, use XmlDefaultSerializer")] public class SerializersPool<T> : ObjectPool<XmlSerializer> { static readonly SerializersPool<T> _instance = new SerializersPool<T>();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Xml/XmlDefaultSerializer.cs Thu May 03 09:59:44 2018 +0300 @@ -0,0 +1,14 @@ +using System.Xml.Serialization; +using Implab.Components; + +namespace Implab.Xml { + /// <summary> + /// This class used to get default serializer for the specified type <typeparamref name="T"/>. + /// </summary> + /// <typeparam name="T">The type used to create <see cref="XmlSerializer"/></typeparam> + public static class XmlDefaultSerializer<T> { + static readonly LazyAndWeak<XmlSerializer> m_instance = new LazyAndWeak<XmlSerializer>(() => new XmlSerializer(typeof(T))); + + public static XmlSerializer Instance { get { return m_instance.Value; } } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Implab/Xml/XmlSerializerExtensions.cs Thu May 03 09:59:44 2018 +0300 @@ -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