From: <jer...@us...> - 2009-04-19 18:40:59
|
Revision: 235 http://structuremap.svn.sourceforge.net/structuremap/?rev=235&view=rev Author: jeremydmiller Date: 2009-04-19 18:40:49 +0000 (Sun, 19 Apr 2009) Log Message: ----------- Making StructureMap throw a clean error when a cyclic dependency is detected Modified Paths: -------------- trunk/Source/StructureMap/BuildSession.cs trunk/Source/StructureMap/CloseGenericTypeExpression.cs trunk/Source/StructureMap/Container.cs trunk/Source/StructureMap/IInstanceFactory.cs trunk/Source/StructureMap/InstanceCache.cs trunk/Source/StructureMap/InstanceFactory.cs trunk/Source/StructureMap/Pipeline/BuildFrame.cs trunk/Source/StructureMap/Pipeline/BuildStack.cs trunk/Source/StructureMap/Pipeline/Instance.cs trunk/Source/StructureMap/StructureMap.csproj trunk/Source/StructureMap/StructureMapException.resx trunk/Source/StructureMap/TypeExtensions.cs trunk/Source/StructureMap.Testing/Pipeline/BuildStackTester.cs trunk/Source/StructureMap.Testing/Pipeline/GenericsHelperExpressionTester.cs trunk/Source/StructureMap.Testing/StructureMap.Testing.csproj Added Paths: ----------- trunk/Source/StructureMap/IContext.cs trunk/Source/StructureMap.Testing/BidirectionalDependencies.cs Modified: trunk/Source/StructureMap/BuildSession.cs =================================================================== --- trunk/Source/StructureMap/BuildSession.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/BuildSession.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -1,5 +1,4 @@ using System; -using System.Collections; using StructureMap.Graph; using StructureMap.Interceptors; using StructureMap.Pipeline; @@ -7,68 +6,6 @@ namespace StructureMap { - public interface IContext - { - /// <summary> - /// Gets a reference to the <see cref="BuildStack">BuildStack</see> for this build session - /// </summary> - BuildStack BuildStack { get; } - - /// <summary> - /// The concrete type of the immediate parent object in the object graph - /// </summary> - Type ParentType { get; } - - /// <summary> - /// Get the object of type T that is valid for this build session. - /// </summary> - /// <typeparam name="T"></typeparam> - /// <returns></returns> - T GetInstance<T>(); - - /// <summary> - /// Get the object of type T that is valid for this build session by name. - /// </summary> - /// <typeparam name="T"></typeparam> - /// <returns></returns> - T GetInstance<T>(string name); - - /// <summary> - /// Gets the root "frame" of the object request - /// </summary> - BuildFrame Root { get; } - - /// <summary> - /// The requested instance name of the object graph - /// </summary> - string RequestedName { get; } - - /// <summary> - /// Register a default object for the given PluginType that will - /// be used throughout the rest of the current object request - /// </summary> - /// <param name="pluginType"></param> - /// <param name="defaultObject"></param> - void RegisterDefault(Type pluginType, object defaultObject); - - /// <summary> - /// Same as GetInstance, but can gracefully return null if - /// the Type does not already exist - /// </summary> - /// <typeparam name="T"></typeparam> - /// <returns></returns> - T TryGetInstance<T>() where T : class; - - /// <summary> - /// Same as GetInstance(name), but can gracefully return null if - /// the Type and name does not already exist - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="name"></param> - /// <returns></returns> - T TryGetInstance<T>(string name) where T : class; - } - public class BuildSession : IContext { private readonly BuildStack _buildStack = new BuildStack(); @@ -102,13 +39,12 @@ { } - public BuildSession() : this(new PluginGraph()) + public BuildSession() + : this(new PluginGraph()) { } - public string RequestedName { get; set; } - protected PipelineGraph pipelineGraph { get { return _pipelineGraph; } @@ -116,6 +52,12 @@ #region IContext Members + public string RequestedName + { + get; + set; + } + public BuildStack BuildStack { get { return _buildStack; } @@ -145,6 +87,28 @@ get { return _buildStack.Root; } } + public virtual void RegisterDefault(Type pluginType, object defaultObject) + { + RegisterDefault(pluginType, () => defaultObject); + } + + public T TryGetInstance<T>() where T : class + { + if (_defaults.Has(typeof (T))) + { + return (T) _defaults[typeof (T)](); + } + + return _pipelineGraph.HasDefaultForPluginType(typeof (T)) + ? ((IContext) this).GetInstance<T>() + : null; + } + + public T TryGetInstance<T>(string name) where T : class + { + return _pipelineGraph.HasInstance(typeof (T), name) ? ((IContext) this).GetInstance<T>(name) : null; + } + #endregion public virtual object CreateInstance(Type pluginType, string name) @@ -158,6 +122,7 @@ return CreateInstance(pluginType, instance); } + // This is where all Creation happens public virtual object CreateInstance(Type pluginType, Instance instance) { object result = _cache.Get(pluginType, instance); @@ -175,27 +140,17 @@ public virtual Array CreateInstanceArray(Type pluginType, Instance[] instances) { - Array array; - if (instances == null) { - IList list = forType(pluginType).GetAllInstances(this); - array = Array.CreateInstance(pluginType, list.Count); - for (int i = 0; i < list.Count; i++) - { - array.SetValue(list[i], i); - } + instances = forType(pluginType).AllInstances; } - else - { - array = Array.CreateInstance(pluginType, instances.Length); - for (int i = 0; i < instances.Length; i++) - { - Instance instance = instances[i]; - object arrayValue = forType(pluginType).Build(this, instance); - array.SetValue(arrayValue, i); - } + Array array = Array.CreateInstance(pluginType, instances.Length); + for (int i = 0; i < instances.Length; i++) + { + Instance instance = instances[i]; + object arrayValue = CreateInstance(pluginType, instance); + array.SetValue(arrayValue, i); } return array; @@ -212,37 +167,15 @@ return _interceptorLibrary.FindInterceptor(actualValue.GetType()).Process(actualValue, this); } - public virtual void RegisterDefault(Type pluginType, object defaultObject) - { - RegisterDefault(pluginType, () => defaultObject); - } - public virtual void RegisterDefault(Type pluginType, Func<object> creator) { _defaults[pluginType] = creator; } - public T TryGetInstance<T>() where T : class - { - if (_defaults.Has(typeof(T))) - { - return (T) _defaults[typeof (T)](); - } - return _pipelineGraph.HasDefaultForPluginType(typeof (T)) - ? ((IContext) this).GetInstance<T>() - : null; - } - - public T TryGetInstance<T>(string name) where T : class - { - return _pipelineGraph.HasInstance(typeof (T), name) ? ((IContext) this).GetInstance<T>(name) : null; - } - - private IInstanceFactory forType(Type pluginType) { return _pipelineGraph.ForType(pluginType); } } -} +} \ No newline at end of file Modified: trunk/Source/StructureMap/CloseGenericTypeExpression.cs =================================================================== --- trunk/Source/StructureMap/CloseGenericTypeExpression.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/CloseGenericTypeExpression.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -1,4 +1,6 @@ using System; +using System.Collections; +using System.Collections.Generic; namespace StructureMap { @@ -7,8 +9,13 @@ T As<T>(); } - public class CloseGenericTypeExpression : OpenGenericTypeSpecificationExpression + public interface OpenGenericTypeListSpecificationExpression { + IList<T> As<T>(); + } + + public class CloseGenericTypeExpression : OpenGenericTypeSpecificationExpression, OpenGenericTypeListSpecificationExpression + { private readonly object _subject; private readonly IContainer _container; private Type _pluginType; @@ -26,13 +33,18 @@ /// <returns></returns> public OpenGenericTypeSpecificationExpression GetClosedTypeOf(Type type) { + closeType(type); + return this; + } + + private void closeType(Type type) + { if (!type.IsGeneric()) { throw new StructureMapException(285); } _pluginType = type.MakeGenericType(_subject.GetType()); - return this; } /// <summary> @@ -44,5 +56,23 @@ { return (T) _container.With(_subject.GetType(), _subject).GetInstance(_pluginType); } + + public OpenGenericTypeListSpecificationExpression GetAllClosedTypesOf(Type type) + { + closeType(type); + return this; + } + + IList<T> OpenGenericTypeListSpecificationExpression.As<T>() + { + IList list = _container.With(_subject.GetType(), _subject).GetAllInstances(_pluginType); + var returnValue = new List<T>(); + foreach (var o in list) + { + returnValue.Add((T) o); + } + + return returnValue; + } } } \ No newline at end of file Modified: trunk/Source/StructureMap/Container.cs =================================================================== --- trunk/Source/StructureMap/Container.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/Container.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -11,7 +11,6 @@ namespace StructureMap { - public class Container : TypeRules, IContainer { private InterceptorLibrary _interceptorLibrary; @@ -147,7 +146,8 @@ args.RegisterDefaults(session); - return forType(type).GetAllInstances(session); + var instances = session.CreateInstanceArray(type, null); + return new ArrayList(instances); } @@ -365,7 +365,8 @@ /// <returns></returns> public IList GetAllInstances(Type pluginType) { - return forType(pluginType).GetAllInstances(withNewSession(Plugin.DEFAULT)); + var instances = withNewSession(Plugin.DEFAULT).CreateInstanceArray(pluginType, null); + return new ArrayList(instances); } /// <summary> @@ -482,7 +483,7 @@ private IList<T> getListOfTypeWithSession<T>(BuildSession session) { var list = new List<T>(); - foreach (T instance in forType(typeof (T)).GetAllInstances(session)) + foreach (T instance in session.CreateInstanceArray(typeof(T), null)) { list.Add(instance); } @@ -505,12 +506,6 @@ return new BuildSession(_pipelineGraph, _interceptorLibrary){RequestedName = name}; } - - protected IInstanceFactory forType(Type type) - { - return _pipelineGraph.ForType(type); - } - /// <summary> /// Convenience method to request an object using an Open Generic /// Type and its parameter Types Added: trunk/Source/StructureMap/IContext.cs =================================================================== --- trunk/Source/StructureMap/IContext.cs (rev 0) +++ trunk/Source/StructureMap/IContext.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -0,0 +1,67 @@ +using System; +using StructureMap.Pipeline; + +namespace StructureMap +{ + public interface IContext + { + /// <summary> + /// Gets a reference to the <see cref="BuildStack">BuildStack</see> for this build session + /// </summary> + BuildStack BuildStack { get; } + + /// <summary> + /// The concrete type of the immediate parent object in the object graph + /// </summary> + Type ParentType { get; } + + /// <summary> + /// Get the object of type T that is valid for this build session. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns></returns> + T GetInstance<T>(); + + /// <summary> + /// Get the object of type T that is valid for this build session by name. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns></returns> + T GetInstance<T>(string name); + + /// <summary> + /// Gets the root "frame" of the object request + /// </summary> + BuildFrame Root { get; } + + /// <summary> + /// The requested instance name of the object graph + /// </summary> + string RequestedName { get; } + + /// <summary> + /// Register a default object for the given PluginType that will + /// be used throughout the rest of the current object request + /// </summary> + /// <param name="pluginType"></param> + /// <param name="defaultObject"></param> + void RegisterDefault(Type pluginType, object defaultObject); + + /// <summary> + /// Same as GetInstance, but can gracefully return null if + /// the Type does not already exist + /// </summary> + /// <typeparam name="T"></typeparam> + /// <returns></returns> + T TryGetInstance<T>() where T : class; + + /// <summary> + /// Same as GetInstance(name), but can gracefully return null if + /// the Type and name does not already exist + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="name"></param> + /// <returns></returns> + T TryGetInstance<T>(string name) where T : class; + } +} \ No newline at end of file Modified: trunk/Source/StructureMap/IInstanceFactory.cs =================================================================== --- trunk/Source/StructureMap/IInstanceFactory.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/IInstanceFactory.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -16,15 +16,23 @@ IBuildPolicy Policy { get; } Instance MissingInstance { get; set; } + Instance[] AllInstances + { + get; + } + void AddInstance(Instance instance); Instance AddType<T>(); + [Obsolete("Return the list of Instances instead")] IList GetAllInstances(BuildSession session); + object Build(BuildSession session, Instance instance); Instance FindInstance(string name); - void ForEachInstance(Action<Instance> action); void ImportFrom(PluginFamily family); void EjectAllInstances(); + + } } \ No newline at end of file Modified: trunk/Source/StructureMap/InstanceCache.cs =================================================================== --- trunk/Source/StructureMap/InstanceCache.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/InstanceCache.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -39,6 +39,14 @@ public void Set(Type pluginType, Instance Instance, object result) { Dictionary<Instance, object> cache = getCache(pluginType); + + if (cache.ContainsKey(Instance)) + { + string message = string.Format("Duplicate Objects detected for Instance {0} of Type {1}", Instance.Name, + pluginType.AssemblyQualifiedName); + throw new ApplicationException(message); + + } cache.Add(Instance, result); } } Modified: trunk/Source/StructureMap/InstanceFactory.cs =================================================================== --- trunk/Source/StructureMap/InstanceFactory.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/InstanceFactory.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -87,16 +87,20 @@ get { return _instances.GetAll(); } } + public Instance[] AllInstances + { + get + { + return _instances.GetAll(); + } + } + public IBuildPolicy Policy { get { return _policy; } set { _policy = value; } } - public void ForEachInstance(Action<Instance> action) - { - _instances.Each(action); - } public void AddInstance(Instance instance) { Modified: trunk/Source/StructureMap/Pipeline/BuildFrame.cs =================================================================== --- trunk/Source/StructureMap/Pipeline/BuildFrame.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/Pipeline/BuildFrame.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -115,5 +115,30 @@ return result; } } + + public bool Contains(BuildFrame frame) + { + if (_requestedType == frame._requestedType && _name == frame._name) + { + return true; + } + + return _next == null ? false : _next.Contains(frame); + } + + public string ToStackString() + { + string message = "\n1.) " + ToString(); + var next = _next; + + int i = 2; + while (next != null) + { + message += "\n{0}.) {1}".ToFormat(i++, next); + next = next._next; + } + + return message; + } } } \ No newline at end of file Modified: trunk/Source/StructureMap/Pipeline/BuildStack.cs =================================================================== --- trunk/Source/StructureMap/Pipeline/BuildStack.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/Pipeline/BuildStack.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -1,3 +1,5 @@ +using System; + namespace StructureMap.Pipeline { /// <summary> @@ -45,6 +47,13 @@ } else { + if (_root.Contains(frame)) + { + + + throw new StructureMapException(295, frame.ToString(), _root.ToStackString()); + } + _current.Attach(frame); _current = frame; } Modified: trunk/Source/StructureMap/Pipeline/Instance.cs =================================================================== --- trunk/Source/StructureMap/Pipeline/Instance.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/Pipeline/Instance.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using StructureMap.Diagnostics; using StructureMap.Graph; using StructureMap.Interceptors; @@ -124,7 +125,8 @@ { if (!doesRecordOnTheStack) return; - session.BuildStack.Push(new BuildFrame(pluginType, Name, getConcreteType(pluginType))); + var frame = new BuildFrame(pluginType, Name, getConcreteType(pluginType)); + session.BuildStack.Push(frame); } private object createRawObject(Type pluginType, BuildSession session) Modified: trunk/Source/StructureMap/StructureMap.csproj =================================================================== --- trunk/Source/StructureMap/StructureMap.csproj 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/StructureMap.csproj 2009-04-19 18:40:49 UTC (rev 235) @@ -405,6 +405,7 @@ <Compile Include="Graph\ITypeScanner.cs" /> <Compile Include="Graph\PluggableAttributeScanner.cs" /> <Compile Include="Graph\PluginCache.cs" /> + <Compile Include="IContext.cs" /> <Compile Include="Pipeline\ConditionalInstance.cs" /> <Compile Include="Pipeline\HttpSessionBuildPolicy.cs" /> <Compile Include="Pipeline\SessionWrapper.cs" /> Modified: trunk/Source/StructureMap/StructureMapException.resx =================================================================== --- trunk/Source/StructureMap/StructureMapException.resx 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/StructureMapException.resx 2009-04-19 18:40:49 UTC (rev 235) @@ -271,4 +271,7 @@ <data name="290" xml:space="preserve"> <value>Could not load the designated Registry class '{0}'</value> </data> + <data name="295" xml:space="preserve"> + <value>Bidirectional Dependency Problem detected with {0}. The BuildStack is: {1}</value> + </data> </root> \ No newline at end of file Modified: trunk/Source/StructureMap/TypeExtensions.cs =================================================================== --- trunk/Source/StructureMap/TypeExtensions.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap/TypeExtensions.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -72,6 +72,16 @@ return pluggedType.BaseType == typeof(object) ? null : pluggedType.BaseType.FindInterfaceThatCloses(templateType); } + public static bool IsNullable(this Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof (Nullable<>); + } + + public static Type GetInnerTypeFromNullable(this Type nullableType) + { + return nullableType.GetGenericArguments()[0]; + } + public static string GetName(this Type type) { if (type.IsGenericType) Added: trunk/Source/StructureMap.Testing/BidirectionalDependencies.cs =================================================================== --- trunk/Source/StructureMap.Testing/BidirectionalDependencies.cs (rev 0) +++ trunk/Source/StructureMap.Testing/BidirectionalDependencies.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -0,0 +1,68 @@ +using System; +using System.Diagnostics; +using NUnit.Framework; + +namespace StructureMap.Testing +{ + [TestFixture] public class BidirectionalDependencies + { + private Container container; + + [SetUp] public void SetUp() + { + container = new Container(x => + { + x.ForRequestedType<IBiView>().TheDefaultIsConcreteType<BiView>(); + x.ForRequestedType<IBiPresenter>().TheDefaultIsConcreteType<BiPresenter>(); + }); + } + + [Test] public void do_not_blow_up_with_a_stack_overflow_problem() + { + try + { + container.GetInstance<BiView>(); + Assert.Fail("Should have thrown error"); + } + catch (StructureMapException e) + { + Debug.WriteLine(e.ToString()); + e.ErrorCode.ShouldEqual(295); + } + } + } + + + public interface IBiView{} + public interface IBiPresenter{} + + public class BiView : IBiView + { + private readonly IBiPresenter _presenter; + + public BiView(IBiPresenter presenter) + { + _presenter = presenter; + } + + public IBiPresenter Presenter + { + get { return _presenter; } + } + } + + public class BiPresenter : IBiPresenter + { + private readonly IBiView _view; + + public BiPresenter(IBiView view) + { + _view = view; + } + + public IBiView View + { + get { return _view; } + } + } +} \ No newline at end of file Modified: trunk/Source/StructureMap.Testing/Pipeline/BuildStackTester.cs =================================================================== --- trunk/Source/StructureMap.Testing/Pipeline/BuildStackTester.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap.Testing/Pipeline/BuildStackTester.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -43,4 +43,26 @@ stack.Current.ShouldBeTheSameAs(root); } } + + [TestFixture] public class when_using_build_frame_contains + { + [SetUp] public void SetUp() + { + + } + + [Test] public void true_if_matching() + { + var frame1 = new BuildFrame(typeof (IWidget), "red", typeof (ColorWidget)); + var frame2 = new BuildFrame(typeof (IWidget), "red", typeof (ColorWidget)); + var frame3 = new BuildFrame(typeof (IWidget), "green", typeof (ColorWidget)); + + frame1.Contains(frame2).ShouldBeTrue(); + frame1.Contains(frame3).ShouldBeFalse(); + + frame3.Attach(frame2); + + frame3.Contains(frame1).ShouldBeTrue(); + } + } } \ No newline at end of file Modified: trunk/Source/StructureMap.Testing/Pipeline/GenericsHelperExpressionTester.cs =================================================================== --- trunk/Source/StructureMap.Testing/Pipeline/GenericsHelperExpressionTester.cs 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap.Testing/Pipeline/GenericsHelperExpressionTester.cs 2009-04-19 18:40:49 UTC (rev 235) @@ -1,3 +1,4 @@ +using System.Collections.Generic; using NUnit.Framework; namespace StructureMap.Testing.Pipeline @@ -182,6 +183,30 @@ } } + [TestFixture] + public class when_getting_all_closed_type_from_an_open_generic_type_by_providing_an_input_parameter + { + [Test] + public void fetch_the_objects() + { + var container = new Container(x => + { + x.ForRequestedType<IHandler<Shipment>>().TheDefaultIsConcreteType<ShipmentHandler>(); + x.ForRequestedType<IHandler<Shipment>>().AddConcreteType<ShipmentHandler2>(); + }); + + var shipment = new Shipment(); + + IList<IHandler> handlers = container + .ForObject(shipment) + .GetAllClosedTypesOf(typeof(IHandler<>)) + .As<IHandler>(); + + handlers[0].ShouldBeOfType<ShipmentHandler>(); + handlers[1].ShouldBeOfType<ShipmentHandler2>(); + } + } + public class Shipment { @@ -211,4 +236,19 @@ get { return _shipment; } } } + + public class ShipmentHandler2 : IHandler<Shipment> + { + private readonly Shipment _shipment; + + public ShipmentHandler2(Shipment shipment) + { + _shipment = shipment; + } + + public Shipment Shipment + { + get { return _shipment; } + } + } } \ No newline at end of file Modified: trunk/Source/StructureMap.Testing/StructureMap.Testing.csproj =================================================================== --- trunk/Source/StructureMap.Testing/StructureMap.Testing.csproj 2009-02-24 03:18:29 UTC (rev 234) +++ trunk/Source/StructureMap.Testing/StructureMap.Testing.csproj 2009-04-19 18:40:49 UTC (rev 235) @@ -177,6 +177,7 @@ <Compile Include="AutoMocking\RhinoAutoMockerTester.cs" /> <Compile Include="AutoMocking\RhinoMockRepositoryProxyTester.cs" /> <Compile Include="AutoWiringExamples.cs" /> + <Compile Include="BidirectionalDependencies.cs" /> <Compile Include="Bugs\BuildUpBug.cs" /> <Compile Include="Bugs\HttpSessionNullRefBug.cs" /> <Compile Include="Bugs\IDictionaryAndXmlBugTester.cs" /> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |