diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -25,3 +25,5 @@ Implab.Playground/obj/
Implab.Playground/bin/
Implab.ServiceHost/bin/
Implab.ServiceHost/obj/
+Implab.ServiceHost.Test/bin/
+Implab.ServiceHost.Test/obj/
diff --git a/Implab.ServiceHost.Test/Implab.ServiceHost.Test.csproj b/Implab.ServiceHost.Test/Implab.ServiceHost.Test.csproj
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/Implab.ServiceHost.Test.csproj
@@ -0,0 +1,29 @@
+
+
+ netcoreapp2.0;net46
+ /usr/lib/mono/4.5/
+
+
+
+ netcoreapp2.0;net46
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Implab.ServiceHost.Test/data/container/basic.xml b/Implab.ServiceHost.Test/data/container/basic.xml
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/data/container/basic.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+ I'm default!
+
+
+
+
+
+
+ GOOD
+
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Baaar
+
+
+
+ !]]>
+
+
+
+
+ name @#$%^&]]>
+
+
+ false
+
+
+
+
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/data/container/empty.xml b/Implab.ServiceHost.Test/data/container/empty.xml
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/data/container/empty.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/data/container/generic.services.xml b/Implab.ServiceHost.Test/data/container/generic.services.xml
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/data/container/generic.services.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+ 3
+
+
+
+
+ 1
+
+
+
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ boxForString
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/data/container/p2.xml b/Implab.ServiceHost.Test/data/container/p2.xml
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/data/container/p2.xml
@@ -0,0 +1,6 @@
+
+
+ Trohn
+ Javolta
+ 88
+
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/data/container/serialized.instances.xml b/Implab.ServiceHost.Test/data/container/serialized.instances.xml
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/data/container/serialized.instances.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+ Com
+ Truise
+ 99
+
+
+
+
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/data/container/simple.services.xml b/Implab.ServiceHost.Test/data/container/simple.services.xml
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/data/container/simple.services.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+ 1
+ 2
+ 3
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+ 3
+
+
+
+
+ 1
+
+
+
+
+ 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/src/Mock/Baz.cs b/Implab.ServiceHost.Test/src/Mock/Baz.cs
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/src/Mock/Baz.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+
+namespace Implab.ServiceHost.Test.Mock {
+ public class Baz {
+
+ public Guid Id {
+ get; set;
+ }
+
+ public int[] Numbers {
+ get; set;
+ }
+
+ public Nut[] Nuts {
+ get; set;
+ }
+
+ public class Nut {
+ public int Size { get; set; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/src/Mock/BazFactory.cs b/Implab.ServiceHost.Test/src/Mock/BazFactory.cs
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/src/Mock/BazFactory.cs
@@ -0,0 +1,17 @@
+using System;
+using Implab.Components;
+
+namespace Implab.ServiceHost.Test.Mock {
+ public class BazFactory : IFactory {
+
+ public Func IdGenerator {
+ get; set;
+ }
+
+ Baz IFactory.Create() {
+ return new Baz {
+ Id = IdGenerator?.Invoke() ?? Guid.Empty
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/src/Mock/BoxFactory`1.cs b/Implab.ServiceHost.Test/src/Mock/BoxFactory`1.cs
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/src/Mock/BoxFactory`1.cs
@@ -0,0 +1,13 @@
+using System;
+using Implab.Components;
+
+namespace Implab.ServiceHost.Test.Mock {
+ public class BoxFactory : IFactory> {
+ public IBox Create() {
+ return new Box {
+ Name = "Creepy sugar"
+ };
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/src/Mock/Box`1.cs b/Implab.ServiceHost.Test/src/Mock/Box`1.cs
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/src/Mock/Box`1.cs
@@ -0,0 +1,13 @@
+namespace Implab.ServiceHost.Test.Mock {
+ public class Box : IBox {
+
+ public string Name { get; set; }
+
+ public T Value { get; set; }
+
+ public T GetBoxValue() {
+ return Value;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/src/Mock/GuidFactory.cs b/Implab.ServiceHost.Test/src/Mock/GuidFactory.cs
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/src/Mock/GuidFactory.cs
@@ -0,0 +1,10 @@
+using System;
+using Implab.Components;
+
+namespace Implab.ServiceHost.Test.Mock {
+ public class GuidFactory : IFactory {
+ public Guid Create() {
+ return Guid.NewGuid();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/src/Mock/IBox.cs b/Implab.ServiceHost.Test/src/Mock/IBox.cs
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/src/Mock/IBox.cs
@@ -0,0 +1,6 @@
+namespace Implab.ServiceHost.Test.Mock {
+ public interface IBox {
+ string Name { get; }
+ T GetBoxValue();
+ }
+}
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/src/Mock/Person.cs b/Implab.ServiceHost.Test/src/Mock/Person.cs
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/src/Mock/Person.cs
@@ -0,0 +1,20 @@
+using System.Xml.Serialization;
+
+namespace Implab.ServiceHost.Test.Mock
+{
+ [XmlRoot(Namespace="urn:implab:test:model")]
+ public class Person {
+ public string FirstName { get; set; }
+
+ public string LastName { get; set; }
+
+ public int Age { get; set; }
+
+ [XmlIgnore]
+ public bool AgeSpecified { get; set; }
+
+
+ [XmlElement("Tag")]
+ public string[] Tags { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/src/Mock/SetOfBoxes`1.cs b/Implab.ServiceHost.Test/src/Mock/SetOfBoxes`1.cs
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/src/Mock/SetOfBoxes`1.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Linq;
+
+namespace Implab.ServiceHost.Test.Mock {
+ public class SetOfBoxes {
+
+ public Guid Uuid { get; set; }
+
+ public IBox[] Boxes {
+ get; set;
+ }
+
+ public void BoxValues(T[] items) {
+ if (items == null || items.Length == 0)
+ return;
+
+ Boxes = items.Select(x => new Box {Value = x}).ToArray();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Implab.ServiceHost.Test/src/UnityConfigTest.cs b/Implab.ServiceHost.Test/src/UnityConfigTest.cs
new file mode 100644
--- /dev/null
+++ b/Implab.ServiceHost.Test/src/UnityConfigTest.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Implab.Components;
+using Implab.Diagnostics;
+using Implab.ServiceHost.Test.Mock;
+using Implab.ServiceHost.Unity;
+using Unity;
+using Xunit;
+
+namespace Implab.Test {
+
+ public class UnityConfigTest {
+
+ [Fact]
+ public void CreateContainer() {
+ var container = new UnityContainer();
+
+ container.LoadXmlConfiguration(Path.Combine("data","container","empty.xml"));
+ }
+
+ [Fact]
+ public void SimpleServicesContainer() {
+ var container = new UnityContainer();
+
+ container.LoadXmlConfiguration(Path.Combine("data","container","simple.services.xml"));
+
+ // named service registration
+ var baz1 = container.Resolve("Baz1");
+
+ Assert.Equal(new [] {1,2,3}, baz1.Numbers);
+
+ // default service registration
+ var baz = container.Resolve();
+
+ Assert.Equal(new [] {2,5,5,1,3}, baz.Nuts.Select(x => x.Size));
+
+ // ServiceFactory registered without a name
+ // explicitly provides 'Baz2' service
+ var baz2 = container.Resolve("Baz2");
+
+ // ServiceFactory registered with name 'Baz3'
+ // provides by default the service registration with same name
+ var baz3 = container.Resolve("Baz3");
+
+ // This factory uses GuidGenerator registration
+ // to generate a new id value
+ Assert.NotEqual(Guid.Empty, baz3.Id);
+ }
+
+ [Fact]
+ public void GenericServicesContainer() {
+ var container = new UnityContainer();
+ container.LoadXmlConfiguration(Path.Combine("data","container","generic.services.xml"));
+
+ // resolve using a generig interface mapping
+ var box = container.Resolve>();
+
+ Assert.NotNull(box.GetBoxValue());
+
+ // registered generic defines dependency of type {T}
+ // in this case it should resolve to the default Nut with Size=2
+ Assert.Equal(box.GetBoxValue().Size,2);
+
+ // generic factory which provides generic services
+ var box2 = container.Resolve>();
+
+ var set1 = container.Resolve>();
+
+ Assert.Equal(new int?[] {null,2,1}, set1.Boxes?.Select(x => x.GetBoxValue()?.Size));
+ }
+
+ [Fact]
+ public void SerializedInstances() {
+ var container = new UnityContainer();
+ container.LoadXmlConfiguration(Path.Combine("data","container","serialized.instances.xml"));
+
+ var p1 = container.Resolve("p1");
+ var p2 = container.Resolve("p2");
+
+ Assert.Equal("Com", p1.FirstName);
+ Assert.True(p1.AgeSpecified);
+ Assert.Equal(99, p1.Age);
+
+ Assert.Equal("Javolta", p2.LastName);
+ Assert.True(p2.AgeSpecified);
+ Assert.Equal(88, p2.Age);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Implab.ServiceHost/Unity/AbstractContainerItem.cs b/Implab.ServiceHost/src/Unity/AbstractContainerItem.cs
rename from Implab.ServiceHost/Unity/AbstractContainerItem.cs
rename to Implab.ServiceHost/src/Unity/AbstractContainerItem.cs
diff --git a/Implab.ServiceHost/Unity/AbstractInjectionParameter.cs b/Implab.ServiceHost/src/Unity/AbstractInjectionParameter.cs
rename from Implab.ServiceHost/Unity/AbstractInjectionParameter.cs
rename to Implab.ServiceHost/src/Unity/AbstractInjectionParameter.cs
diff --git a/Implab.ServiceHost/Unity/AbstractMemberInjection.cs b/Implab.ServiceHost/src/Unity/AbstractMemberInjection.cs
rename from Implab.ServiceHost/Unity/AbstractMemberInjection.cs
rename to Implab.ServiceHost/src/Unity/AbstractMemberInjection.cs
diff --git a/Implab.ServiceHost/Unity/AbstractRegistration.cs b/Implab.ServiceHost/src/Unity/AbstractRegistration.cs
rename from Implab.ServiceHost/Unity/AbstractRegistration.cs
rename to Implab.ServiceHost/src/Unity/AbstractRegistration.cs
diff --git a/Implab.ServiceHost/Unity/ArrayParameterElement.cs b/Implab.ServiceHost/src/Unity/ArrayParameterElement.cs
rename from Implab.ServiceHost/Unity/ArrayParameterElement.cs
rename to Implab.ServiceHost/src/Unity/ArrayParameterElement.cs
diff --git a/Implab.ServiceHost/Unity/ArrayTypeReference.cs b/Implab.ServiceHost/src/Unity/ArrayTypeReference.cs
rename from Implab.ServiceHost/Unity/ArrayTypeReference.cs
rename to Implab.ServiceHost/src/Unity/ArrayTypeReference.cs
diff --git a/Implab.ServiceHost/Unity/AssemblyElement.cs b/Implab.ServiceHost/src/Unity/AssemblyElement.cs
rename from Implab.ServiceHost/Unity/AssemblyElement.cs
rename to Implab.ServiceHost/src/Unity/AssemblyElement.cs
diff --git a/Implab.ServiceHost/Unity/ConstructorInjectionElement.cs b/Implab.ServiceHost/src/Unity/ConstructorInjectionElement.cs
rename from Implab.ServiceHost/Unity/ConstructorInjectionElement.cs
rename to Implab.ServiceHost/src/Unity/ConstructorInjectionElement.cs
diff --git a/Implab.ServiceHost/Unity/ContainerBuilder.cs b/Implab.ServiceHost/src/Unity/ContainerBuilder.cs
rename from Implab.ServiceHost/Unity/ContainerBuilder.cs
rename to Implab.ServiceHost/src/Unity/ContainerBuilder.cs
--- a/Implab.ServiceHost/Unity/ContainerBuilder.cs
+++ b/Implab.ServiceHost/src/Unity/ContainerBuilder.cs
@@ -50,7 +50,8 @@ namespace Implab.ServiceHost.Unity {
var builder = new TypeRegistrationBuilder(
m_resolver,
registrationType,
- implementationType
+ implementationType,
+ this
);
builder.Lifetime = registration.GetLifetime(this);
@@ -76,7 +77,8 @@ namespace Implab.ServiceHost.Unity {
var builder = new InstanceRegistrationBuilder (
m_resolver,
- registrationType
+ registrationType,
+ this
);
builder.Lifetime = registration.GetLifetime(this);
@@ -123,6 +125,15 @@ namespace Implab.ServiceHost.Unity {
}
///
+ /// Resolves a path ralatively to the current container configuration location.
+ ///
+ /// A path yto resolve
+ /// Resolved Uri fot the specified location
+ public Uri MakeLocationUri(string location) {
+ return m_location != null ? new Uri(m_location, location) : new Uri(location);
+ }
+
+ ///
/// Loads a configuration from the specified local file.
///
/// The path to the configuration file.
diff --git a/Implab.ServiceHost/Unity/ContainerConfigurationSchema.cs b/Implab.ServiceHost/src/Unity/ContainerConfigurationSchema.cs
rename from Implab.ServiceHost/Unity/ContainerConfigurationSchema.cs
rename to Implab.ServiceHost/src/Unity/ContainerConfigurationSchema.cs
diff --git a/Implab.ServiceHost/Unity/ContainerElement.cs b/Implab.ServiceHost/src/Unity/ContainerElement.cs
rename from Implab.ServiceHost/Unity/ContainerElement.cs
rename to Implab.ServiceHost/src/Unity/ContainerElement.cs
diff --git a/Implab.ServiceHost/Unity/ContainerLifetimeElement.cs b/Implab.ServiceHost/src/Unity/ContainerLifetimeElement.cs
rename from Implab.ServiceHost/Unity/ContainerLifetimeElement.cs
rename to Implab.ServiceHost/src/Unity/ContainerLifetimeElement.cs
diff --git a/Implab.ServiceHost/Unity/ContextLifetimeElement.cs b/Implab.ServiceHost/src/Unity/ContextLifetimeElement.cs
rename from Implab.ServiceHost/Unity/ContextLifetimeElement.cs
rename to Implab.ServiceHost/src/Unity/ContextLifetimeElement.cs
diff --git a/Implab.ServiceHost/Unity/DefaultParameterElement.cs b/Implab.ServiceHost/src/Unity/DefaultParameterElement.cs
rename from Implab.ServiceHost/Unity/DefaultParameterElement.cs
rename to Implab.ServiceHost/src/Unity/DefaultParameterElement.cs
diff --git a/Implab.ServiceHost/Unity/DependencyParameterElement.cs b/Implab.ServiceHost/src/Unity/DependencyParameterElement.cs
rename from Implab.ServiceHost/Unity/DependencyParameterElement.cs
rename to Implab.ServiceHost/src/Unity/DependencyParameterElement.cs
diff --git a/Implab.ServiceHost/Unity/FactoryActivator.cs b/Implab.ServiceHost/src/Unity/FactoryActivator.cs
rename from Implab.ServiceHost/Unity/FactoryActivator.cs
rename to Implab.ServiceHost/src/Unity/FactoryActivator.cs
--- a/Implab.ServiceHost/Unity/FactoryActivator.cs
+++ b/Implab.ServiceHost/src/Unity/FactoryActivator.cs
@@ -27,12 +27,23 @@ namespace Implab.ServiceHost.Unity {
public IEnumerable MemberInjections {
get {
- yield return new FactoryInjector {
- Factory = (InjectionFactory)GetType()
- .GetMethod(nameof(CreateInjectionFactory), BindingFlags.Static | BindingFlags.NonPublic)
- .MakeGenericMethod(FactoryType, RegistrationType)
- .Invoke(null, new [] { FactoryName })
- };
+ if (RegistrationType == null)
+ throw new Exception($"RegistrationType must be specified");
+ if (!typeof(IFactory<>).MakeGenericType(RegistrationType).IsAssignableFrom(FactoryType))
+ throw new Exception($"The factory {FactoryType} can't be used to create {RegistrationType} instances");
+
+ if (FactoryType.ContainsGenericParameters) {
+ yield return new FactoryInjector {
+ Factory = CreateDynamicInjectionFactory(FactoryName)
+ };
+ } else {
+ yield return new FactoryInjector {
+ Factory = (InjectionFactory)GetType()
+ .GetMethod(nameof(CreateInjectionFactory), BindingFlags.Static | BindingFlags.NonPublic)
+ .MakeGenericMethod(FactoryType, RegistrationType)
+ .Invoke(null, new [] { FactoryName })
+ };
+ }
}
}
@@ -59,5 +70,9 @@ namespace Implab.ServiceHost.Unity {
return new InjectionFactory(c => c.Resolve(dependencyName).Create());
}
+
+ static InjectionFactory CreateDynamicInjectionFactory(string dependencyName) {
+ return new InjectionFactory((c,t,name) => ((IFactory