# HG changeset patch # User Steven Robbins # Date 1268593254 0 # Node ID efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 # Parent 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 # Parent a767d2a87f0f8e5fffff9f3e8148fa3404c8a73f Merge of messenger and child containers code diff -r 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 -r efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Sun Mar 14 19:00:54 2010 +0000 @@ -0,0 +1,35 @@ +# Ignore file for Visual Studio 2008 + +# use glob syntax +syntax: glob + +# Ignore Visual Studio 2008 files +*.obj +*.exe +*.pdb +*.user +*.aps +*.pch +*.vspscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.cache +*.ilk +*.log +*.lib +*.sbr +*.scc +*.cs.orig +[Bb]in +[Db]ebug*/ +obj/ +[Rr]elease*/ +_ReSharper*/ +[Tt]est[Rr]esult* +[Bb]uild[Ll]og.* +*.[Pp]ublish.xml diff -r 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 -r efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 TinyIoC.Tests/TestData/TinyMessengerTestData.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TinyIoC.Tests/TestData/TinyMessengerTestData.cs Sun Mar 14 19:00:54 2010 +0000 @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using TinyMessenger; + +namespace TinyIoC.Tests.TestData +{ + public class TestMessage : TinyMessageBase + { + public TestMessage(object sender) : base(sender) + { + + } + } + + public class TestProxy : ITinyMessageProxy + { + public ITinyMessage Message {get; private set;} + + public void Deliver(ITinyMessage message, ITinyMessageSubscription subscription) + { + this.Message = message; + subscription.Deliver(message); + } + } + +} diff -r 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 -r efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 TinyIoC.Tests/TestData/UtilityMethods.cs --- a/TinyIoC.Tests/TestData/UtilityMethods.cs Thu Mar 11 17:26:31 2010 +0000 +++ b/TinyIoC.Tests/TestData/UtilityMethods.cs Sun Mar 14 19:00:54 2010 +0000 @@ -18,6 +18,7 @@ using System.Linq; using System.Text; using TinyIoC.Tests.TestData.BasicClasses; +using TinyMessenger; namespace TinyIoC.Tests.TestData { @@ -60,5 +61,20 @@ container.Register(item).WithWeakReference(); } + public static ITinyMessengerHub GetMessenger() + { + return new TinyMessengerHub(); + } + + public static void FakeDeliveryAction(T message) + where T:ITinyMessage + { + } + + public static bool FakeMessageFilter(T message) + where T:ITinyMessage + { + return true; + } } } diff -r 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 -r efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 TinyIoC.Tests/TinyIoC.Tests.csproj --- a/TinyIoC.Tests/TinyIoC.Tests.csproj Thu Mar 11 17:26:31 2010 +0000 +++ b/TinyIoC.Tests/TinyIoC.Tests.csproj Sun Mar 14 19:00:54 2010 +0000 @@ -50,9 +50,11 @@ + + diff -r 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 -r efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 TinyIoC.Tests/TinyIoCTests.cs --- a/TinyIoC.Tests/TinyIoCTests.cs Thu Mar 11 17:26:31 2010 +0000 +++ b/TinyIoC.Tests/TinyIoCTests.cs Sun Mar 14 19:00:54 2010 +0000 @@ -1861,5 +1861,147 @@ Assert.IsInstanceOfType(result2, typeof(ExternalTypes.IExternalTestInterface)); } + [TestMethod] + public void GetChildContainer_NoParameters_ReturnsContainerInstance() + { + var container = UtilityMethods.GetContainer(); + + var child = container.GetChildContainer(); + + Assert.IsInstanceOfType(child, typeof(TinyIoCContainer)); + } + + [TestMethod] + public void GetChildContainer_NoParameters_ContainerReturnedIsNewContainer() + { + var container = UtilityMethods.GetContainer(); + + var child = container.GetChildContainer(); + + Assert.IsFalse(object.ReferenceEquals(child, container)); + } + + [TestMethod] + public void ChildContainerResolve_TypeRegisteredWithParent_ResolvesType() + { + var container = UtilityMethods.GetContainer(); + var child = container.GetChildContainer(); + container.Register(); + + var result = child.Resolve(); + + Assert.IsInstanceOfType(result, typeof(TestClassDefaultCtor)); + } + + [TestMethod] + public void ChildContainerCanResolve_TypeRegisteredWithParent_ReturnsTrue() + { + var container = UtilityMethods.GetContainer(); + var child = container.GetChildContainer(); + container.Register(); + + var result = child.CanResolve(); + + Assert.IsTrue(result); + } + + [TestMethod] + public void ChildContainerResolve_TypeRegisteredWithChild_ResolvesType() + { + var container = UtilityMethods.GetContainer(); + var child = container.GetChildContainer(); + child.Register(); + + var result = child.Resolve(); + + Assert.IsInstanceOfType(result, typeof(TestClassDefaultCtor)); + } + + [TestMethod] + public void ChildContainerCanResolve_TypeRegisteredWithChild_ReturnsTrue() + { + var container = UtilityMethods.GetContainer(); + var child = container.GetChildContainer(); + child.Register(); + + var result = child.CanResolve(); + + Assert.IsTrue(result); + } + + [TestMethod] + public void ChildContainerResolve_TypeRegisteredWithParentAndChild_ResolvesChildVersion() + { + var container = UtilityMethods.GetContainer(); + var containerInstance = new TestClassDefaultCtor(); + var child = container.GetChildContainer(); + var childInstance = new TestClassDefaultCtor(); + container.Register(containerInstance); + child.Register(childInstance); + + var result = child.Resolve(); + + Assert.ReferenceEquals(result, childInstance); + } + + [TestMethod] + public void ChildContainerResolve_NamedOnlyRegisteredWithParent_ResolvesFromParent() + { + var container = UtilityMethods.GetContainer(); + var containerInstance = new TestClassDefaultCtor(); + var child = container.GetChildContainer(); + var childInstance = new TestClassDefaultCtor(); + container.Register(containerInstance, "Testing"); + child.Register(childInstance); + + var result = child.Resolve("Testing"); + + Assert.ReferenceEquals(result, containerInstance); + } + + [TestMethod] + public void ChildContainerCanResolve_NamedOnlyRegisteredWithParent_ReturnsTrue() + { + var container = UtilityMethods.GetContainer(); + var containerInstance = new TestClassDefaultCtor(); + var child = container.GetChildContainer(); + var childInstance = new TestClassDefaultCtor(); + container.Register(containerInstance, "Testing"); + child.Register(childInstance); + + var result = child.CanResolve("Testing"); + + Assert.IsTrue(result); + } + + [TestMethod] + public void ChildContainerResolve_NamedOnlyRegisteredWithParentUnnamedFallbackOn_ResolvesFromChild() + { + var container = UtilityMethods.GetContainer(); + var containerInstance = new TestClassDefaultCtor(); + var child = container.GetChildContainer(); + var childInstance = new TestClassDefaultCtor(); + container.Register(containerInstance, "Testing"); + child.Register(childInstance); + + var result = child.Resolve("Testing", new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.AttemptUnnamedResolution }); + + Assert.ReferenceEquals(result, childInstance); + } + + [TestMethod] + public void ChildContainerResolve_NamedOnlyRegisteredWithParentChildNoRegistrationUnnamedFallbackOn_ResolvesFromParent() + { + var container = UtilityMethods.GetContainer(); + var containerInstance = new TestClassDefaultCtor(); + var child = container.GetChildContainer(); + var childInstance = new TestClassDefaultCtor(); + container.Register(containerInstance, "Testing"); + + var result = child.Resolve("Testing", new ResolveOptions() { NamedResolutionFailureAction = NamedResolutionFailureActions.AttemptUnnamedResolution }); + + Assert.ReferenceEquals(result, childInstance); + } + } } diff -r 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 -r efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 TinyIoC.Tests/TinyMessengerTests.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TinyIoC.Tests/TinyMessengerTests.cs Sun Mar 14 19:00:54 2010 +0000 @@ -0,0 +1,425 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using TinyIoC.Tests.TestData; +using TinyMessenger; +using System.Threading; + +namespace TinyIoC.Tests +{ + + [TestClass] + public class TinyMessengerTests + { + [TestMethod] + public void TinyMessenger_Ctor_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + } + + [TestMethod] + public void Subscribe_ValidDeliverAction_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction)); + } + + [TestMethod] + public void SubScribe_ValidDeliveryAction_ReturnsRegistrationObject() + { + var messenger = UtilityMethods.GetMessenger(); + + var output = messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction)); + + Assert.IsInstanceOfType(output, typeof(TinyMessageSubscriptionToken)); + } + + [TestMethod] + public void Subscribe_ValidDeliverActionWIthStrongReferences_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), true); + } + + [TestMethod] + public void Subscribe_ValidDeliveryActionAndFilter_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void Subscribe_NullDeliveryAction_Throws() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.Subscribe(null, new Func(UtilityMethods.FakeMessageFilter)); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void Subscribe_NullFilter_Throws() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), null, new TestProxy()); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void Subscribe_NullProxy_Throws() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter), null); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void Unsubscribe_NullSubscriptionObject_Throws() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.Unsubscribe(null); + } + + [TestMethod] + public void Unsubscribe_PreviousSubscription_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + var subscription = messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + + messenger.Unsubscribe(subscription); + } + + [TestMethod] + public void Subscribe_PreviousSubscription_ReturnsDifferentSubscriptionObject() + { + var messenger = UtilityMethods.GetMessenger(); + var sub1 = messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + var sub2 = messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + + Assert.IsFalse(object.ReferenceEquals(sub1, sub2)); + } + + [TestMethod] + public void Subscribe_CustomProxyNoFilter_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + var proxy = new TestProxy(); + + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), proxy); + } + + [TestMethod] + public void Subscribe_CustomProxyWithFilter_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + var proxy = new TestProxy(); + + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter), proxy); + } + + [TestMethod] + public void Subscribe_CustomProxyNoFilterStrongReference_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + var proxy = new TestProxy(); + + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), true, proxy); + } + + [TestMethod] + public void Subscribe_CustomProxyFilterStrongReference_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + var proxy = new TestProxy(); + + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter), true, proxy); + } + + [TestMethod] + public void Publish_CustomProxyNoFilter_UsesCorrectProxy() + { + var messenger = UtilityMethods.GetMessenger(); + var proxy = new TestProxy(); + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), proxy); + var message = new TestMessage(this); + + messenger.Publish(message); + + Assert.ReferenceEquals(message, proxy.Message); + } + + [TestMethod] + public void Publish_CustomProxyWithFilter_UsesCorrectProxy() + { + var messenger = UtilityMethods.GetMessenger(); + var proxy = new TestProxy(); + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter), proxy); + var message = new TestMessage(this); + + messenger.Publish(message); + + Assert.ReferenceEquals(message, proxy.Message); + } + + [TestMethod] + public void Publish_CustomProxyNoFilterStrongReference_UsesCorrectProxy() + { + var messenger = UtilityMethods.GetMessenger(); + var proxy = new TestProxy(); + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), true, proxy); + var message = new TestMessage(this); + + messenger.Publish(message); + + Assert.ReferenceEquals(message, proxy.Message); + } + + [TestMethod] + public void Publish_CustomProxyFilterStrongReference_UsesCorrectProxy() + { + var messenger = UtilityMethods.GetMessenger(); + var proxy = new TestProxy(); + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter), true, proxy); + var message = new TestMessage(this); + + messenger.Publish(message); + + Assert.ReferenceEquals(message, proxy.Message); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void Publish_NullMessage_Throws() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.Publish(null); + } + + [TestMethod] + public void Publish_NoSubscribers_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.Publish(new TestMessage(this)); + } + + [TestMethod] + public void Publish_Subscriber_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + messenger.Subscribe(new Action(UtilityMethods.FakeDeliveryAction), new Func(UtilityMethods.FakeMessageFilter)); + + messenger.Publish(new TestMessage(this)); + } + + [TestMethod] + public void Publish_SubscribedMessageNoFilter_GetsMessage() + { + var messenger = UtilityMethods.GetMessenger(); + bool received = false; + messenger.Subscribe((m) => { received = true; }); + + messenger.Publish(new TestMessage(this)); + + Assert.IsTrue(received); + } + + [TestMethod] + public void Publish_SubscribedThenUnsubscribedMessageNoFilter_DoesNotGetMessage() + { + var messenger = UtilityMethods.GetMessenger(); + bool received = false; + var token = messenger.Subscribe((m) => { received = true; }); + messenger.Unsubscribe(token); + + messenger.Publish(new TestMessage(this)); + + Assert.IsFalse(received); + } + + [TestMethod] + public void Publish_SubscribedMessageButFiltered_DoesNotGetMessage() + { + var messenger = UtilityMethods.GetMessenger(); + bool received = false; + messenger.Subscribe((m) => { received = true; }, (m) => false); + + messenger.Publish(new TestMessage(this)); + + Assert.IsFalse(received); + } + + [TestMethod] + public void Publish_SubscribedMessageNoFilter_GetsActualMessage() + { + var messenger = UtilityMethods.GetMessenger(); + ITinyMessage receivedMessage = null; + var payload = new TestMessage(this); + messenger.Subscribe((m) => { receivedMessage = m; }); + + messenger.Publish(payload); + + Assert.ReferenceEquals(payload, receivedMessage); + } + + [TestMethod] + public void GenericTinyMessage_String_SubscribeDoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + var output = string.Empty; + messenger.Subscribe>((m) => { output = m.Content; }); + } + + [TestMethod] + public void GenericTinyMessage_String_PubishDoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + messenger.Publish(new GenericTinyMessage(this, "Testing")); + } + + [TestMethod] + public void GenericTinyMessage_String_PubishAndSubscribeDeliversContent() + { + var messenger = UtilityMethods.GetMessenger(); + var output = string.Empty; + messenger.Subscribe>((m) => { output = m.Content; }); + messenger.Publish(new GenericTinyMessage(this, "Testing")); + + Assert.AreEqual("Testing", output); + } + + [TestMethod] + public void Publish_SubscriptionThrowingException_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + messenger.Subscribe>((m) => { throw new NotImplementedException(); }); + + messenger.Publish(new GenericTinyMessage(this, "Testing")); + } + + [TestMethod] + public void PublishAsync_NoCallback_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.PublishAsync(new TestMessage(this)); + } + + [TestMethod] + public void PublishAsync_NoCallback_PublishesMessage() + { + var messenger = UtilityMethods.GetMessenger(); + bool received = false; + messenger.Subscribe((m) => { received = true; }); + + messenger.PublishAsync(new TestMessage(this)); + + // Horrible wait loop! + int waitCount = 0; + while (!received && waitCount < 100) + { + Thread.Sleep(10); + waitCount++; + } + Assert.IsTrue(received); + } + + [TestMethod] + public void PublishAsync_Callback_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + + messenger.PublishAsync(new TestMessage(this), (r) => {string test = "Testing";}); + } + + [TestMethod] + public void PublishAsync_Callback_PublishesMessage() + { + var messenger = UtilityMethods.GetMessenger(); + bool received = false; + messenger.Subscribe((m) => { received = true; }); + + messenger.PublishAsync(new TestMessage(this), (r) => { string test = "Testing"; }); + + // Horrible wait loop! + int waitCount = 0; + while (!received && waitCount < 100) + { + Thread.Sleep(10); + waitCount++; + } + Assert.IsTrue(received); + } + + [TestMethod] + public void PublishAsync_Callback_CallsCallback() + { + var messenger = UtilityMethods.GetMessenger(); + bool received = false; + bool callbackReceived = false; + messenger.Subscribe((m) => { received = true; }); + + messenger.PublishAsync(new TestMessage(this), (r) => { callbackReceived = true; }); + + // Horrible wait loop! + int waitCount = 0; + while (!callbackReceived && waitCount < 100) + { + Thread.Sleep(10); + waitCount++; + } + Assert.IsTrue(received); + } + + [TestMethod] + public void CancellableGenericTinyMessage_Publish_DoesNotThrow() + { + var messenger = UtilityMethods.GetMessenger(); + messenger.Publish>(new CancellableGenericTinyMessage(this, "Testing", () => { bool test = true; })); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void CancellableGenericTinyMessage_PublishWithNullAction_Throws() + { + var messenger = UtilityMethods.GetMessenger(); + messenger.Publish>(new CancellableGenericTinyMessage(this, "Testing", null)); + } + + [TestMethod] + public void CancellableGenericTinyMessage_SubscriberCancels_CancelActioned() + { + var messenger = UtilityMethods.GetMessenger(); + bool cancelled = false; + messenger.Subscribe>((m) => { m.Cancel(); }); + + messenger.Publish>(new CancellableGenericTinyMessage(this, "Testing", () => { cancelled = true; })); + + Assert.IsTrue(cancelled); + } + + [TestMethod] + public void CancellableGenericTinyMessage_SeveralSubscribersOneCancels_CancelActioned() + { + var messenger = UtilityMethods.GetMessenger(); + bool cancelled = false; + messenger.Subscribe>((m) => { var test = 1; }); + messenger.Subscribe>((m) => { m.Cancel(); }); + messenger.Subscribe>((m) => { var test = 1; }); + + messenger.Publish>(new CancellableGenericTinyMessage(this, "Testing", () => { cancelled = true; })); + + Assert.IsTrue(cancelled); + } + } +} \ No newline at end of file diff -r 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 -r efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 TinyIoC.vsmdi --- a/TinyIoC.vsmdi Thu Mar 11 17:26:31 2010 +0000 +++ b/TinyIoC.vsmdi Sun Mar 14 19:00:54 2010 +0000 @@ -3,4 +3,10 @@ + + + + + + \ No newline at end of file diff -r 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 -r efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 TinyIoC/TinyIoC.cs --- a/TinyIoC/TinyIoC.cs Thu Mar 11 17:26:31 2010 +0000 +++ b/TinyIoC/TinyIoC.cs Sun Mar 14 19:00:54 2010 +0000 @@ -13,6 +13,10 @@ // FITNESS FOR A PARTICULAR PURPOSE. //=============================================================================== +// Uncomment this line if you want the container to automatically +// register the TinyMessenger messenger/event aggregator +//#define TINYMESSENGER + using System; using System.Collections.Generic; using System.Linq; @@ -87,6 +91,8 @@ item.Dispose(); } } + + GC.SuppressFinalize(this); } #endregion @@ -398,6 +404,13 @@ #endregion #region Public API + #region Child Containers + public TinyIoCContainer GetChildContainer() + { + return new TinyIoCContainer(this); + } + #endregion + #region Registration /// /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. @@ -1382,6 +1395,17 @@ RegisterDefaultTypes(); } + + ~TinyIoCContainer() + { + _RegisteredTypes.Clear(); + } + + TinyIoCContainer _Parent; + private TinyIoCContainer(TinyIoCContainer parent) : this() + { + this._Parent = parent; + } #endregion #region Internal Methods @@ -1449,6 +1473,12 @@ private void RegisterDefaultTypes() { this.Register(this); + +#if TINYMESSENGER + // Only register the TinyMessenger singleton if we are the root container + if (this._Parent != null) + this.Register(); +#endif } private ObjectFactoryBase GetCurrentFactory(TypeRegistration registration) @@ -1510,8 +1540,9 @@ } // Fail if requesting named resolution and settings set to fail if unresolved + // Or bubble up if we have a parent if (!String.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.Fail) - return false; + return (_Parent != null) ? _Parent.CanResolveInternal(registration, parameters, options) : false; // Attemped unnamed fallback container resolution if relevant and requested if (!String.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.AttemptUnnamedResolution) @@ -1530,8 +1561,13 @@ return true; // Attempt unregistered construction if possible and requested + // If we cant', bubble if we have a parent if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (checkType.IsGenericType && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) - return (GetBestConstructor(checkType, parameters, options) != null) ? true : false; + return (GetBestConstructor(checkType, parameters, options) != null) ? true : (_Parent != null) ? _Parent.CanResolveInternal(registration, parameters, options) : false; + + // Bubble resolution up the container tree if we have a parent + if (_Parent != null) + return _Parent.CanResolveInternal(registration, parameters, options); return false; } @@ -1577,7 +1613,13 @@ // Fail if requesting named resolution and settings set to fail if unresolved if (!String.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.Fail) - throw new TinyIoCResolutionException(registration.Type); + { + // Bubble resolution up the container tree if we have a parent + if (_Parent != null) + return _Parent.ResolveInternal(registration, parameters, options); + else + throw new TinyIoCResolutionException(registration.Type); + } // Attemped unnamed fallback container resolution if relevant and requested if (!String.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.AttemptUnnamedResolution) @@ -1606,6 +1648,10 @@ return ConstructType(registration.Type, parameters, options); } + // Bubble resolution up the container tree if we have a parent + if (_Parent != null) + return _Parent.ResolveInternal(registration, parameters, options); + // Unable to resolve - throw throw new TinyIoCResolutionException(registration.Type); } @@ -1772,7 +1818,7 @@ { property.SetValue(input, ResolveInternal(new TypeRegistration(property.PropertyType), NamedParameterOverloads.Default, resolveOptions), null); } - catch (Exception TinyIoCResolutionException) + catch (TinyIoCResolutionException) { // Catch any resolution errors and ignore them } @@ -1785,6 +1831,8 @@ public void Dispose() { _RegisteredTypes.Dispose(); + + GC.SuppressFinalize(this); } #endregion diff -r 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 -r efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 TinyIoC/TinyIoC.cs.orig --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TinyIoC/TinyIoC.cs.orig Sun Mar 14 19:00:54 2010 +0000 @@ -0,0 +1,1796 @@ +//=============================================================================== +// TinyIoC +// +// An easy to use, hassle free, Inversion of Control Container for small projects +// and beginners alike. +// +// http://hg.grumpydev.com/tinyioc +//=============================================================================== +// Copyright © Steven Robbins. All rights reserved. +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY +// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT +// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE. +//=============================================================================== + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using System.Linq.Expressions; + +namespace TinyIoC +{ + #region SafeDictionary + public class SafeDictionary : IDisposable + { + private readonly object _Padlock = new object(); + private readonly Dictionary _Dictionary = new Dictionary(); + + public TValue this[TKey key] + { + set + { + lock (_Padlock) + { + TValue current; + if (_Dictionary.TryGetValue(key, out current)) + { + var disposable = current as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + + _Dictionary[key] = value; + } + } + } + + public bool TryGetValue(TKey key, out TValue value) + { + lock (_Padlock) + { + return _Dictionary.TryGetValue(key, out value); + } + } + + public bool Remove(TKey key) + { + lock (_Padlock) + { + return _Dictionary.Remove(key); + } + } + + public void Clear() + { + lock (_Padlock) + { + _Dictionary.Clear(); + } + } + + #region IDisposable Members + + public void Dispose() + { + lock (_Padlock) + { + var disposableItems = from item in _Dictionary.Values + where item is IDisposable + select item as IDisposable; + + foreach (var item in disposableItems) + { + item.Dispose(); + } + } + } + + #endregion + } + #endregion + + #region TinyIoC Exception Types + public class TinyIoCResolutionException : Exception + { + private const string ERROR_TEXT = "Unable to resolve type: {0}"; + + public TinyIoCResolutionException(Type type) + : base(String.Format(ERROR_TEXT, type.FullName)) + { + } + + public TinyIoCResolutionException(Type type, Exception innerException) + : base(String.Format(ERROR_TEXT, type.FullName), innerException) + { + } + } + + public class TinyIoCRegistrationTypeException : Exception + { + private const string REGISTER_ERROR_TEXT = "Cannot register type {0} - abstract classes or interfaces are not valid implementation types for {1}."; + + public TinyIoCRegistrationTypeException(Type type, string factory) + : base(String.Format(REGISTER_ERROR_TEXT, type.FullName, factory)) + { + } + + public TinyIoCRegistrationTypeException(Type type, string factory, Exception innerException) + : base(String.Format(REGISTER_ERROR_TEXT, type.FullName, factory), innerException) + { + } + } + + public class TinyIoCRegistrationException : Exception + { + private const string CONVERT_ERROR_TEXT = "Cannot convert current registration of {0} to {1}"; + + public TinyIoCRegistrationException(Type type, string method) + : base(String.Format(CONVERT_ERROR_TEXT, type.FullName, method)) + { + } + + public TinyIoCRegistrationException(Type type, string method, Exception innerException) + : base(String.Format(CONVERT_ERROR_TEXT, type.FullName, method), innerException) + { + } + } + + public class TinyIoCWeakReferenceException : Exception + { + private const string ERROR_TEXT = "Unable to instantiate {0} - referenced object has been reclaimed"; + + public TinyIoCWeakReferenceException(Type type) + : base(String.Format(ERROR_TEXT, type.FullName)) + { + } + + public TinyIoCWeakReferenceException(Type type, Exception innerException) + : base(String.Format(ERROR_TEXT, type.FullName), innerException) + { + } + } + + public class TinyIoCConstructorResolutionException : Exception + { + private const string ERROR_TEXT = "Unable to resolve constructor for {0} using provided Expression."; + + public TinyIoCConstructorResolutionException(Type type) + : base(String.Format(ERROR_TEXT, type.FullName)) + { + } + + public TinyIoCConstructorResolutionException(Type type, Exception innerException) + : base(String.Format(ERROR_TEXT, type.FullName), innerException) + { + } + + public TinyIoCConstructorResolutionException(string message, Exception innerException) + : base(message, innerException) + { + } + + public TinyIoCConstructorResolutionException(string message) + : base(message) + { + } + } + + public class TinyIoCAutoRegistrationException : Exception + { + private const string ERROR_TEXT = "Duplicate implementation of type {0} found ({1})."; + + public TinyIoCAutoRegistrationException(Type registerType, IEnumerable types) + : base(String.Format(ERROR_TEXT, registerType, GetTypesString(types))) + { + } + + public TinyIoCAutoRegistrationException(Type registerType, IEnumerable types, Exception innerException) + : base(String.Format(ERROR_TEXT, registerType, GetTypesString(types)), innerException) + { + } + + private static string GetTypesString(IEnumerable types) + { + var typeNames = from type in types + select type.FullName; + + return string.Join(",", typeNames.ToArray()); + } + } + #endregion + + #region Public Setup / Settings Classes + /// + /// Name/Value pairs for specifying "user" parameters when resolving + /// + public sealed class NamedParameterOverloads : Dictionary + { + public static NamedParameterOverloads FromIDictionary(IDictionary data) + { + return data as NamedParameterOverloads ?? new NamedParameterOverloads(data); + } + + public NamedParameterOverloads() + { + } + + public NamedParameterOverloads(IDictionary data) : base(data) + { + } + + private static readonly NamedParameterOverloads _Default = new NamedParameterOverloads(); + + public static NamedParameterOverloads Default + { + get + { + return _Default; + } + } + } + + public enum UnregisteredResolutionActions + { + /// + /// Attempt to resolve type, even if the type isn't registered. + /// + /// Registered types/options will always take precedence. + /// + AttemptResolve, + + /// + /// Fail resolution if type not explicitly registered + /// + Fail, + + /// + /// Attempt to resolve unregistered type if requested type is generic + /// and no registration exists for the specific generic parameters used. + /// + /// Registered types/options will always take precedence. + /// + GenericsOnly + } + + public enum NamedResolutionFailureActions + { + AttemptUnnamedResolution, + Fail + } + + /// + /// Resolution settings + /// + public sealed class ResolveOptions + { + private static readonly ResolveOptions _Default = new ResolveOptions(); + + private UnregisteredResolutionActions _UnregisteredResolutionAction = UnregisteredResolutionActions.AttemptResolve; + public UnregisteredResolutionActions UnregisteredResolutionAction + { + get { return _UnregisteredResolutionAction; } + set { _UnregisteredResolutionAction = value; } + } + + private NamedResolutionFailureActions _NamedResolutionFailureAction = NamedResolutionFailureActions.Fail; + public NamedResolutionFailureActions NamedResolutionFailureAction + { + get { return _NamedResolutionFailureAction; } + set { _NamedResolutionFailureAction = value; } + } + + public static ResolveOptions Default + { + get + { + return _Default; + } + } + } + #endregion + + public sealed class TinyIoCContainer : IDisposable + { + #region "Fluent" API + /// + /// Registration options for "fluent" API + /// + /// Registered type + /// Implementation type for construction of RegisteredType + public sealed class RegisterOptions + { + private TinyIoCContainer _Container; + private TypeRegistration _Registration; + + public RegisterOptions(TinyIoCContainer container, TypeRegistration registration) + { + _Container = container; + _Registration = registration; + } + + /// + /// Make registration a singleton (single instance) if possible + /// + /// RegisterOptions + /// + public RegisterOptions AsSingleton() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "singleton"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.SingletonVariant); + } + + /// + /// Make registration multi-instance if possible + /// + /// RegisterOptions + /// + public RegisterOptions AsMultiInstance() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "multi-instance"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.MultiInstanceVariant); + } + + /// + /// Make registration hold a weak reference if possible + /// + /// RegisterOptions + /// + public RegisterOptions WithWeakReference() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "weak reference"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.WeakReferenceVariant); + } + + /// + /// Make registration hold a strong reference if possible + /// + /// RegisterOptions + /// + public RegisterOptions WithStrongReference() + { + var currentFactory = _Container.GetCurrentFactory(_Registration); + + if (currentFactory == null) + throw new TinyIoCRegistrationException(_Registration.Type, "strong reference"); + + return _Container.AddUpdateRegistration(_Registration, currentFactory.StrongReferenceVariant); + } + + public RegisterOptions UsingConstructor(Expression> constructor) + { + var lambda = constructor as LambdaExpression; + if (lambda == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + var newExpression = lambda.Body as NewExpression; + if (newExpression == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + var constructorInfo = newExpression.Constructor; + if (constructorInfo == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + var currentFactory = _Container.GetCurrentFactory(_Registration); + if (currentFactory == null) + throw new TinyIoCConstructorResolutionException(typeof(RegisterType)); + + currentFactory.SetConstructor(constructorInfo); + + return this; + } + } + #endregion + + #region Public API + #region Registration + /// + /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + public void AutoRegister() + { + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies(), true); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the current app domain. + /// + /// Whether to ignore duplicate implementations of an interface/base class. False=throw an exception + /// + public void AutoRegister(bool ignoreDuplicateImplementations) + { + AutoRegisterInternal(AppDomain.CurrentDomain.GetAssemblies(), ignoreDuplicateImplementations); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assembly + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + /// Assembly to process + public void AutoRegister(Assembly assembly) + { + AutoRegisterInternal(new Assembly[] {assembly}, true); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assembly + /// + /// Assembly to process + /// Whether to ignore duplicate implementations of an interface/base class. False=throw an exception + /// + public void AutoRegister(Assembly assembly, bool ignoreDuplicateImplementations) + { + AutoRegisterInternal(new Assembly[] {assembly}, ignoreDuplicateImplementations); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies + /// + /// If more than one class implements an interface then only one implementation will be registered + /// although no error will be thrown. + /// + /// Assemblies to process + public void AutoRegister(IEnumerable assemblies) + { + AutoRegisterInternal(assemblies, true); + } + + /// + /// Attempt to automatically register all non-generic classes and interfaces in the specified assemblies + /// + /// Assemblies to process + /// Whether to ignore duplicate implementations of an interface/base class. False=throw an exception + /// + public void AutoRegister(IEnumerable assemblies, bool ignoreDuplicateImplementations) + { + AutoRegisterInternal(assemblies, ignoreDuplicateImplementations); + } + + /// + /// Creates/replaces a container class registration with default options. + /// + /// Type to register + /// RegisterOptions for fluent API + public RegisterOptions Register() + where RegisterType : class + { + return RegisterInternal(typeof(RegisterType), typeof(RegisterType), string.Empty, GetDefaultObjectFactory()); + } + + /// + /// Creates/replaces a named container class registration with default options. + /// + /// Type to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(string name) + where RegisterType : class + { + return RegisterInternal(typeof(RegisterType), typeof(RegisterType), name, GetDefaultObjectFactory()); + } + + /// + /// Creates/replaces a container class registration with a given implementation and default options. + /// + /// Type to register + /// Type to instantiate that implements RegisterType + /// RegisterOptions for fluent API + public RegisterOptions Register() + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return RegisterInternal(typeof(RegisterType), typeof(RegisterImplementation), string.Empty, GetDefaultObjectFactory()); + } + + /// + /// Creates/replaces a named container class registration with a given implementation and default options. + /// + /// Type to register + /// Type to instantiate that implements RegisterType + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(string name) + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return RegisterInternal(typeof(RegisterType), typeof(RegisterImplementation), name, GetDefaultObjectFactory()); + } + + /// + /// Creates/replaces a container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Instance of RegisterType to register + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterType instance) + where RegisterType : class + { + return RegisterInternal(typeof(RegisterType), typeof(RegisterType), string.Empty, new InstanceFactory(instance)); + } + + /// + /// Creates/replaces a named container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Instance of RegisterType to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterType instance, string name) + where RegisterType : class + { + return RegisterInternal(typeof(RegisterType), typeof(RegisterType), name, new InstanceFactory(instance)); + } + + /// + /// Creates/replaces a container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Type of instance to register that implements RegisterType + /// Instance of RegisterImplementation to register + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterImplementation instance) + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return RegisterInternal(typeof(RegisterType), typeof(RegisterImplementation), string.Empty, new InstanceFactory(instance)); + } + + /// + /// Creates/replaces a named container class registration with a specific, strong referenced, instance. + /// + /// Type to register + /// Type of instance to register that implements RegisterType + /// Instance of RegisterImplementation to register + /// Name of registration + /// RegisterOptions for fluent API + public RegisterOptions Register(RegisterImplementation instance, string name) + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + return RegisterInternal(typeof(RegisterType), typeof(RegisterImplementation), name, new InstanceFactory(instance)); + } + + /// + /// Creates/replaces a container class registration with a user specified factory + /// + /// Type to register + /// Factory/lambda that returns an instance of RegisterType + /// RegisterOptions for fluent API + public RegisterOptions Register(Func factory) + where RegisterType : class + { + return RegisterInternal(typeof(RegisterType), typeof(RegisterType), string.Empty, new DelegateFactory(factory)); + } + + /// + /// Creates/replaces a named container class registration with a user specified factory + /// + /// Type to register + /// Factory/lambda that returns an instance of RegisterType + /// Name of registation + /// RegisterOptions for fluent API + public RegisterOptions Register(Func factory, string name) + where RegisterType : class + { + return RegisterInternal(typeof(RegisterType), typeof(RegisterType), name, new DelegateFactory(factory)); + } + + #endregion + + #region Resolution + /// + /// Attempts to resolve a type using default options. + /// + /// Type to resolve + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve() + where ResolveType : class + { + return ResolveInternal(new TypeRegistration(typeof(ResolveType)), NamedParameterOverloads.Default, ResolveOptions.Default) as ResolveType; + } + + /// + /// Attempts to resolve a type using specified options. + /// + /// Type to resolve + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(ResolveOptions options) + where ResolveType : class + { + return ResolveInternal(new TypeRegistration(typeof(ResolveType)), NamedParameterOverloads.Default, options) as ResolveType; + } + + /// + /// Attempts to resolve a type using default options and the supplied name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name) + where ResolveType : class + { + return ResolveInternal(new TypeRegistration(typeof(ResolveType), name), NamedParameterOverloads.Default, ResolveOptions.Default) as ResolveType; + } + + /// + /// Attempts to resolve a type using supplied options and name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name, ResolveOptions options) + where ResolveType : class + { + return ResolveInternal(new TypeRegistration(typeof(ResolveType), name), NamedParameterOverloads.Default, options) as ResolveType; + } + + /// + /// Attempts to resolve a type using default options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(NamedParameterOverloads parameters) + where ResolveType : class + { + return ResolveInternal(new TypeRegistration(typeof(ResolveType)), parameters, ResolveOptions.Default) as ResolveType; + } + + /// + /// Attempts to resolve a type using specified options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return ResolveInternal(new TypeRegistration(typeof(ResolveType)), parameters, options) as ResolveType; + } + + /// + /// Attempts to resolve a type using default options and the supplied constructor parameters and name. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// User specified constructor parameters + /// Name of registration + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name, NamedParameterOverloads parameters) + where ResolveType : class + { + return ResolveInternal(new TypeRegistration(typeof(ResolveType), name), parameters, ResolveOptions.Default) as ResolveType; + } + + /// + /// Attempts to resolve a named type using specified options and the supplied constructor parameters. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Type to resolve + /// Name of registration + /// User specified constructor parameters + /// Resolution options + /// Instance of type + /// Unable to resolve the type. + public ResolveType Resolve(string name, NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return ResolveInternal(new TypeRegistration(typeof(ResolveType), name), parameters, options) as ResolveType; + } + + /// + /// Attempts to predict whether a given type can be resolved with default options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// Bool indicating whether the type can be resolved + public bool CanResolve() + where ResolveType : class + { + return CanResolveInternal(new TypeRegistration(typeof(ResolveType)), NamedParameterOverloads.Default, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given named type can be resolved with default options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name) + where ResolveType : class + { + return CanResolveInternal(new TypeRegistration(typeof(ResolveType), name), NamedParameterOverloads.Default, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given type can be resolved with the specified options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(ResolveOptions options) + where ResolveType : class + { + return CanResolveInternal(new TypeRegistration(typeof(ResolveType)), NamedParameterOverloads.Default, options); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the specified options. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name, ResolveOptions options) + where ResolveType : class + { + return CanResolveInternal(new TypeRegistration(typeof(ResolveType), name), NamedParameterOverloads.Default, options); + } + + /// + /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters and default options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// User supplied named parameter overloads + /// Bool indicating whether the type can be resolved + public bool CanResolve(NamedParameterOverloads parameters) + where ResolveType : class + { + return CanResolveInternal(new TypeRegistration(typeof(ResolveType)), parameters, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters and default options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// User supplied named parameter overloads + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name, NamedParameterOverloads parameters) + where ResolveType : class + { + return CanResolveInternal(new TypeRegistration(typeof(ResolveType), name), parameters, ResolveOptions.Default); + } + + /// + /// Attempts to predict whether a given type can be resolved with the supplied constructor parameters options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// User supplied named parameter overloads + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return CanResolveInternal(new TypeRegistration(typeof(ResolveType)), parameters, options); + } + + /// + /// Attempts to predict whether a given named type can be resolved with the supplied constructor parameters options. + /// + /// Parameters are used in conjunction with normal container resolution to find the most suitable constructor (if one exists). + /// All user supplied parameters must exist in at least one resolvable constructor of RegisterType or resolution will fail. + /// + /// Note: Resolution may still fail if user defined factory registations fail to construct objects when called. + /// + /// Type to resolve + /// Name of registration + /// User supplied named parameter overloads + /// Resolution options + /// Bool indicating whether the type can be resolved + public bool CanResolve(string name, NamedParameterOverloads parameters, ResolveOptions options) + where ResolveType : class + { + return CanResolveInternal(new TypeRegistration(typeof(ResolveType), name), parameters, options); + } + + /// + /// Attempts to resolve all public property dependencies on the given object. + /// + /// Object to "build up" + public void BuildUp(object input) + { + BuildUpInternal(input, ResolveOptions.Default); + } + + /// + /// Attempts to resolve all public property dependencies on the given object using the given resolve options. + /// + /// Object to "build up" + /// Resolve options to use + public void BuildUp(object input, ResolveOptions resolveOptions) + { + BuildUpInternal(input, resolveOptions); + } + #endregion + #endregion + + #region Object Factories + private abstract class ObjectFactoryBase + { + /// + /// Whether to assume this factory sucessfully constructs its objects + /// + /// Generally set to true for delegate style factories as CanResolve cannot delve + /// into the delegates they contain. + /// + public virtual bool AssumeConstruction { get { return false; } } + + /// + /// The type the factory instantiates + /// + public abstract Type CreatesType { get; } + + /// + /// Constructor to use, if specified + /// + public ConstructorInfo Constructor { get; protected set; } + + /// + /// Create the type + /// + /// Container that requested the creation + /// Any user parameters passed + /// + public abstract object GetObject(TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options); + + public virtual ObjectFactoryBase SingletonVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "singleton"); + } + } + + public virtual ObjectFactoryBase MultiInstanceVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "multi-instance"); + } + } + + public virtual ObjectFactoryBase StrongReferenceVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "strong reference"); + } + } + + public virtual ObjectFactoryBase WeakReferenceVariant + { + get + { + throw new TinyIoCRegistrationException(this.GetType(), "weak reference"); + } + } + + public virtual void SetConstructor(ConstructorInfo constructor) + { + this.Constructor = constructor; + } + } + + /// + /// IObjectFactory that creates new instances of types for each resolution + /// + /// Registered type + /// Type to construct to fullful request for RegisteredType + private class MultiInstanceFactory : ObjectFactoryBase + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + public override Type CreatesType { get { return typeof(RegisterImplementation); } } + + public MultiInstanceFactory() + { + if (typeof(RegisterImplementation).IsAbstract || typeof(RegisterImplementation).IsInterface) + throw new TinyIoCRegistrationTypeException(typeof(RegisterImplementation), "MultiInstanceFactory"); + } + + public override object GetObject(TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + try + { + return container.ConstructType(typeof(RegisterImplementation), Constructor, parameters, options); + } + catch (TinyIoCResolutionException ex) + { + throw new TinyIoCResolutionException(typeof(RegisterImplementation), ex); + } + } + + public override ObjectFactoryBase SingletonVariant + { + get + { + return new SingletonFactory(); + } + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + return this; + } + } + } + + /// + /// IObjectFactory that invokes a specified delegate to construct the object + /// + /// Registered type to be constructed + private class DelegateFactory : ObjectFactoryBase + where RegisterType : class + { + private Func _factory; + + public override bool AssumeConstruction { get { return true; } } + + public override Type CreatesType { get { return typeof(RegisterType); } } + + public override object GetObject(TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + try + { + return _factory.Invoke(container, parameters); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(typeof(RegisterType), ex); + } + } + + public DelegateFactory(Func factory) + { + if (factory == null) + throw new ArgumentNullException("factory"); + + _factory = factory; + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return new WeakDelegateFactory(_factory); + } + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + return this; + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for delegate factory registrations"); + } + } + + /// + /// IObjectFactory that invokes a specified delegate to construct the object + /// + /// Holds the delegate using a weak reference + /// + /// Registered type to be constructed + private class WeakDelegateFactory : ObjectFactoryBase + where RegisterType : class + { + private WeakReference _factory; + + public override bool AssumeConstruction { get { return true; } } + + public override Type CreatesType { get { return typeof(RegisterType); } } + + public override object GetObject(TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + var factory = _factory.Target as Func; + + if (factory == null) + throw new TinyIoCWeakReferenceException(typeof(RegisterType)); + + try + { + return factory.Invoke(container, parameters); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(typeof(RegisterType), ex); + } + } + + public WeakDelegateFactory(Func factory) + { + if (factory == null) + throw new ArgumentNullException("factory"); + + _factory = new WeakReference(factory); + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + var factory = _factory.Target as Func; + + if (factory == null) + throw new TinyIoCWeakReferenceException(typeof(RegisterType)); + + return new DelegateFactory(factory); + } + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return this; + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for delegate factory registrations"); + } + } + + /// + /// Stores an particular instance to return for a type + /// + /// Registered type + /// Type of the instance + private class InstanceFactory : ObjectFactoryBase, IDisposable + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + private RegisterImplementation _instance; + + public override bool AssumeConstruction { get { return true; } } + + public InstanceFactory(RegisterImplementation instance) + { + this._instance = instance; + } + + public override Type CreatesType + { + get { return typeof(RegisterImplementation); } + } + + public override object GetObject(TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + return _instance; + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + return new MultiInstanceFactory(); + } + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return new WeakInstanceFactory(_instance); + } + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + return this; + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for instance factory registrations"); + } + + public void Dispose() + { + var disposable = _instance as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + } + + /// + /// Stores an particular instance to return for a type + /// + /// Stores the instance with a weak reference + /// + /// Registered type + /// Type of the instance + private class WeakInstanceFactory : ObjectFactoryBase, IDisposable + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + private WeakReference _instance; + + public WeakInstanceFactory(RegisterImplementation instance) + { + this._instance = new WeakReference(instance); + } + + public override Type CreatesType + { + get { return typeof(RegisterImplementation); } + } + + public override object GetObject(TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + var instance = _instance.Target as RegisterImplementation; + + if (instance == null) + throw new TinyIoCWeakReferenceException(typeof(RegisterType)); + + return instance; + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + return new MultiInstanceFactory(); + } + } + + public override ObjectFactoryBase WeakReferenceVariant + { + get + { + return this; + } + } + + public override ObjectFactoryBase StrongReferenceVariant + { + get + { + var instance = _instance.Target as RegisterImplementation; + + if (instance == null) + throw new TinyIoCWeakReferenceException(typeof(RegisterType)); + + return new InstanceFactory(instance); + } + } + + public override void SetConstructor(ConstructorInfo constructor) + { + throw new TinyIoCConstructorResolutionException("Constructor selection is not possible for instance factory registrations"); + } + + public void Dispose() + { + var disposable = _instance.Target as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + } + + /// + /// A factory that lazy instantiates a type and always returns the same instance + /// + /// Registered type + /// Type to instantiate + private class SingletonFactory : ObjectFactoryBase, IDisposable + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + private readonly object SingletonLock = new object(); + private RegisterImplementation _Current; + + public SingletonFactory() + { + if (typeof(RegisterImplementation).IsAbstract || typeof(RegisterImplementation).IsInterface) + throw new TinyIoCRegistrationTypeException(typeof(RegisterImplementation), "SingletonFactory"); + } + + public override Type CreatesType + { + get { return typeof(RegisterImplementation); } + } + + public override object GetObject(TinyIoCContainer container, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters.Count != 0) + throw new ArgumentException("Cannot specify parameters for singleton types"); + + lock (SingletonLock) + if (_Current == null) + _Current = container.ConstructType(typeof(RegisterImplementation), Constructor, options) as RegisterImplementation; + + return _Current; + } + + public override ObjectFactoryBase SingletonVariant + { + get + { + return this; + } + } + + public override ObjectFactoryBase MultiInstanceVariant + { + get + { + return new MultiInstanceFactory(); + } + } + + public void Dispose() + { + if (_Current != null) + { + var disposable = _Current as IDisposable; + + if (disposable != null) + disposable.Dispose(); + } + } + } + #endregion + + #region Singleton Container + private static readonly TinyIoCContainer _Current = new TinyIoCContainer(); + + static TinyIoCContainer() + { + } + + /// + /// Lazy created Singleton instance of the container for simple scenarios + /// + public static TinyIoCContainer Current + { + get + { + return _Current; + } + } + #endregion + + #region Type Registrations + public sealed class TypeRegistration + { + public Type Type { get; private set; } + public string Name { get; private set; } + + public TypeRegistration(Type type) + : this(type, string.Empty) + { + } + + public TypeRegistration(Type type, string name) + { + Type = type; + Name = name; + } + + public override bool Equals(object obj) + { + var typeRegistration = obj as TypeRegistration; + + if (obj == null) + return false; + + if (this.Type != typeRegistration.Type) + return false; + + if (String.Compare(this.Name, typeRegistration.Name, true) != 0) + return false; + + return true; + } + + public override int GetHashCode() + { + return String.Format("{0}|{1}", this.Type.FullName, this.Name).GetHashCode(); + } + } + private readonly SafeDictionary _RegisteredTypes; + #endregion + + #region Constructors + public TinyIoCContainer() + { + _RegisteredTypes = new SafeDictionary(); + + RegisterDefaultTypes(); + } + #endregion + + #region Internal Methods + private readonly object _AutoRegisterLock = new object(); + private void AutoRegisterInternal(IEnumerable assemblies, bool ignoreDuplicateImplementations) + { + lock (_AutoRegisterLock) + { + var defaultFactoryMethod = this.GetType().GetMethod("GetDefaultObjectFactory", BindingFlags.NonPublic | BindingFlags.Instance); + + var types = assemblies.SelectMany(a => a.GetTypes()).Where(t => !IsIgnoredType(t)).ToList(); + + var sortedTypes = types.OrderBy(o => o.Name); + + var concreteTypes = from type in types + where (type.IsClass == true) && (type.IsAbstract == false) && (type != this.GetType() && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition)) + select type; + + foreach (var type in concreteTypes) + { + Type[] genericTypes = { type, type }; + var genericDefaultFactoryMethod = defaultFactoryMethod.MakeGenericMethod(genericTypes); + this.RegisterInternal(type, type, string.Empty, genericDefaultFactoryMethod.Invoke(this, null) as ObjectFactoryBase); + } + + var abstractInterfaceTypes = from type in types + where ((type.IsInterface == true || type.IsAbstract == true) && (type.DeclaringType != this.GetType()) && (!type.IsGenericTypeDefinition)) + select type; + + foreach (var type in abstractInterfaceTypes) + { + var implementations = from implementationType in concreteTypes + where implementationType.GetInterfaces().Contains(type) || implementationType.BaseType == type + select implementationType; + + if (!ignoreDuplicateImplementations && implementations.Count() > 1) + throw new TinyIoCAutoRegistrationException(type, implementations); + + var firstImplementation = implementations.FirstOrDefault(); + if (firstImplementation != null) + { + Type[] genericTypes = { type, firstImplementation }; + var genericDefaultFactoryMethod = defaultFactoryMethod.MakeGenericMethod(genericTypes); + this.RegisterInternal(type, firstImplementation, string.Empty, genericDefaultFactoryMethod.Invoke(this, null) as ObjectFactoryBase); + } + } + } + } + + private bool IsIgnoredType(Type type) + { + // TODO - find a better way to remove "system" types from the auto registration + if (type.FullName.StartsWith("System.") || type.FullName.StartsWith("Microsoft.")) + return true; + + if (type.IsPrimitive) + return true; + + if ((type.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Length == 0) && !(type.IsInterface || type.IsAbstract)) + return true; + + return false; + } + + private void RegisterDefaultTypes() + { + this.Register(this); +#if TINYMESSENGER + if (parent == null) + this.Register(); +#endif + } + + private ObjectFactoryBase GetCurrentFactory(TypeRegistration registration) + { + ObjectFactoryBase current = null; + + _RegisteredTypes.TryGetValue(registration, out current); + + return current; + } + + private RegisterOptions RegisterInternal(Type registerType, Type registerImplementation, string name, ObjectFactoryBase factory) + { + var typeRegistration = new TypeRegistration(registerType, name); + + return AddUpdateRegistration(typeRegistration, factory); + } + + private RegisterOptions AddUpdateRegistration(TypeRegistration typeRegistration, ObjectFactoryBase factory) + { + _RegisteredTypes[typeRegistration] = factory; + + return new RegisterOptions(this, typeRegistration); + } + + private void RemoveRegistration(TypeRegistration typeRegistration) + { + _RegisteredTypes.Remove(typeRegistration); + } + + private ObjectFactoryBase GetDefaultObjectFactory() + where RegisterType : class + where RegisterImplementation : class, RegisterType + { + if (typeof(RegisterType).IsInterface || typeof(RegisterType).IsAbstract) + return new SingletonFactory(); + + return new MultiInstanceFactory(); + } + + private bool CanResolveInternal(TypeRegistration registration, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + Type checkType = registration.Type; + string name = registration.Name; + + ObjectFactoryBase factory; + if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType, name), out factory)) + { + if (factory.AssumeConstruction) + return true; + + if (factory.Constructor == null) + return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; + else + return CanConstruct(factory.Constructor, parameters, options); + } + + // Fail if requesting named resolution and settings set to fail if unresolved + if (!String.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.Fail) + return false; + + // Attemped unnamed fallback container resolution if relevant and requested + if (!String.IsNullOrEmpty(name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.AttemptUnnamedResolution) + { + if (_RegisteredTypes.TryGetValue(new TypeRegistration(checkType), out factory)) + { + if (factory.AssumeConstruction) + return true; + + return (GetBestConstructor(factory.CreatesType, parameters, options) != null) ? true : false; + } + } + + // Check if type is an automatic lazy factory request + if (IsAutomaticLazyFactoryRequest(checkType)) + return true; + + // Attempt unregistered construction if possible and requested + if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (checkType.IsGenericType && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) + return (GetBestConstructor(checkType, parameters, options) != null) ? true : false; + + return false; + } + + private bool IsAutomaticLazyFactoryRequest(Type type) + { + if (!type.IsGenericType) + return false; + + Type genericType = type.GetGenericTypeDefinition(); + + // Just a func + if (genericType == typeof(Func<>)) + return true; + + // 2 parameter func with string as first parameter (name) + if ((genericType == typeof(Func<,>) && type.GetGenericArguments()[0] == typeof(string))) + return true; + + // 3 parameter func with string as first parameter (name) and IDictionary as second (parameters) + if ((genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) && type.GetGenericArguments()[1] == typeof(IDictionary))) + return true; + + return false; + } + + private object ResolveInternal(TypeRegistration registration, NamedParameterOverloads parameters, ResolveOptions options) + { + ObjectFactoryBase factory; + + // Attempt container resolution + if (_RegisteredTypes.TryGetValue(registration, out factory)) + { + try + { + return factory.GetObject(this, parameters, options); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(registration.Type, ex); + } + } + + // Fail if requesting named resolution and settings set to fail if unresolved + if (!String.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.Fail) + throw new TinyIoCResolutionException(registration.Type); + + // Attemped unnamed fallback container resolution if relevant and requested + if (!String.IsNullOrEmpty(registration.Name) && options.NamedResolutionFailureAction == NamedResolutionFailureActions.AttemptUnnamedResolution) + { + if (_RegisteredTypes.TryGetValue(new TypeRegistration(registration.Type, string.Empty), out factory)) + { + try + { + return factory.GetObject(this, parameters, options); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(registration.Type, ex); + } + } + } + + // Attempt to construct an automatic lazy factory if possible + if (IsAutomaticLazyFactoryRequest(registration.Type)) + return GetLazyAutomaticFactoryRequest(registration.Type); + + // Attempt unregistered construction if possible and requested + if ((options.UnregisteredResolutionAction == UnregisteredResolutionActions.AttemptResolve) || (registration.Type.IsGenericType && options.UnregisteredResolutionAction == UnregisteredResolutionActions.GenericsOnly)) + { + if (!registration.Type.IsAbstract && !registration.Type.IsInterface) + return ConstructType(registration.Type, parameters, options); + } + + // Unable to resolve - throw + throw new TinyIoCResolutionException(registration.Type); + } + + private object GetLazyAutomaticFactoryRequest(Type type) + { + if (!type.IsGenericType) + return null; + + Type genericType = type.GetGenericTypeDefinition(); + Type[] genericArguments = type.GetGenericArguments(); + + // Just a func + if (genericType == typeof(Func<>)) + { + Type returnType = genericArguments[0]; + + MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { }); + resolveMethod = resolveMethod.MakeGenericMethod(returnType); + + var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod); + + var resolveLambda = Expression.Lambda(resolveCall).Compile(); + + return resolveLambda; + } + + // 2 parameter func with string as first parameter (name) + if ((genericType == typeof(Func<,>)) && (genericArguments[0] == typeof(string))) + { + Type returnType = genericArguments[1]; + + MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { typeof(String) }); + resolveMethod = resolveMethod.MakeGenericMethod(returnType); + + ParameterExpression[] resolveParameters = new ParameterExpression[] { Expression.Parameter(typeof(String), "name") }; + var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod, resolveParameters); + + var resolveLambda = Expression.Lambda(resolveCall, resolveParameters).Compile(); + + return resolveLambda; + } + + // 3 parameter func with string as first parameter (name) and IDictionary as second (parameters) + if ((genericType == typeof(Func<,,>) && type.GetGenericArguments()[0] == typeof(string) && type.GetGenericArguments()[1] == typeof(IDictionary))) + { + Type returnType = genericArguments[2]; + + var name = Expression.Parameter(typeof(string), "name"); + var parameters = Expression.Parameter(typeof(IDictionary), "parameters"); + + MethodInfo resolveMethod = typeof(TinyIoCContainer).GetMethod("Resolve", new Type[] { typeof(String), typeof(NamedParameterOverloads) }); + resolveMethod = resolveMethod.MakeGenericMethod(returnType); + + var resolveCall = Expression.Call(Expression.Constant(this), resolveMethod, name, Expression.Call(typeof(NamedParameterOverloads), "FromIDictionary", null, parameters)); + + var resolveLambda = Expression.Lambda(resolveCall, name, parameters).Compile(); + + return resolveLambda; + } + + throw new TinyIoCResolutionException(type); + } + + private bool CanConstruct(ConstructorInfo ctor, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + foreach (var parameter in ctor.GetParameters()) + { + if (string.IsNullOrEmpty(parameter.Name)) + return false; + + var isParameterOverload = parameters.ContainsKey(parameter.Name); + + if (parameter.ParameterType.IsPrimitive && !isParameterOverload) + return false; + + if (!isParameterOverload && !CanResolveInternal(new TypeRegistration(parameter.ParameterType), NamedParameterOverloads.Default, options)) + return false; + } + + return true; + } + + private ConstructorInfo GetBestConstructor(Type type, NamedParameterOverloads parameters, ResolveOptions options) + { + if (parameters == null) + throw new ArgumentNullException("parameters"); + + if (type.IsValueType) + return null; + + // Get constructors in reverse order based on the number of parameters + // i.e. be as "greedy" as possible so we satify the most amount of dependencies possible + var ctors = from ctor in type.GetConstructors() + orderby ctor.GetParameters().Count() descending + select ctor; + + foreach (var ctor in ctors) + { + if (CanConstruct(ctor, parameters, options)) + return ctor; + } + + return null; + } + + private object ConstructType(Type type, ResolveOptions options) + { + return ConstructType(type, null, NamedParameterOverloads.Default, options); + } + + private object ConstructType(Type type, ConstructorInfo constructor, ResolveOptions options) + { + return ConstructType(type, constructor, NamedParameterOverloads.Default, options); + } + + private object ConstructType(Type type, NamedParameterOverloads parameters, ResolveOptions options) + { + return ConstructType(type, null, parameters, options); + } + + private object ConstructType(Type type, ConstructorInfo constructor, NamedParameterOverloads parameters, ResolveOptions options) + { + if (constructor == null) + constructor = GetBestConstructor(type, parameters, options); + + if (constructor == null) + throw new TinyIoCResolutionException(type); + + var ctorParams = constructor.GetParameters(); + object[] args = new object[ctorParams.Count()]; + + for (int parameterIndex = 0; parameterIndex < ctorParams.Count(); parameterIndex++) + { + var currentParam = ctorParams[parameterIndex]; + + args[parameterIndex] = parameters.ContainsKey(currentParam.Name) ? parameters[currentParam.Name] : ResolveInternal(new TypeRegistration(currentParam.ParameterType), NamedParameterOverloads.Default, options); + } + + try + { + return constructor.Invoke(args); + } + catch (Exception ex) + { + throw new TinyIoCResolutionException(type, ex); + } + } + + private void BuildUpInternal(object input, ResolveOptions resolveOptions) + { + var properties = from property in input.GetType().GetProperties() + where (property.GetGetMethod() != null) && (property.GetSetMethod() != null) && !property.PropertyType.IsValueType + select property; + + foreach (var property in properties) + { + if (property.GetValue(input, null) == null) + { + try + { + property.SetValue(input, ResolveInternal(new TypeRegistration(property.PropertyType), NamedParameterOverloads.Default, resolveOptions), null); + } + catch (Exception TinyIoCResolutionException) + { + // Catch any resolution errors and ignore them + } + } + } + } + #endregion + + #region IDisposable Members + public void Dispose() + { + _RegisteredTypes.Dispose(); + } + + #endregion + } +} diff -r 0b294eb35ad2b5d59594af0aedfa39a80eec7f58 -r efb2e93c1d9cfea3cdfb5ae96b0b2ad6a2ec9c55 TinyIoC/TinyIoC.csproj --- a/TinyIoC/TinyIoC.csproj Thu Mar 11 17:26:31 2010 +0000 +++ b/TinyIoC/TinyIoC.csproj Sun Mar 14 19:00:54 2010 +0000 @@ -47,6 +47,7 @@ +