diff --git a/AppBoot/iQuarc.AppBoot.UnitTests/BootstrapperTests.RegisterBehaviors.cs b/AppBoot/iQuarc.AppBoot.UnitTests/BootstrapperTests.RegisterBehaviors.cs new file mode 100644 index 0000000..78fcfbc --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.UnitTests/BootstrapperTests.RegisterBehaviors.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using iQuarc.SystemEx; +using Microsoft.Practices.ServiceLocation; +using Moq; +using Xunit; + +namespace iQuarc.AppBoot.UnitTests +{ + public class BootstrapperTestsWithMoreRegisterBehaviors + { + [Fact] + public void Run_TypesOnSameInterfaceRegisteredByDifferentBehaviors_LastRegisteredBehaviorOverwrites() + { + DummyAssembly assembly = new DummyAssembly(typeof (Implementation), typeof (ProxyImpl)); + DummyContainer dummyContainer = new DummyContainer(); + Bootstrapper bootstrapper = new Bootstrapper(new[] {assembly}, dummyContainer); + + IRegistrationBehavior proxyBeh = new DummyBehavior("Proxy"); + IRegistrationBehavior serviceBeh = new DummyBehavior("Service"); + bootstrapper.AddRegistrationBehavior(proxyBeh); + bootstrapper.AddRegistrationBehavior(serviceBeh); + + bootstrapper.Run(); + + ServiceInfo resolved = dummyContainer.GetRegistration(typeof (IService)); + Assert.Equal(typeof (Implementation), resolved.To); + } + + private interface IService + { + } + + [Service("Service")] //using contract name as annotation + private class Implementation : IService + { + } + + [Service("Proxy")] //using contract name as annotation + private class ProxyImpl : IService + { + } + + private class DummyBehavior : IRegistrationBehavior + { + private readonly string annotation; + + public DummyBehavior(string annotation) + { + this.annotation = annotation; + } + + public IEnumerable GetServicesFrom(Type type) + { + ServiceAttribute atr = type.GetAttribute(); + if (atr.ContractName == annotation) + return new[] {new ServiceInfo(typeof (IService), type, Lifetime.Instance)}; + + return new ServiceInfo[] {}; + } + } + + + private class DummyAssembly : Assembly + { + private readonly Type[] types; + + public DummyAssembly(params Type[] types) + { + this.types = types; + } + + public override Type[] GetTypes() + { + return types; + } + } + + private class DummyContainer : IDependencyContainer + { + private readonly Dictionary dic = new Dictionary(); + + public DummyContainer() + { + AsServiceLocator = GetFakeServiceLocator(); + } + + public IServiceLocator AsServiceLocator { get; private set; } + + public void RegisterService(ServiceInfo service) + { + dic[service.From] = service; + } + + public ServiceInfo GetRegistration(Type from) + { + return dic[from]; + } + + public void RegisterInstance(T instance) + { + } + + private static IServiceLocator GetFakeServiceLocator() + { + IModule[] modules = {}; + + Mock fakeServiceLocator = new Mock(); + IServiceLocator serviceLocator = fakeServiceLocator.Object; + + fakeServiceLocator.Setup(s => s.GetInstance()) + .Returns(new Application(modules)); + fakeServiceLocator.Setup(s => s.GetInstance()) + .Returns(serviceLocator); + + return serviceLocator; + } + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.UnitTests/BootstrapperTests.cs b/AppBoot/iQuarc.AppBoot.UnitTests/BootstrapperTests.cs new file mode 100644 index 0000000..cf7d887 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.UnitTests/BootstrapperTests.cs @@ -0,0 +1,111 @@ +using System; +using System.Reflection; +using Microsoft.Practices.ServiceLocation; +using Moq; +using Xunit; + +namespace iQuarc.AppBoot.UnitTests +{ + public class BootstrapperTests + { + [Fact] + public void Run_AfterCalled_ServiceLocatorIsAvailableAsStaticSingleton() + { + IServiceLocator serviceLocatorStub = GetFakeServiceLocator(); + Mock containerStub = GetFakeContainer(serviceLocatorStub); + + Bootstrapper bootstrapper = GetTarget(containerStub); + + bootstrapper.Run(); + + IServiceLocator staticLocator = ServiceLocator.Current; + Assert.Same(serviceLocatorStub, staticLocator); + } + + [Fact] + public void Run_AfterCalled_ServiceLocatorRegisteredInTheContainer() + { + IServiceLocator serviceLocatorStub = GetFakeServiceLocator(); + Mock containerMock = GetFakeContainer(serviceLocatorStub); + + Bootstrapper boostrapper = GetTarget(containerMock); + + boostrapper.Run(); + + containerMock.Verify(c => c.RegisterInstance(serviceLocatorStub), Times.Once); + } + + [Fact] + public void Run_RegistrationBehaviorReturnsOneService_TypeRegistered() + { + Type testType = typeof (TestType); + ServiceInfo testSi = new ServiceInfo(testType, testType, "test contract", Lifetime.Instance); + IRegistrationBehavior regBehaviorStub = GetRegBehaviorStub(testSi); + + Mock containerMock = GetFakeContainer(); + Bootstrapper bootstrapper = GetTargetWithAssembly(containerMock); + bootstrapper.AddRegistrationBehavior(regBehaviorStub); + + bootstrapper.Run(); + + containerMock.Verify(c => c.RegisterService(testSi), Times.AtLeastOnce); + } + + private Bootstrapper GetTargetWithAssemblyAndFakeContainer() + { + Assembly[] assemblies = {typeof (BootstrapperTests).Assembly}; + IDependencyContainer container = GetFakeContainer().Object; + + return new Bootstrapper(assemblies, container); + } + + private static Bootstrapper GetTarget(Mock fakeContainer) + { + return new Bootstrapper(new Assembly[] {}, fakeContainer.Object); + } + + private Bootstrapper GetTargetWithAssembly(Mock container) + { + Assembly[] assemblies = {typeof (BootstrapperTests).Assembly}; + return new Bootstrapper(assemblies, container.Object); + } + + private static IServiceLocator GetFakeServiceLocator() + { + IModule[] modules = {}; + + Mock fakeServiceLocator = new Mock(); + IServiceLocator serviceLocator = fakeServiceLocator.Object; + + fakeServiceLocator.Setup(s => s.GetInstance()) + .Returns(new Application(modules)); + fakeServiceLocator.Setup(s => s.GetInstance()) + .Returns(serviceLocator); + + return serviceLocator; + } + + private static Mock GetFakeContainer(IServiceLocator serviceLocatorStub = null) + { + IServiceLocator serviceLocator = serviceLocatorStub ?? GetFakeServiceLocator(); + + Mock containerStub = new Mock(); + containerStub.Setup(c => c.AsServiceLocator).Returns(serviceLocator); + return containerStub; + } + + private static IRegistrationBehavior GetRegBehaviorStub(ServiceInfo si) + { + Mock regBehaviorStub = new Mock(); + regBehaviorStub.Setup(r => r.GetServicesFrom(It.IsAny())) + .Returns(new[] {si}); + + return regBehaviorStub.Object; + } + + + private class TestType + { + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.UnitTests/Properties/AssemblyInfo.cs b/AppBoot/iQuarc.AppBoot.UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2b9c7b5 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +[assembly: AssemblyTitle("iQuarc.AppBoot.UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("iQuarc")] +[assembly: AssemblyProduct("iQuarc.AppBoot.UnitTests")] +[assembly: AssemblyCopyright("Copyright © iQuarc 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM + +[assembly: Guid("ffbfe0fa-8497-492d-a151-486243e877f0")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.UnitTests/RegistrationsCatalogTests.cs b/AppBoot/iQuarc.AppBoot.UnitTests/RegistrationsCatalogTests.cs new file mode 100644 index 0000000..18d8943 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.UnitTests/RegistrationsCatalogTests.cs @@ -0,0 +1,252 @@ +using System.Linq; +using iQuarc.xUnitEx; +using Xunit; + +namespace iQuarc.AppBoot.UnitTests +{ + + /// + /// Test cases take all relevant combinations between: + /// FromType Contract Priority --> Expected Result + /// Different Same Same --> All + /// Same Same Same --> One + /// . . . + /// + /// Registration catalog can contain: + /// 1. For one FromType it has registrations given by ONLY one behavior, and that behavior is the one with the highest priority + /// 2. Registrations with same FromType and same Contract are ignored or overwritten + /// + /// + public class RegistrationsCatalogTests + { + private RegistrationsCatalog catalog; + + public RegistrationsCatalogTests() + { + catalog = new RegistrationsCatalog(); + } + + [Fact] + public void Add__DifferentFrom_NullContract_SamePriority__AllAdded() + { + ServiceInfo si1 = GetSi(null); + ServiceInfo si2 = GetSi(null); + ServiceInfo si3 = GetSi(null); + + catalog.Add(si1, 1); + catalog.Add(si2, 1); + catalog.Add(si3, 1); + + AssertEx.AreEquivalent(catalog, si1, si2, si3); + } + + [Fact] + public void Add__SameFrom_NullContract_HigherPriorityAddedLater__HigherPriorityOverwrites() + { + ServiceInfo si1 = GetSi(null); + ServiceInfo si2 = GetSi(null); + ServiceInfo si3 = GetSi(null); + + catalog.Add(si1, 2); + catalog.Add(si2, 3); + catalog.Add(si3, 5); + + AssertCatalogContainsOnly(si3); + } + + [Fact] + public void Add__SameFrom_NullContract_LowerPriorityAddedLater__HigherPriorityIsNotOverwritten() + { + ServiceInfo si1 = GetSi(null); + ServiceInfo si2 = GetSi(null); + ServiceInfo si3 = GetSi(null); + + catalog.Add(si2, 5); + catalog.Add(si1, 2); + catalog.Add(si3, 1); + + AssertCatalogContainsOnly(si2); + } + + [Fact] + public void Add__DifferentFrom_SameContract_SamePriority__AllAdded() + { + ServiceInfo si1 = GetSi("Contract"); + ServiceInfo si2 = GetSi("Contract"); + ServiceInfo si3 = GetSi("Contract"); + + catalog.Add(si1, 1); + catalog.Add(si2, 1); + catalog.Add(si3, 1); + + AssertEx.AreEquivalent(catalog, si1, si2, si3); + } + + [Fact] + public void Add__SameFrom_SameContract_SamePriority__OnlyOneAdded() + { + ServiceInfo si1 = GetSi("Contract"); + ServiceInfo si2 = GetSi("Contract"); + ServiceInfo si3 = GetSi("Contract"); + + catalog.Add(si1, 1); + catalog.Add(si2, 1); + catalog.Add(si3, 1); + + Assert.Equal(1, catalog.Count()); + } + + [Fact] + public void Add_SameFrom_DifferentContract_SamePriority__AllAdded() + { + ServiceInfo si1 = GetSi("Contract1"); + ServiceInfo si2 = GetSi("Contract2"); + ServiceInfo si3 = GetSi("Contract3"); + + catalog.Add(si1, 1); + catalog.Add(si2, 1); + catalog.Add(si3, 1); + + AssertEx.AreEquivalent(catalog, si1, si2, si3); + } + + [Fact] + public void Add__SameFrom_DifferentContract_HighPriorityAddedLast__LowerPrioritiesOverwritten() + { + ServiceInfo si1 = GetSi("Contract1"); + ServiceInfo si2 = GetSi("Contract2"); + ServiceInfo si3 = GetSi("Contract3"); + + catalog.Add(si1, 1); + catalog.Add(si2, 1); + catalog.Add(si3, 3); + + AssertCatalogContainsOnly(si3); + } + + [Fact] + public void Add__SameFrom_SameContractForDifferentPrio_HighPriorityAddedLast__LowerPrioritiesOverwritten() + { + ServiceInfo si1 = GetSi("Contract1"); + ServiceInfo si2 = GetSi("Contract2"); + ServiceInfo si3 = GetSi("Contract1"); + + catalog.Add(si1, 1); + catalog.Add(si2, 1); + catalog.Add(si3, 3); + + AssertCatalogContainsOnly(si3); + } + + [Fact] + public void Add__SameFrom_DifferentContract_HighPriorityAddedFirst__HighPrioritiesRemain() + { + ServiceInfo si1 = GetSi("Contract1"); + ServiceInfo si2 = GetSi("Contract2"); + ServiceInfo si3 = GetSi("Contract3"); + + catalog.Add(si1, 3); + catalog.Add(si2, 3); + catalog.Add(si3, 1); + + AssertEx.AreEquivalent(catalog, si1, si2); + } + + [Fact] + public void Add__SameFrom_SameContract_HighPriorityAddedFirst__HighPrioritiesRemain() + { + ServiceInfo si1 = GetSi("Contract"); + ServiceInfo si2 = GetSi("Contract2"); + ServiceInfo si3 = GetSi("Contract"); + + catalog.Add(si1, 3); + catalog.Add(si2, 3); + catalog.Add(si3, 1); + + AssertEx.AreEquivalent(catalog, si1, si2); + } + + [Fact] + public void Add__SameType_NullAndContracts_SamePriority__AllAdded() + { + ServiceInfo si1 = GetSi(null); + ServiceInfo si2 = GetSi("Contract1"); + ServiceInfo si3 = GetSi("Contract2"); + + catalog.Add(si1, 1); + catalog.Add(si2, 1); + catalog.Add(si3, 1); + + AssertEx.AreEquivalent(catalog, si1, si2, si3); + } + + [Fact] + public void Add__SameFrom_NullAndContracts_HighPriorityAddedLast__LowerPrioritiesOverwritten() + { + ServiceInfo si1 = GetSi(null); + ServiceInfo si2 = GetSi("Contract2"); + ServiceInfo si3 = GetSi("Contract3"); + + catalog.Add(si1, 1); + catalog.Add(si2, 1); + catalog.Add(si3, 3); + + AssertCatalogContainsOnly(si3); + } + + [Fact] + public void Add__SameFrom_NullAndContract_HighPriorityAddedFirst__HighPrioritiesRemain() + { + ServiceInfo si1 = GetSi(null); + ServiceInfo si2 = GetSi("Contract2"); + ServiceInfo si3 = GetSi("Contract3"); + + catalog.Add(si1, 3); + catalog.Add(si2, 3); + catalog.Add(si3, 1); + + AssertEx.AreEquivalent(catalog, si1, si2); + } + + [Fact] + public void Add__SameFrom_SameNullAndContract_HighPriorityAddedLast__LowerPrioritiesOverwritten() + { + ServiceInfo si1 = GetSi(null); + ServiceInfo si2 = GetSi("Contract"); + ServiceInfo si3 = GetSi(null); + + catalog.Add(si1, 1); + catalog.Add(si2, 1); + catalog.Add(si3, 3); + + AssertCatalogContainsOnly(si3); + } + + [Fact] + public void Add__SameFrom_SameNullAndContract_HighPriorityAddedFirst__HighPrioritiesRemain() + { + ServiceInfo si1 = GetSi(null); + ServiceInfo si2 = GetSi("Contract"); + ServiceInfo si3 = GetSi(null); + + catalog.Add(si1, 3); + catalog.Add(si2, 3); + catalog.Add(si3, 1); + + AssertEx.AreEquivalent(catalog, si1, si2); + } + + private static ServiceInfo GetSi(string contractName) + { + return new ServiceInfo(typeof (TFrom), typeof (int), contractName, Lifetime.Instance); + } + + private void AssertCatalogContainsOnly(ServiceInfo si) + { + ServiceInfo[] catalogAsArray = catalog.ToArray(); + Assert.True(catalogAsArray.Length == 1, "Catalog contains zero or more registrations, but one expected"); + Assert.Same(si, catalogAsArray[0]); + + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.UnitTests/ServiceRegistrationBehaviorTests.cs b/AppBoot/iQuarc.AppBoot.UnitTests/ServiceRegistrationBehaviorTests.cs new file mode 100644 index 0000000..7066107 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.UnitTests/ServiceRegistrationBehaviorTests.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using iQuarc.xUnitEx; +using Moq; +using Xunit; + +namespace iQuarc.AppBoot.UnitTests +{ + public class ServiceRegistrationBehaviorTests + { + [Fact] + public void GetServicesFrom_NotDecoratedWithServiceAttribute_NoServicesReturned() + { + ServiceRegistrationBehavior behavior = new ServiceRegistrationBehavior(); + + IEnumerable services = behavior.GetServicesFrom(typeof (MyNonService)); + + Assert.Empty(services); + } + + [Fact] + public void GetServicesFrom_DecoratedWithAttribute_ServiceInfoWithAttributeParameters() + { + ServiceRegistrationBehavior behavior = new ServiceRegistrationBehavior(); + + IEnumerable services = behavior.GetServicesFrom(typeof (MyService)); + + ServiceInfo expected = new ServiceInfo(typeof (IMyService), typeof (MyService), + "SomeContractName", Lifetime.Application); + AssertEx.AreEquivalent(services, ServiceInfoEquals, expected); + } + + [Fact] + public void GetServicesFrom_DecoratedWithTwoAttributes_TwoServiceInfoReturned() + { + ServiceInfo expected1 = new ServiceInfo(typeof (IMyService), typeof (MyDoubleService), "SomeContractName", + Lifetime.Application); + ServiceInfo expected2 = new ServiceInfo(typeof (IMyService), typeof (MyDoubleService), + "SomeOtherContractName", Lifetime.Instance); + + ServiceRegistrationBehavior behavior = new ServiceRegistrationBehavior(); + + IEnumerable services = behavior.GetServicesFrom(typeof (MyDoubleService)); + + AssertEx.AreEquivalent(services, ServiceInfoEquals, expected1, expected2); + } + + [Fact] + public void GetServicesFrom_ServiceAttributeDoesNotHaveExportType_DecoratedTypeUsedAsFromType() + { + ServiceAttribute service = new ServiceAttribute(); + Type fakeType = GetFakeType(service); + + ServiceRegistrationBehavior behavior = GetTarget(); + + IEnumerable services = behavior.GetServicesFrom(fakeType); + + ServiceInfo[] expected = {new ServiceInfo(fakeType, fakeType, Lifetime.Instance)}; + AssertEx.AreEquivalent(services, ServiceInfoEquals, expected); + } + + private ServiceRegistrationBehavior GetTarget() + { + return new ServiceRegistrationBehavior(); + } + + private Type GetFakeType(ServiceAttribute serviceAttribute) + { + Mock type = new Mock(); + type.Setup(t => t.GetCustomAttributes(It.IsAny(), It.IsAny())) + .Returns(new object[] {serviceAttribute}); + + return type.Object; + } + + private bool ServiceInfoEquals(ServiceInfo s1, ServiceInfo s2) + { + return s1.From == s2.From && + s1.To == s2.To && + s1.ContractName == s2.ContractName && + s1.InstanceLifetime == s2.InstanceLifetime; + } + + [Service("SomeContractName", typeof (IMyService), Lifetime.Application)] + private class MyService : IMyService + { + } + + internal interface IMyService + { + } + + private class MyNonService + { + } + + [Service("SomeContractName", typeof (IMyService), Lifetime.Application)] + [Service("SomeOtherContractName", typeof (IMyService), Lifetime.Instance)] + private class MyDoubleService : IMyService + { + } + } + + public static class Extensions + { + public static T[] AsArray(this T o) + { + return new[] {o}; + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.UnitTests/iQuarc.AppBoot.UnitTests.csproj b/AppBoot/iQuarc.AppBoot.UnitTests/iQuarc.AppBoot.UnitTests.csproj new file mode 100644 index 0000000..cf2bdc5 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.UnitTests/iQuarc.AppBoot.UnitTests.csproj @@ -0,0 +1,107 @@ + + + + Debug + AnyCPU + {FFA3AB36-9635-4639-B1CB-3C915B5D97AF} + Library + Properties + iQuarc.AppBoot.UnitTests + iQuarc.AppBoot.UnitTests + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\packages\iQuarc.SystemEx.1.0.0.0\lib\net40\iQuarc.SystemEx.dll + + + ..\packages\iQuarc.xUnitEx.1.0.0.0\lib\net35\iQuarc.xUnitEx.dll + + + ..\packages\CommonServiceLocator.1.0\lib\NET35\Microsoft.Practices.ServiceLocation.dll + + + ..\packages\Moq.4.2.1312.1622\lib\net40\Moq.dll + + + + ..\packages\xunit.1.9.2\lib\net20\xunit.dll + + + + + + + + + + + + + + + + + + + + + + + {F1412A2B-3E4D-43BC-8A0A-8FC72703AA2C} + iQuarc.AppBoot + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.UnitTests/packages.config b/AppBoot/iQuarc.AppBoot.UnitTests/packages.config new file mode 100644 index 0000000..0819a72 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.UnitTests/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/ConstructorInjectionTests.cs b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/ConstructorInjectionTests.cs new file mode 100644 index 0000000..2f67d98 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/ConstructorInjectionTests.cs @@ -0,0 +1,96 @@ +using Microsoft.Practices.Unity; +using Xunit; + +namespace iQuarc.AppBoot.Unity.ExplorationTests +{ + public class ConstructorInjectionTests + { + [Fact] + public void ConstructorInjectionGivesSameInstancesAndServiceLocatorInjectionGivesNewInstance() + { + IUnityContainer container = new UnityContainer() + .RegisterType(new PerResolveLifetimeManager()) + .RegisterType(new PerResolveLifetimeManager()) + .RegisterType(new PerResolveLifetimeManager()) + .RegisterType(new PerResolveLifetimeManager()) + .RegisterType(new PerResolveLifetimeManager()); + + Controller controller = (Controller) container.Resolve(); + + Assert.Same(controller.S1.Repository, controller.S2.Repository); + Assert.NotSame(controller.S1.Repository, controller.S3.Repository); + } + + private interface IController + { + } + + private class Controller : IController + { + public IService1 S1 { get; set; } + public IService2 S2 { get; set; } + public IService3 S3 { get; set; } + + public Controller(IService1 s1, IService2 s2, IService3 s3) + { + S1 = s1; + S2 = s2; + S3 = s3; + } + } + + internal interface IService2 + { + IRepository Repository { get; } + } + + private class Service2 : IService2 + { + public IRepository Repository { get; set; } + + public Service2(IRepository repository) + { + Repository = repository; + } + } + + internal interface IService1 + { + IRepository Repository { get; } + } + + private class Service1 : IService1 + { + public IRepository Repository { get; set; } + + public Service1(IRepository repository) + { + Repository = repository; + } + } + + internal interface IService3 + { + IRepository Repository { get; } + } + + private class Service3 : IService3 + { + public IRepository Repository { get; set; } + + public Service3(IUnityContainer container) + { + //serviceLocator.GetInstance() + Repository = container.Resolve(); + } + } + + internal interface IRepository + { + } + + private class Repository : IRepository + { + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/PerResolveLifetimeManagerTests.cs b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/PerResolveLifetimeManagerTests.cs new file mode 100644 index 0000000..23577eb --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/PerResolveLifetimeManagerTests.cs @@ -0,0 +1,59 @@ +using Microsoft.Practices.Unity; +using Xunit; + +namespace iQuarc.AppBoot.Unity.ExplorationTests +{ + public class PerResolveLifetimeManagerTests + { + [Fact] + public void PerResolveUsedForTheView_SameViewInstanceInjected() + { + IUnityContainer container = new UnityContainer() + .RegisterType(new TransientLifetimeManager()) + .RegisterType(new PerResolveLifetimeManager()); + + IView view = container.Resolve(); + IView viewOfThePresenter = ((Presenter) view.Presenter).View; + + Assert.Same(view, viewOfThePresenter); + } + + [Fact] + public void TransientUsedForPresenterAndPerResolveForTheView_DifferentInstancesForThePresenter() + { + IUnityContainer container = new UnityContainer() + .RegisterType(new TransientLifetimeManager()) + .RegisterType(new PerResolveLifetimeManager()); + + IPresenter p = container.Resolve(); + IPresenter presenterOfTheView = ((Presenter) p).View.Presenter; + + Assert.NotSame(p, presenterOfTheView); + } + + public interface IPresenter + { + } + + public class Presenter : IPresenter + { + public Presenter(IView view) + { + View = view; + } + + public IView View { get; set; } + } + + public interface IView + { + IPresenter Presenter { get; set; } + } + + public class View : IView + { + [Dependency] + public IPresenter Presenter { get; set; } + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/Properties/AssemblyInfo.cs b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b0bdd72 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +[assembly: AssemblyTitle("iQuarc.AppBoot.Unity.ExplorationTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("iQuarc")] +[assembly: AssemblyProduct("iQuarc.AppBoot.Unity.ExplorationTests")] +[assembly: AssemblyCopyright("Copyright © iQuarc 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM + +[assembly: Guid("f65e9a42-c154-444e-b43b-ab75b49c4b22")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/RegisterTests.cs b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/RegisterTests.cs new file mode 100644 index 0000000..d1c3a7a --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/RegisterTests.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using iQuarc.xUnitEx; +using Microsoft.Practices.Unity; +using Xunit; + +namespace iQuarc.AppBoot.Unity.ExplorationTests +{ + public class RegisterTests + { + private readonly InjectionMember[] emptyInjectionMembers; + + public RegisterTests() + { + emptyInjectionMembers = new InjectionMember[] {}; + } + + [Fact] + public void RegisterType_FromIsNull_RegistrationMadeWithToType() + { + UnityContainer container = new UnityContainer(); + + Type to = typeof (SomeBaseClass); + container.RegisterType(null, to, (string) null, emptyInjectionMembers); + + AssertRegistrationsContain(container, to, to, null); + } + + [Fact] + public void RegisterType_ToIsNull_ArgumentNullException() + { + UnityContainer container = new UnityContainer(); + + Type @from = typeof (SomeBaseClass); + Action act = () => container.RegisterType(@from, null, (string) null, emptyInjectionMembers); + + act.ShouldThrow(); + } + + [Fact] + public void RegisterType_FromIsAnInterfaceOfTo_RegistrationMade() + { + UnityContainer container = new UnityContainer(); + + container.RegisterType(typeof (ISomeInterface), typeof (SomeInterfaceImp), (string) null, emptyInjectionMembers); + + AssertRegistrationsContain(container, typeof (ISomeInterface), typeof (SomeInterfaceImp), null); + } + + [Fact] + public void RegisterType_ToDoesNotInheritFrom_ExceptionExpected() + { + UnityContainer container = new UnityContainer(); + + Action act = () => + container.RegisterType(typeof (SomeInterfaceImp), typeof (SomeBaseClass), "", emptyInjectionMembers); + + act.ShouldThrow(); + } + + [Fact] + public void RegisterType_FromIsNotAbstractButToInheritsIt_RegistrationIsCorrect() + { + UnityContainer container = new UnityContainer(); + + container.RegisterType(typeof (SomeBaseClass), typeof (SomeSubClass), "", emptyInjectionMembers); + + AssertRegistrationsContain(container, typeof (SomeBaseClass), typeof (SomeSubClass), null); + } + + [Fact] + public void RegisterType_TwoNamedContracts_FirstIsOverwritten() + { + + IUnityContainer container = new UnityContainer() + .RegisterType("MyName") + .RegisterType("MyName"); + + List instances = container.ResolveAll().ToList(); + Assert.Equal(1, instances.Count); + } + + private static void AssertRegistrationsContain(UnityContainer container, Type from, Type to, string name) + { + Assert.True(container.Registrations.Any(r => + r.RegisteredType == from && + r.MappedToType == to && + r.Name == name + ), + "Registrations do not contain expected type registration"); + } + + private interface ISomeInterface + { + } + + private class SomeInterfaceImp : ISomeInterface + { + } + + public class SomeInterfaceSecondImp : ISomeInterface + { + } + + private class SomeBaseClass + { + } + + private class SomeSubClass : SomeBaseClass + { + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/iQuarc.AppBoot.Unity.ExploratoryTests.csproj b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/iQuarc.AppBoot.Unity.ExploratoryTests.csproj new file mode 100644 index 0000000..249fb3a --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/iQuarc.AppBoot.Unity.ExploratoryTests.csproj @@ -0,0 +1,100 @@ + + + + Debug + AnyCPU + {2C568C19-3B63-4B8F-B281-6BC2A62EA230} + Library + Properties + iQuarc.AppBoot.Unity.ExplorationTests + iQuarc.AppBoot.Unity.ExplorationTests + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\packages\iQuarc.xUnitEx.1.0.0.0\lib\net35\iQuarc.xUnitEx.dll + + + False + ..\packages\Unity.3.0.1304.1\lib\Net45\Microsoft.Practices.Unity.dll + + + False + ..\packages\Unity.3.0.1304.1\lib\Net45\Microsoft.Practices.Unity.Configuration.dll + + + + False + ..\packages\xunit.1.9.2\lib\net20\xunit.dll + + + + + + + + + + + + + + + + + + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/packages.config b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/packages.config new file mode 100644 index 0000000..80b22f1 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.Unity.ExplorationTests/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot.sln b/AppBoot/iQuarc.AppBoot.sln new file mode 100644 index 0000000..266f034 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iQuarc.AppBoot", "iQuarc.AppBoot\iQuarc.AppBoot.csproj", "{F1412A2B-3E4D-43BC-8A0A-8FC72703AA2C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iQuarc.AppBoot.UnitTests", "iQuarc.AppBoot.UnitTests\iQuarc.AppBoot.UnitTests.csproj", "{FFA3AB36-9635-4639-B1CB-3C915B5D97AF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "iQuarc.AppBoot.Unity.ExploratoryTests", "iQuarc.AppBoot.Unity.ExplorationTests\iQuarc.AppBoot.Unity.ExploratoryTests.csproj", "{2C568C19-3B63-4B8F-B281-6BC2A62EA230}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F1412A2B-3E4D-43BC-8A0A-8FC72703AA2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1412A2B-3E4D-43BC-8A0A-8FC72703AA2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1412A2B-3E4D-43BC-8A0A-8FC72703AA2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1412A2B-3E4D-43BC-8A0A-8FC72703AA2C}.Release|Any CPU.Build.0 = Release|Any CPU + {FFA3AB36-9635-4639-B1CB-3C915B5D97AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFA3AB36-9635-4639-B1CB-3C915B5D97AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFA3AB36-9635-4639-B1CB-3C915B5D97AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFA3AB36-9635-4639-B1CB-3C915B5D97AF}.Release|Any CPU.Build.0 = Release|Any CPU + {2C568C19-3B63-4B8F-B281-6BC2A62EA230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2C568C19-3B63-4B8F-B281-6BC2A62EA230}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2C568C19-3B63-4B8F-B281-6BC2A62EA230}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2C568C19-3B63-4B8F-B281-6BC2A62EA230}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/AppBoot/iQuarc.AppBoot/Application.cd b/AppBoot/iQuarc.AppBoot/Application.cd new file mode 100644 index 0000000..3138e7a --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/Application.cd @@ -0,0 +1,42 @@ + + + + + + + + + + + + + AAAAAAAAAAAAAQAAABAAAAAQAAAAAAAAAAAAAAAAAAA= + Application.cs + + + + + + + + + + + BAAAAAAQBCAAAAAAAQAAAAgAAAAAAkAAAAgAAAAIAAI= + Bootstrapper.cs + + + + + + + AAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + IModule.cs + + + + \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/Application.cs b/AppBoot/iQuarc.AppBoot/Application.cs new file mode 100644 index 0000000..df7cf85 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/Application.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using iQuarc.SystemEx.Priority; + +namespace iQuarc.AppBoot +{ + [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "This is initialized by Unity Container")] + internal sealed class Application + { + private readonly IEnumerable modules; + + public Application(IModule[] modules) + { + this.modules = modules.OrderByPriority(); + } + + public IEnumerable Modules + { + get { return modules; } + } + + public void Initialize() + { + if (Modules != null) + { + foreach (IModule module in Modules) + module.Initialize(); + } + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/Bootstrapper.cs b/AppBoot/iQuarc.AppBoot/Bootstrapper.cs new file mode 100644 index 0000000..c35241a --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/Bootstrapper.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.Practices.ServiceLocation; + +namespace iQuarc.AppBoot +{ + /// + /// A class that starts the application and initializes it. + /// + public class Bootstrapper : IDisposable + { + private readonly IEnumerable applicationAssemblies; + private readonly IDependencyContainer container; + private readonly List behaviors = new List(); + + public Bootstrapper(IEnumerable applicationAssemblies, IDependencyContainer container) + { + this.applicationAssemblies = applicationAssemblies; + this.container = container; + } + + public IEnumerable ApplicationAssemblies + { + get { return applicationAssemblies; } + } + + public IServiceLocator ServiceLocator + { + get { return container.AsServiceLocator; } + } + + public void AddRegistrationBehavior(IRegistrationBehavior behavior) + { + behaviors.Add(behavior); + } + + public virtual void Run() + { + SetupServiceLocator(); + + RegisterServices(); + + InitApplication(); + } + + private void SetupServiceLocator() + { + IServiceLocator serviceLocator = container.AsServiceLocator; + container.RegisterInstance(serviceLocator); + Microsoft.Practices.ServiceLocation.ServiceLocator.SetLocatorProvider(serviceLocator.GetInstance); + } + + private void RegisterServices() + { + RegistrationsCatalog catalog = new RegistrationsCatalog(); + + IEnumerable types = applicationAssemblies.SelectMany(a => a.GetTypes()); + + foreach (Type type in types) + { + for (int i = 0; i < behaviors.Count; i++) + { + IRegistrationBehavior behavior = behaviors[i]; + + IEnumerable registrations = behavior.GetServicesFrom(type); + foreach (ServiceInfo reg in registrations) + catalog.Add(reg, i); + } + } + + foreach (ServiceInfo registration in catalog) + container.RegisterService(registration); + } + + private void InitApplication() + { + Application application = container.AsServiceLocator.GetInstance(); + application.Initialize(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + IDisposable disposable = container as IDisposable; + if (disposable != null) + disposable.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/Container/IRegistrationBehavior.cs b/AppBoot/iQuarc.AppBoot/Container/IRegistrationBehavior.cs new file mode 100644 index 0000000..511a731 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/Container/IRegistrationBehavior.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace iQuarc.AppBoot +{ + /// + /// Represents one of the behaviors used by the Bootstraper to register types into the Dependency Injection Container + /// + public interface IRegistrationBehavior + { + /// + /// Gets the services information that will be registered for given type + /// + IEnumerable GetServicesFrom(Type type); + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/Container/Lifetime.cs b/AppBoot/iQuarc.AppBoot/Container/Lifetime.cs new file mode 100644 index 0000000..997a893 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/Container/Lifetime.cs @@ -0,0 +1,24 @@ +namespace iQuarc.AppBoot +{ + /// + /// Specifies the lifetime of an instance of a service + /// + public enum Lifetime + { + /// + /// New instances are created each time a new object graph is created. + /// During the scope of build-up of one object graph the created instances are reused. + /// + Instance, + + /// + /// Always creates a new instance of this class when it is injected as a dependency. + /// + AlwaysNew, + + /// + /// Lives on the application as a singleton instance + /// + Application + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/Container/ServiceAttribute.cs b/AppBoot/iQuarc.AppBoot/Container/ServiceAttribute.cs new file mode 100644 index 0000000..32e17de --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/Container/ServiceAttribute.cs @@ -0,0 +1,61 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace iQuarc.AppBoot +{ + /// + /// Declares a service implementation, by decorating the class that implements it. + /// It may also specify the lifetime of the service instance by using the Lifetime enum + /// + [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "This attribute may be inherited by client applications to extend the registration behaviors")] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class ServiceAttribute : Attribute + { + public ServiceAttribute() + { + } + + public ServiceAttribute(Type exportType) + : this(null, exportType) + { + } + + public ServiceAttribute(string contractName) + : this(contractName, null) + { + } + + public ServiceAttribute(Lifetime lifetime) + : this(null, null, lifetime) + { + } + + public ServiceAttribute(string contractName, Lifetime lifetime) + : this(contractName, null, lifetime) + { + } + + public ServiceAttribute(string contractName, Type exportType) + : this(contractName, exportType, Lifetime.Instance) + { + } + + public ServiceAttribute(Type exportType, Lifetime lifetime) + : this(null, exportType, lifetime) + { + } + + public ServiceAttribute(string contractName, Type exportType, Lifetime lifetime) + { + ContractName = contractName; + ExportType = exportType; + Lifetime = lifetime; + } + + public string ContractName { get; private set; } + + public Type ExportType { get; private set; } + + public Lifetime Lifetime { get; private set; } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/Container/ServiceInfo.cs b/AppBoot/iQuarc.AppBoot/Container/ServiceInfo.cs new file mode 100644 index 0000000..d5b3208 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/Container/ServiceInfo.cs @@ -0,0 +1,31 @@ +using System; + +namespace iQuarc.AppBoot +{ + /// + /// Contains information about a service that is going to be registered into the Dependency Injection Container + /// + public class ServiceInfo + { + public ServiceInfo(Type from, Type to, Lifetime lifetime) + : this(from, to, null, lifetime) + { + } + + public ServiceInfo(Type from, Type to, string contractName, Lifetime lifetime) + { + From = from; + To = to; + ContractName = contractName; + InstanceLifetime = lifetime; + } + + public Type From { get; private set; } + + public Type To { get; private set; } + + public string ContractName { get; private set; } + + public Lifetime InstanceLifetime { get; private set; } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/Container/ServiceRegistrationBehavior.cs b/AppBoot/iQuarc.AppBoot/Container/ServiceRegistrationBehavior.cs new file mode 100644 index 0000000..6459eb7 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/Container/ServiceRegistrationBehavior.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using iQuarc.SystemEx; + +namespace iQuarc.AppBoot +{ + /// + /// Gives service information to be registered to Dependency Injection Container, based on the ServiceAttribute + /// If more service attribute decorations are on current type more ServiceInfo are returned, one for each attribute + /// + public sealed class ServiceRegistrationBehavior : IRegistrationBehavior + { + public IEnumerable GetServicesFrom(Type type) + { + IEnumerable attributes = type.GetAttributes(false); + return attributes.Select(a => + new ServiceInfo(a.ExportType ?? type, type, a.ContractName, a.Lifetime)); + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/IDependencyContainer.cs b/AppBoot/iQuarc.AppBoot/IDependencyContainer.cs new file mode 100644 index 0000000..eabf8d0 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/IDependencyContainer.cs @@ -0,0 +1,28 @@ +using Microsoft.Practices.ServiceLocation; + +namespace iQuarc.AppBoot +{ + /// + /// Represents an abstraction of a Dependency Injection Container + /// Existent container frameworks (like Unity) are adapted to this abstraction + /// + public interface IDependencyContainer + { + /// + /// Gets this Dependency Injection Container adapted to IServiceLocator interface. + /// This is the interface that is going to be used to request registered service implementations + /// + IServiceLocator AsServiceLocator { get; } + + /// + /// Registers a type to the container based on the service information. + /// + void RegisterService(ServiceInfo service); + + + /// + /// Registers the instance into the container as a singleton (Lifetime.Application) + /// + void RegisterInstance(T instance); + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/IModule.cs b/AppBoot/iQuarc.AppBoot/IModule.cs new file mode 100644 index 0000000..c98b005 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/IModule.cs @@ -0,0 +1,10 @@ +namespace iQuarc.AppBoot +{ + /// + /// Represents a module of the application. + /// + public interface IModule + { + void Initialize(); + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/Properties/AssemblyInfo.cs b/AppBoot/iQuarc.AppBoot/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..051f9c4 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/Properties/AssemblyInfo.cs @@ -0,0 +1,49 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +[assembly: AssemblyTitle("iQuarc.AppBoot")] +[assembly: AssemblyDescription("Basic functionality for hiding the DI Container and for defining a modular application.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("iQuarc")] +[assembly: AssemblyProduct("iQuarc.AppBoot")] +[assembly: AssemblyCopyright("Copyright © iQuarc 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. + +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM + +[assembly: Guid("5df84816-eaa2-460c-8068-96a2b1a066ee")] + +// This assembly code is intended to be used from other languages than C# + +[assembly: CLSCompliant(true)] + +// Make the Unit Testing assembly friendly + +[assembly: InternalsVisibleTo("iQuarc.AppBoot.UnitTests")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/RegistrationsCatalog.cs b/AppBoot/iQuarc.AppBoot/RegistrationsCatalog.cs new file mode 100644 index 0000000..93ff8fe --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/RegistrationsCatalog.cs @@ -0,0 +1,82 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace iQuarc.AppBoot +{ + /// + /// A catalog of service registrations which keeps the registrations based on contracts and priorities + /// Lower priority registrations for same contract are overwritten by higher priority registrations. + /// + /// Registration catalog will contain: + /// 1. For one FromType it has registrations given by ONLY one behavior, and that behavior is the one with the highest priority + /// 2. Newly added registrations with same FromType, same ContractKey and are ignored + /// + /// + internal class RegistrationsCatalog : IEnumerable + { + private readonly LinkedList registrations = new LinkedList(); + + public void Add(ServiceInfo serviceInfo, int priority) + { + Registration newRegistration = new Registration(serviceInfo, priority); + Add(newRegistration); + } + + private void Add(Registration newReg) + { + bool isNewFrom = true; + bool add = true; + + LinkedListNode current = registrations.First; + while (current != null) + { + Registration reg = current.Value; + LinkedListNode next = current.Next; + + if (reg.Service.From == newReg.Service.From) + { + isNewFrom = false; + + if (reg.Priority < newReg.Priority) + registrations.Remove(current); + else if ( reg.Priority > newReg.Priority || + reg.Service.ContractName == newReg.Service.ContractName) + { + add = false; + break; + } + } + + current = next; + } + + if (isNewFrom || add) + registrations.AddLast(newReg); + } + + public IEnumerator GetEnumerator() + { + return registrations.Select(r => r.Service).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + + private class Registration + { + public Registration(ServiceInfo service, int priority) + { + Service = service; + Priority = priority; + } + + public int Priority { get; private set; } + + public ServiceInfo Service { get; private set; } + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/UnityBootstrapper.cs b/AppBoot/iQuarc.AppBoot/UnityBootstrapper.cs new file mode 100644 index 0000000..a3537d1 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/UnityBootstrapper.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Reflection; + +namespace iQuarc.AppBoot +{ + public class UnityBootstrapper : Bootstrapper + { + public UnityBootstrapper(IEnumerable applicationAssemblies) + : base(applicationAssemblies, new UnityContainerAdapter()) + { + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/UnityContainerAdapter.cs b/AppBoot/iQuarc.AppBoot/UnityContainerAdapter.cs new file mode 100644 index 0000000..785a958 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/UnityContainerAdapter.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using Microsoft.Practices.ServiceLocation; +using Microsoft.Practices.Unity; + +namespace iQuarc.AppBoot +{ + internal sealed class UnityContainerAdapter : IDependencyContainer, IDisposable + { + private static readonly Dictionary> lifetimeManagers + = new Dictionary> + { + {Lifetime.Instance, s => new PerResolveLifetimeManager()}, + {Lifetime.AlwaysNew, s => new TransientLifetimeManager()}, + {Lifetime.Application, s => new ContainerControlledLifetimeManager()}, + }; + + private readonly IUnityContainer container; + private readonly IServiceLocator serviceLocator; + + public UnityContainerAdapter() + { + container = new UnityContainer(); + serviceLocator = new UnityServiceLocator(container); + } + + public IServiceLocator AsServiceLocator + { + get { return serviceLocator; } + } + + public void RegisterService(ServiceInfo service) + { + LifetimeManager lifetime = GetLifetime(service); + container.RegisterType(service.From, service.To, service.ContractName, lifetime, new InjectionMember[] {}); + } + + private static LifetimeManager GetLifetime(ServiceInfo srv) + { + Func factory = lifetimeManagers[srv.InstanceLifetime]; + return factory(srv); + } + + public void RegisterInstance(T instance) + { + container.RegisterInstance(instance); + } + + public void Dispose() + { + container.Dispose(); + } + } +} \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/iQuarc.AppBoot.csproj b/AppBoot/iQuarc.AppBoot/iQuarc.AppBoot.csproj new file mode 100644 index 0000000..d1caaba --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/iQuarc.AppBoot.csproj @@ -0,0 +1,88 @@ + + + + + Debug + AnyCPU + {F1412A2B-3E4D-43BC-8A0A-8FC72703AA2C} + Library + Properties + iQuarc.AppBoot + iQuarc.AppBoot + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + iQuarc.AppBoot.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\packages\iQuarc.SystemEx.1.0.0.0\lib\net40\iQuarc.SystemEx.dll + + + ..\packages\CommonServiceLocator.1.0\lib\NET35\Microsoft.Practices.ServiceLocation.dll + + + False + ..\packages\Unity.3.0.1304.1\lib\Net45\Microsoft.Practices.Unity.dll + + + False + ..\packages\Unity.3.0.1304.1\lib\Net45\Microsoft.Practices.Unity.Configuration.dll + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/iQuarc.AppBoot.nuspec b/AppBoot/iQuarc.AppBoot/iQuarc.AppBoot.nuspec new file mode 100644 index 0000000..6f5d194 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/iQuarc.AppBoot.nuspec @@ -0,0 +1,18 @@ + + + + $id$ + $version$ + $title$ + $author$ + $author$ + false + $description$ + $description$ + Basic functionality for hiding the DI Container and for defining a modular application. + Copyright 2014 + Dependency Injection Modularity Bootstrap Configuration + https://raw.githubusercontent.com/iQuarc/AppBoot/master/MIT.md + https://github.com/iQuarc/AppBoot + + diff --git a/AppBoot/iQuarc.AppBoot/iQuarc.AppBoot.ruleset b/AppBoot/iQuarc.AppBoot/iQuarc.AppBoot.ruleset new file mode 100644 index 0000000..556355a --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/iQuarc.AppBoot.ruleset @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AppBoot/iQuarc.AppBoot/make-package.bat b/AppBoot/iQuarc.AppBoot/make-package.bat new file mode 100644 index 0000000..35db4e3 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/make-package.bat @@ -0,0 +1,2 @@ +c:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe iQuarc.AppBoot.csproj /p:Configuration=Release +nuget pack iQuarc.AppBoot.csproj -Prop Configuration=Release diff --git a/AppBoot/iQuarc.AppBoot/packages.config b/AppBoot/iQuarc.AppBoot/packages.config new file mode 100644 index 0000000..1a6f383 --- /dev/null +++ b/AppBoot/iQuarc.AppBoot/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/MIT.md b/MIT.md new file mode 100644 index 0000000..3bbc739 --- /dev/null +++ b/MIT.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 iQuarc + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file