Skip to content

Commit

Permalink
Resolve or remove ambiguities.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jamie Briant committed Nov 8, 2010
1 parent 1a57309 commit ffbcf03
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 24 deletions.
82 changes: 58 additions & 24 deletions BinaryFinery.IOC.Runtime/Build/ContextFactory.cs
Expand Up @@ -76,7 +76,6 @@ public Type TypeForProperty(string property)

public Type ImplementationTypeForProperty(string property)
{

// find the most recent declaration.

var ti = contextType;
Expand All @@ -85,10 +84,10 @@ public Type ImplementationTypeForProperty(string property)
Type guess = null;
Type imp = null;
Type impContext = null;
while ( i < ifaces.Length)
while (true)
{
var info = ti.GetProperty(property);
if (info != null )
if (info != null)
{
if (guess == null)
{
Expand All @@ -105,16 +104,18 @@ public Type ImplementationTypeForProperty(string property)
impContext = ti;
if (!guess.IsAssignableFrom(imp))
{
throw new ImplementationInterfaceMismatchException(contextType, imp, guess,ti);
throw new ImplementationInterfaceMismatchException(contextType, imp, guess, ti);
}
}
else
{
if ( !timp.IsAssignableFrom(imp))
if (!timp.IsAssignableFrom(imp))
throw new ImplementationsMismatchException(contextType, imp, impContext, timp, ti);
}
}
}
if (i >= ifaces.Length)
break;
ti = ifaces[i];
++i;
}
Expand All @@ -123,7 +124,7 @@ public Type ImplementationTypeForProperty(string property)

public object ObjectForProperty(string propertyName)
{
if ( currentRun == null)
if (currentRun == null)
{
currentRun = new Queue<String>();
}
Expand All @@ -147,8 +148,7 @@ public object ObjectForProperty(string propertyName)
object[] args = new object[parameters.Length];
for (int i = 0; i < args.Length; ++i)
{
PropertyInfo pi = PropertyForType(parameters[i].ParameterType,propertyName);

PropertyInfo pi = PropertyForType(parameters[i].ParameterType, propertyName);
args[i] = ObjectForProperty(pi.Name);
}

Expand All @@ -172,11 +172,12 @@ private PropertyInfo PropertyForType(Type parameterType, string dependentPropert
while (i >= 0)
{
--i;
Type iface = i >=0 ? ifaces[i] : contextType;
Type iface = i >= 0 ? ifaces[i] : contextType;
var pi = iface.GetProperty(dependentProperty);
if (pi != null)
{
if (!pi.PropertyType.IsInterface || pi.GetCustomAttributes(typeof(ImplementationAttribute), false).Length>0)
if (!pi.PropertyType.IsInterface ||
pi.GetCustomAttributes(typeof(ImplementationAttribute), false).Length > 0)
{
// found a possible match.
var dt = PropertyForType(parameterType, iface);
Expand All @@ -185,26 +186,38 @@ private PropertyInfo PropertyForType(Type parameterType, string dependentPropert
}
}
}
return null;
throw new PropertyDependencyResolutionException(contextType, dependentProperty, parameterType);
}

private PropertyInfo PropertyForType(Type parameterType, Type startingContext)
{

var props =
startingContext.GetProperties(BindingFlags.Public | BindingFlags.FlattenHierarchy |
BindingFlags.Instance);
foreach (var propertyInfo in props)
var ti = startingContext;
Type[] ifaces = startingContext.GetInterfaces();
int i = 0;
while (true)
{
if (propertyInfo.PropertyType == parameterType ||
parameterType.IsAssignableFrom(propertyInfo.PropertyType))
{
return propertyInfo;
}
Type impType = ImplementationTypeForProperty(propertyInfo.Name);
if (impType == parameterType || parameterType.IsAssignableFrom(impType))

var props =
ti.GetProperties(BindingFlags.Public | BindingFlags.FlattenHierarchy |
BindingFlags.Instance);
foreach (var propertyInfo in props)
{
return propertyInfo;
if (propertyInfo.PropertyType == parameterType ||
parameterType.IsAssignableFrom(propertyInfo.PropertyType))
{
return propertyInfo;
}
Type impType = ImplementationTypeForProperty(propertyInfo.Name);
if (impType == parameterType || parameterType.IsAssignableFrom(impType))
{
return propertyInfo;
}
}
if (i >= ifaces.Length)
break;

ti = ifaces[i];
++i;
}
return null;
}
Expand Down Expand Up @@ -239,4 +252,25 @@ public TContext Create<TContext>()
return rv;
}
}

