Got extension methods working on DynamicDocument/DynamicDocumentList and added unit tests

for them.
This commit is contained in:
Shannon Deminick
2012-08-24 23:44:47 +07:00
parent 71fd352ea5
commit 64c9d97e53
4 changed files with 69 additions and 28 deletions

View File

@@ -252,6 +252,8 @@ namespace Umbraco.Core.Dynamics
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
//TODO: We MUST cache the result here, it is very expensive to keep finding extension methods!
try
{
//Property?
@@ -294,6 +296,8 @@ namespace Umbraco.Core.Dynamics
catch
{
//TODO: LOg this!
result = null;
return false;
}
@@ -316,7 +320,6 @@ namespace Umbraco.Core.Dynamics
var methodTypesToFind = new[]
{
typeof(IDocument),
typeof(DynamicDocument)
};

View File

@@ -53,6 +53,9 @@ namespace Umbraco.Core.Dynamics
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
//TODO: We MUST cache the result here, it is very expensive to keep finding extension methods and processing this stuff!
var name = binder.Name;
if (name == "Where")
{
@@ -353,7 +356,6 @@ namespace Umbraco.Core.Dynamics
var methodTypesToFind = new[]
{
typeof(IEnumerable<IDocument>),
typeof(IEnumerable<DynamicDocument>),
typeof(DynamicDocumentList)
};

View File

@@ -13,15 +13,21 @@ namespace Umbraco.Core.Dynamics
{
private static List<MethodInfo> GetAllExtensionMethods(Type thisType, string name, int argumentCount, bool argsContainsThis)
/// <summary>
/// Returns all extension methods found matching the definition
/// </summary>
/// <param name="thisType"></param>
/// <param name="name"></param>
/// <param name="argumentCount"></param>
/// <param name="argsContainsThis"></param>
/// <returns></returns>
/// <remarks>
/// NOTE: This will be an intensive method to call!! Results should be cached!
/// </remarks>
private static IEnumerable<MethodInfo> GetAllExtensionMethods(Type thisType, string name, int argumentCount, bool argsContainsThis)
{
//only scan assemblies we know to contain extension methods (user assemblies)
//var assembliesToScan = TypeFinder.GetAssembliesWithKnownExclusions();
var assembliesToScan = new List<Assembly>()
{
Assembly.Load("Umbraco.Tests")
};
var assembliesToScan = TypeFinder.GetAssembliesWithKnownExclusions();
//get extension methods from runtime
var candidates = (
@@ -36,11 +42,8 @@ namespace Umbraco.Core.Dynamics
select method
);
//search an explicit type (e.g. Enumerable, where most of the Linq methods are defined)
//if (explicitTypeToSearch != null)
//{
candidates = candidates.Concat(typeof(IEnumerable).GetMethods(BindingFlags.Static | BindingFlags.Public));
//}
//add the extension methods defined in IEnumerable
candidates = candidates.Concat(typeof(IEnumerable).GetMethods(BindingFlags.Static | BindingFlags.Public));
//filter by name
var methodsByName = candidates.Where(m => m.Name == name);
@@ -57,7 +60,7 @@ namespace Umbraco.Core.Dynamics
method.t != null && MethodArgZeroHasCorrectTargetType(method.m, method.t, thisType)
select method);
return methodsWhereArgZeroIsTargetType.Select(mt => mt.m).ToList();
return methodsWhereArgZeroIsTargetType.Select(mt => mt.m);
}
private static bool MethodArgZeroHasCorrectTargetType(MethodInfo method, Type firstArgumentType, Type thisType)
{
@@ -127,30 +130,28 @@ namespace Umbraco.Core.Dynamics
genericType = thisType.GetGenericArguments()[0];
}
var methods = GetAllExtensionMethods(thisType, name, args.Length, argsContainsThis);
var methods = GetAllExtensionMethods(thisType, name, args.Length, argsContainsThis)
.ToArray();
if (methods.Count == 0)
if (!methods.Any())
{
return null;
}
MethodInfo methodToExecute = null;
if (methods.Count > 1)
if (methods.Count() > 1)
{
//Given the args, lets get the types and compare the type sequence to try and find the correct overload
var argTypes = args.ToList().ConvertAll(o =>
{
Expression oe = (o as Expression);
var oe = (o as Expression);
if (oe != null)
{
return oe.Type.FullName;
}
return o.GetType().FullName;
});
var methodsWithArgTypes = methods.ConvertAll(method => new { method = method, types = method.GetParameters().ToList().ConvertAll(pi => pi.ParameterType.FullName) });
var firstMatchingOverload = methodsWithArgTypes.FirstOrDefault(m =>
{
return m.types.SequenceEqual(argTypes);
});
var methodsWithArgTypes = methods.Select(method => new {method, types = method.GetParameters().Select(pi => pi.ParameterType.FullName) });
var firstMatchingOverload = methodsWithArgTypes.FirstOrDefault(m => m.types.SequenceEqual(argTypes));
if (firstMatchingOverload != null)
{
methodToExecute = firstMatchingOverload.method;
@@ -159,9 +160,9 @@ namespace Umbraco.Core.Dynamics
if (methodToExecute == null)
{
MethodInfo firstMethod = methods.FirstOrDefault();
var firstMethod = methods.FirstOrDefault();
// NH: this is to ensure that it's always the correct one being chosen when using the LINQ extension methods
if (methods.Count > 1)
if (methods.Count() > 1)
{
var firstGenericMethod = methods.FirstOrDefault(x => x.IsGenericMethodDefinition);
if (firstGenericMethod != null)

View File

@@ -6,6 +6,7 @@ using System.Web;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Dynamics;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
@@ -54,7 +55,21 @@ namespace Umbraco.Tests
}
[Test]
public void Run_Custom_Extension_Method()
public void Custom_Extension_Methods()
{
var dynamicNode = GetDynamicNode(1173);
var asDynamic = dynamicNode.AsDynamic();
Assert.AreEqual("Hello world", asDynamic.DynamicDocumentNoParameters());
Assert.AreEqual("Hello world!", asDynamic.DynamicDocumentCustomString("Hello world!"));
Assert.AreEqual("Hello world!" + 123 + false, asDynamic.DynamicDocumentMultiParam("Hello world!", 123, false));
Assert.AreEqual("Hello world!" + 123 + false, asDynamic.Children.DynamicDocumentListMultiParam("Hello world!", 123, false));
Assert.AreEqual("Hello world!" + 123 + false, asDynamic.Children.DynamicDocumentEnumerableMultiParam("Hello world!", 123, false));
}
[Test]
public void Custom_Extension_Method_With_Params()
{
var dynamicNode = GetDynamicNode(1173);
var asDynamic = dynamicNode.AsDynamic();
@@ -321,10 +336,30 @@ namespace Umbraco.Tests
public static class DynamicDocumentCustomExtensionMethods
{
public static string ReturnHelloWorld(this DynamicDocument doc)
public static string DynamicDocumentNoParameters(this DynamicDocument doc)
{
return "Hello world";
}
public static string DynamicDocumentCustomString(this DynamicDocument doc, string custom)
{
return custom;
}
public static string DynamicDocumentMultiParam(this DynamicDocument doc, string custom, int i, bool b)
{
return custom + i + b;
}
public static string DynamicDocumentListMultiParam(this DynamicDocumentList doc, string custom, int i, bool b)
{
return custom + i + b;
}
public static string DynamicDocumentEnumerableMultiParam(this IEnumerable<DynamicDocument> doc, string custom, int i, bool b)
{
return custom + i + b;
}
}
}