Got extension methods working on DynamicDocument/DynamicDocumentList and added unit tests
for them.
This commit is contained in:
@@ -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)
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user