public class PropertyDependencyResolutionException : BuildException
{
private readonly string dependentProperty;
private readonly Type parameterType;

public PropertyDependencyResolutionException(Type contextType, string dependentProperty, Type parameterType)
: base(contextType)
{
this.dependentProperty = dependentProperty;
this.parameterType = parameterType;
}

public override string ToString()
{
return
string.Format(
"The system attempted to find a property that provides type {2} that appears at or below an implementation attribute for type {1}. The error follows:\n{0} DependentProperty: {1}\n ParameterType: {2}",
base.ToString(), dependentProperty, parameterType);
}
}
}
52 changes: 52 additions & 0 deletions BinaryFinery.IOC.UnitTests/Inputs/LocationDependencies.cs
Expand Up @@ -60,6 +60,58 @@ public IDontCareDeps Deps
}
}

/***********************************************************************************/

public interface IFragmentedContext : IContext
{
[Implementation(typeof(DontCareDeps))]
IDontCareDeps Deps { get; }
}

public interface IMissingPieceContext : IContext
{
[Implementation(typeof(OtherFoo))]
IFoo B { get; }
}

public interface IJoinContext : IFragmentedContext, IMissingPieceContext
{

}

public class JoinContextImp : BaseContextImpl, IJoinContext
{
public IFoo B
{
get { return (IFoo)Factory.ObjectForProperty("B"); }
}

public IDontCareDeps Deps
{
get { return (IDontCareDeps)Factory.ObjectForProperty("Deps"); }
}
}

/**********************************************************************/

public interface IWorkingJoinContext : IFragmentedContext, IMissingPieceContext
{
[Implementation(typeof(DontCareDeps))]
IDontCareDeps Deps { get; }
}

public class WorkingJoinContextImp : BaseContextImpl, IWorkingJoinContext
{
public IFoo B
{
get { return (IFoo)Factory.ObjectForProperty("B"); }
}

public IDontCareDeps Deps
{
get { return (IDontCareDeps)Factory.ObjectForProperty("Deps"); }
}
}


}
18 changes: 18 additions & 0 deletions BinaryFinery.IOC.UnitTests/Tests/Building/TestCompoundContexts.cs
Expand Up @@ -14,6 +14,8 @@ public void Setup()
{
CM = ContextSystem.Manager;
CM.RegisterCustomContextImplementation(typeof(CompoundContextImp), typeof(ICompoundContext));
CM.RegisterCustomContextImplementation(typeof(JoinContextImp), typeof(IJoinContext));
CM.RegisterCustomContextImplementation(typeof(WorkingJoinContextImp), typeof(IWorkingJoinContext));
}

[Test]
Expand All @@ -35,5 +37,21 @@ public void DepsFooShouldBeOtherFoo()
Assert.IsInstanceOfType(typeof(OtherFoo),d.Myfoo);
}

[Test]
[ExpectedException(typeof(PropertyDependencyResolutionException))]
public void FragmentedInterfacesAreNotAllowed()
{
var context = CM.Create<IJoinContext>();
var d = context.Deps;
Assert.Fail("Should not get here.");
}
[Test]
public void FragmentedInterfacesAreAllowedIfExplicitlyOverriden()
{
var context = CM.Create<IWorkingJoinContext>();
var d = context.Deps;
Assert.That(d, Is.Not.Null);
}

}
}
34 changes: 34 additions & 0 deletions README.mdown
Expand Up @@ -139,6 +139,40 @@ property B, not Foo from property A. So the simple type-only resolution from v 1
where property X was initially declared - i.e. the most general interface. In the case where X is actually compatibly defined in more than one "base" interface, the behavior
is "not defined", i.e. it will use the first one it finds, and this can depending on circumstance, build version, optization etc.

### Depdendency Fragments

public interface IFragmentedContext : IContext
{
[Implementation(typeof(DontCareDeps))]
IDontCareDeps Deps { get; }
}

A "dependency fragment" is one where an Implementation specifies a class whose dependencies cannot be resolved. In the above case, the concrete implementation DontCareDeps requires
an IFoo in its constructor. The IFragmentedContext does not declare an IFoo. It is considered broken. The dependency must be resolvable in the base contexts of that
interface. The following is *not* enough:

public interface IMissingPieceContext : IContext
{
[Implementation(typeof(OtherFoo))]
IFoo B { get; }
}

public interface IJoinContext : IFragmentedContext, IMissingPieceContext
{
}

The IJoinContext does have an IFoo (from IMissingPieceContext), but it will not be used to resolve the dependency in IFragmentedContext. This is by design to reduce fragile behavior.

However, a fragmented interface can be repaired later, by redoing the implentation in a context which can resolve the dependency (directly or indirectly). Such as:

public interface IWorkingJoinContext : IFragmentedContext, IMissingPieceContext
{
[Implementation(typeof(DontCareDeps))]
IDontCareDeps Deps { get; }
}



Background
----------
Expand Down

0 comments on commit ffbcf03

Please sign in to comment.