Facade cleanup and refactoring

This commit is contained in:
Stephan
2017-09-27 21:16:09 +02:00
parent e7a0d79ea7
commit 101ba8f620
10 changed files with 452 additions and 188 deletions

View File

@@ -1,31 +1,31 @@
using System;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models.PublishedContent
{
/// <inheritdoc />
/// <summary>
/// Indicates that the class is a published content model for a specified content type.
/// </summary>
/// <remarks>By default, the name of the class is assumed to be the content type alias. The
/// <c>PublishedContentModelAttribute</c> can be used to indicate a different alias.</remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class PublishedContentModelAttribute : Attribute
{
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="PublishedContentModelAttribute" /> class with a content type alias.
/// </summary>
/// <param name="contentTypeAlias">The content type alias.</param>
public PublishedContentModelAttribute(string contentTypeAlias)
{
if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(contentTypeAlias));
ContentTypeAlias = contentTypeAlias;
}
/// <summary>
/// Gets or sets the content type alias.
/// </summary>
public string ContentTypeAlias { get; }
}
}
using System;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.Models.PublishedContent
{
/// <inheritdoc />
/// <summary>
/// Indicates that the class is a published content model for a specified content type.
/// </summary>
/// <remarks>By default, the name of the class is assumed to be the content type alias. The
/// <c>PublishedContentModelAttribute</c> can be used to indicate a different alias.</remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class PublishedContentModelAttribute : Attribute
{
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="PublishedContentModelAttribute" /> class with a content type alias.
/// </summary>
/// <param name="contentTypeAlias">The content type alias.</param>
public PublishedContentModelAttribute(string contentTypeAlias)
{
if (string.IsNullOrWhiteSpace(contentTypeAlias)) throw new ArgumentNullOrEmptyException(nameof(contentTypeAlias));
ContentTypeAlias = contentTypeAlias;
}
/// <summary>
/// Gets or sets the content type alias.
/// </summary>
public string ContentTypeAlias { get; }
}
}

View File

@@ -44,13 +44,27 @@ namespace Umbraco.Core.Models.PublishedContent
foreach (var type in types)
{
// fixme - annoying - we want the xpression to be of a give type
// fixme - but then do we need ctor(IPublishedElement x) and what about ctor(IPublishedContent x)?
//var expr = ReflectionUtilities.GetCtorExpr<Func<IPublishedElement, IPublishedElement>>(type, false);
//if (expr == null)
// throw new InvalidOperationException($"Type {type.FullName} is missing a public constructor with one argument of type IPublishedElement.");
//var ccc = ReflectionUtilities.EmitCtor<IPublishedElement, IPublishedElement>(type, false);
//if (ccc == null)
// throw new InvalidOperationException($"Type {type.FullName} is missing a public constructor with one argument of type IPublishedElement.");
// so... the model type has to implement a ctor with one parameter being, or inheriting from,
// IPublishedElement - but it can be IPublishedContent - so we cannot get one precise ctor,
// we have to iterate over all ctors and try to find the right one
ConstructorInfo constructor = null;
Type parameterType = null;
foreach (var ctor in type.GetConstructors())
{
var parms = ctor.GetParameters();
if (parms.Length == 1 && typeof (IPublishedElement).IsAssignableFrom(parms[0].ParameterType))
if (parms.Length == 1 && typeof(IPublishedElement).IsAssignableFrom(parms[0].ParameterType))
{
if (constructor != null)
throw new InvalidOperationException($"Type {type.FullName} has more than one public constructor with one argument of type, or implementing, IPublishedElement.");
@@ -62,21 +76,21 @@ namespace Umbraco.Core.Models.PublishedContent
if (constructor == null)
throw new InvalidOperationException($"Type {type.FullName} is missing a public constructor with one argument of type, or implementing, IPublishedElement.");
var attribute = type.GetCustomAttribute<PublishedContentModelAttribute>(false); // fixme rename FacadeModelAttribute
var attribute = type.GetCustomAttribute<PublishedContentModelAttribute>(false);
var typeName = attribute == null ? type.Name : attribute.ContentTypeAlias;
if (modelInfos.TryGetValue(typeName, out ModelInfo modelInfo))
if (modelInfos.TryGetValue(typeName, out var modelInfo))
throw new InvalidOperationException($"Both types {type.FullName} and {modelInfo.ModelType.FullName} want to be a model type for content type with alias \"{typeName}\".");
exprs.Add(Expression.Lambda<Func<IPublishedElement, IPublishedElement>>(Expression.New(constructor)));
modelInfos[typeName] = new ModelInfo { ParameterType = parameterType, ModelType = type };
//exprs.Add(Expression.Lambda<Func<IPublishedElement, IPublishedElement>>(Expression.New(constructor)));
modelInfos[typeName] = new ModelInfo { ParameterType = parameterType, ModelType = type, Ctor = ReflectionUtilities.EmitCtor<Func<IPublishedElement, IPublishedElement>>(constructor) };
ModelTypeMap[typeName] = type;
}
var compiled = ReflectionUtilities.CompileToDelegates(exprs.ToArray());
var i = 0;
foreach (var modelInfo in modelInfos.Values)
modelInfo.Ctor = compiled[i++];
//var compiled = ReflectionUtilities.CompileToDelegates(exprs.ToArray());
//var i = 0;
//foreach (var modelInfo in modelInfos.Values)
// modelInfo.Ctor = compiled[i++];
_modelInfos = modelInfos.Count > 0 ? modelInfos : null;
}

View File

@@ -12,10 +12,13 @@ namespace Umbraco.Core
/// </summary>
public static class ReflectionUtilities
{
public const AssemblyBuilderAccess DefaultAssemblyBuilderAccess = AssemblyBuilderAccess.Run;
public const AssemblyBuilderAccess NoAssembly = 0;
public static Func<TInstance, TValue> GetPropertyGetter<TInstance, TValue>(string propertyName)
{
var type = typeof(TInstance);
var type0 = typeof(TValue);
var type = typeof (TInstance);
var type0 = typeof (TValue);
var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (property == null || property.PropertyType != type0)
throw new InvalidOperationException($"Could not get property {type}.{propertyName} : {type0}.");
@@ -24,8 +27,8 @@ namespace Umbraco.Core
public static Action<TInstance, TValue> GetPropertySetter<TInstance, TValue>(string propertyName)
{
var type = typeof(TInstance);
var type0 = typeof(TValue);
var type = typeof (TInstance);
var type0 = typeof (TValue);
var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (property == null || property.PropertyType != type0)
throw new InvalidOperationException($"Could not get property {type}.{propertyName} : {type0}.");
@@ -34,8 +37,8 @@ namespace Umbraco.Core
public static void GetPropertyGetterSetter<TInstance, TValue>(string propertyName, out Func<TInstance, TValue> getter, out Action<TInstance, TValue> setter)
{
var type = typeof(TInstance);
var type0 = typeof(TValue);
var type = typeof (TInstance);
var type0 = typeof (TValue);
var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (property == null || property.PropertyType != type0)
throw new InvalidOperationException($"Could not get property {type}.{propertyName} : {type0}.");
@@ -43,40 +46,155 @@ namespace Umbraco.Core
setter = GetPropertySetter<TInstance, TValue>(property);
}
public static Func<TInstance> GetCtor<TInstance>()
public static Expression<Func<TInstance>> GetCtorExpr<TInstance>(bool mustExist = true)
{
var type = typeof(TInstance);
var type = typeof (TInstance);
// get the constructor infos
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, Type.EmptyTypes, null);
if (ctor == null)
throw new InvalidOperationException($"Could not find constructor {type}.ctor().");
{
if (!mustExist) return null;
throw new InvalidOperationException($"Could not find constructor {type}.ctor().");
}
var exprNew = Expression.New(ctor);
var expr = Expression.Lambda<Func<TInstance>>(exprNew);
return expr.CompileToDelegate();
return Expression.Lambda<Func<TInstance>>(exprNew);
}
public static Func<object> GetCtor(Type type)
public static Expression<TLambda> GetCtorExpr<TLambda>(Type type, bool mustExist = true)
{
// get the constructor infos
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, Type.EmptyTypes, null);
if (ctor == null)
{
if (!mustExist) return null;
throw new InvalidOperationException($"Could not find constructor {type}.ctor().");
}
var exprNew = Expression.New(ctor);
var expr = Expression.Lambda<Func<object>>(exprNew);
return expr.CompileToDelegate();
return Expression.Lambda<TLambda>(exprNew);
}
public static Func<TInstance> GetCtor<TInstance>(bool mustExist = true, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var expr = GetCtorExpr<TInstance>(mustExist);
if (expr == null) return null;
return access == NoAssembly ? expr.Compile() : CompileToDelegate(expr, access);
}
public static TLambda GetCtor<TLambda>(Type type, bool mustExist = true, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var expr = GetCtorExpr<TLambda>(type, mustExist);
if (expr == null) return default(TLambda);
return access == NoAssembly ? expr.Compile() : CompileToDelegate(expr, access);
}
public static Func<TInstance> EmitCtor<TInstance>(bool mustExist = true)
{
var type = typeof (TInstance);
// get the constructor infos
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, Type.EmptyTypes, null);
if (ctor == null)
{
if (!mustExist) return null;
throw new InvalidOperationException($"Could not find constructor {type}.ctor().");
}
var dm = new DynamicMethod(string.Empty, type, Array.Empty<Type>(), type.Module, true);
var gen = dm.GetILGenerator();
gen.Emit(OpCodes.Newobj, ctor);
gen.Emit(OpCodes.Ret);
return (Func<TInstance>) dm.CreateDelegate(typeof (Func<TInstance>));
}
public static Func<TArg0, TInstance> EmitCtor<TInstance, TArg0>(bool mustExist = true)
{
var type = typeof (TInstance);
var type0 = typeof (TArg0);
// get the constructor infos
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, new[] { type0 }, null);
if (ctor == null)
{
if (!mustExist) return null;
throw new InvalidOperationException($"Could not find constructor {type}.ctor().");
}
var dm = new DynamicMethod(string.Empty, type, new[] { type0 }, type.Module, true);
var gen = dm.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Newobj, ctor);
gen.Emit(OpCodes.Ret);
return (Func<TArg0, TInstance>) dm.CreateDelegate(typeof (Func<TArg0, TInstance>));
}
public static Func<TArg0, TInstance> EmitCtor<TArg0, TInstance>(Type type, bool mustExist = true)
{
var type0 = typeof (TArg0);
// get the constructor infos
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
null, new[] { type0 }, null);
if (ctor == null)
{
if (!mustExist) return null;
throw new InvalidOperationException($"Could not find constructor {type}.ctor().");
}
var dm = new DynamicMethod(string.Empty, type, new[] { type0 }, type.Module, true);
var gen = dm.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Newobj, ctor);
gen.Emit(OpCodes.Ret);
return (Func<TArg0, TInstance>) dm.CreateDelegate(typeof (Func<TArg0, TInstance>));
}
public static TLambda EmitCtor<TLambda>(ConstructorInfo constructorInfo)
{
var type = constructorInfo.DeclaringType;
var args = constructorInfo.GetParameters().Select(x => x.ParameterType).ToArray();
var dm = new DynamicMethod(string.Empty, type, args, type.Module, true);
var gen = dm.GetILGenerator();
if (args.Length < 5)
{
if (args.Length > 0)
gen.Emit(OpCodes.Ldarg_0);
if (args.Length > 1)
gen.Emit(OpCodes.Ldarg_1);
if (args.Length > 2)
gen.Emit(OpCodes.Ldarg_2);
if (args.Length > 3)
gen.Emit(OpCodes.Ldarg_3);
}
else
{
for (var i = 0; i < args.Length; i++)
gen.Emit(OpCodes.Ldarg, i);
}
gen.Emit(OpCodes.Newobj, constructorInfo);
gen.Emit(OpCodes.Ret);
return (TLambda) (object) dm.CreateDelegate(typeof (TLambda));
}
// fixme others need it too?
public static Func<TArg0, TInstance> GetCtor<TInstance, TArg0>()
{
var type = typeof(TInstance);
var type0 = typeof(TArg0);
var type = typeof (TInstance);
var type0 = typeof (TArg0);
// get the constructor infos
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
@@ -94,9 +212,9 @@ namespace Umbraco.Core
public static Func<TArg0, TArg1, TInstance> GetCtor<TInstance, TArg0, TArg1>()
{
var type = typeof(TInstance);
var type0 = typeof(TArg0);
var type1 = typeof(TArg1);
var type = typeof (TInstance);
var type0 = typeof (TArg0);
var type1 = typeof (TArg1);
// get the constructor infos
var ctor = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
@@ -130,7 +248,7 @@ namespace Umbraco.Core
public static TMethod GetMethod<TInstance, TMethod>(string methodName)
{
var type = typeof(TInstance);
var type = typeof (TInstance);
GetMethodParms<TInstance, TMethod>(out var parameterTypes, out var returnType);
@@ -142,7 +260,7 @@ namespace Umbraco.Core
private static Func<TInstance, TValue> GetPropertyGetter<TInstance, TValue>(PropertyInfo property)
{
var type = typeof(TInstance);
var type = typeof (TInstance);
var getMethod = property.GetMethod;
if (getMethod == null)
@@ -156,14 +274,14 @@ namespace Umbraco.Core
private static Action<TInstance, TValue> GetPropertySetter<TInstance, TValue>(PropertyInfo property)
{
var type = typeof(TInstance);
var type = typeof (TInstance);
var setMethod = property.SetMethod;
if (setMethod == null)
throw new InvalidOperationException($"Property {type}.{property.Name} : {property.PropertyType} does not have a setter.");
var exprThis = Expression.Parameter(type, "this");
var exprArg0 = Expression.Parameter(typeof(TValue), "value");
var exprArg0 = Expression.Parameter(typeof (TValue), "value");
var exprCall = Expression.Call(exprThis, setMethod, exprArg0);
var expr = Expression.Lambda<Action<TInstance, TValue>>(exprCall, exprThis, exprArg0);
return expr.CompileToDelegate();
@@ -171,14 +289,16 @@ namespace Umbraco.Core
private static void GetMethodParms<TMethod>(out Type[] parameterTypes, out Type returnType)
{
var typeM = typeof(TMethod);
var typeM = typeof (TMethod);
var typeList = new List<Type>();
returnType = typeof(void);
returnType = typeof (void);
if (!typeof(MulticastDelegate).IsAssignableFrom(typeM) || typeM == typeof(MulticastDelegate))
if (!typeof (MulticastDelegate).IsAssignableFrom(typeM) || typeM == typeof (MulticastDelegate))
throw new InvalidOperationException("Invalid TMethod, must be a Func or Action.");
var typeName = typeM.FullName;
if (typeName == null)
throw new InvalidOperationException($"Could not get {typeM} type name.");
if (typeName.StartsWith("System.Func`"))
{
var i = 0;
@@ -204,20 +324,22 @@ namespace Umbraco.Core
private static void GetMethodParms<TInstance, TMethod>(out Type[] parameterTypes, out Type returnType)
{
var type = typeof(TInstance);
var type = typeof (TInstance);
var typeM = typeof(TMethod);
var typeM = typeof (TMethod);
var typeList = new List<Type>();
returnType = typeof(void);
returnType = typeof (void);
if (!typeof(MulticastDelegate).IsAssignableFrom(typeM) || typeM == typeof(MulticastDelegate))
if (!typeof (MulticastDelegate).IsAssignableFrom(typeM) || typeM == typeof (MulticastDelegate))
throw new InvalidOperationException("Invalid TMethod, must be a Func or Action.");
var typeName = typeM.FullName;
if (typeName == null)
throw new InvalidOperationException($"Could not get {typeM} type name.");
if (!typeM.IsGenericType)
throw new InvalidOperationException(typeName);
throw new InvalidOperationException($"Type {typeName} is not generic.");
if (typeM.GenericTypeArguments[0] != type)
throw new InvalidOperationException("Invalid TMethod, the first generic argument must be TInstance.");
throw new InvalidOperationException($"Invalid type {typeName}, the first generic argument must be {type.FullName}.");
if (typeName.StartsWith("System.Func`"))
{
var i = 1;
@@ -435,11 +557,19 @@ namespace Umbraco.Core
propInfo.SetValue(obj, val, null);
}
*/
*/
public static Action CompileToDelegate(Expression<Action> expr)
// fixme dont think this can work at all
public static TLambda Compile<TLambda>(Expression<TLambda> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var typeBuilder = CreateTypeBuilder();
return access == NoAssembly
? expr.Compile()
: CompileToDelegate(expr, access);
}
public static Action CompileToDelegate(Expression<Action> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var typeBuilder = CreateTypeBuilder(access);
var builder = typeBuilder.DefineMethod("Method",
MethodAttributes.Public | MethodAttributes.Static); // CompileToMethod requires a static method
@@ -449,9 +579,9 @@ namespace Umbraco.Core
return (Action) Delegate.CreateDelegate(typeof (Action), typeBuilder.CreateType().GetMethod("Method"));
}
public static Action<T1> CompileToDelegate<T1>(Expression<Action<T1>> expr)
public static Action<T1> CompileToDelegate<T1>(Expression<Action<T1>> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var typeBuilder = CreateTypeBuilder();
var typeBuilder = CreateTypeBuilder(access);
var builder = typeBuilder.DefineMethod("Method",
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
@@ -462,35 +592,35 @@ namespace Umbraco.Core
return (Action<T1>) Delegate.CreateDelegate(typeof (Action<T1>), typeBuilder.CreateType().GetMethod("Method"));
}
public static Action<T1, T2> CompileToDelegate<T1, T2>(Expression<Action<T1, T2>> expr)
public static Action<T1, T2> CompileToDelegate<T1, T2>(Expression<Action<T1, T2>> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var typeBuilder = CreateTypeBuilder();
var typeBuilder = CreateTypeBuilder(access);
var builder = typeBuilder.DefineMethod("Method",
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
typeof (void), new[] { typeof(T1), typeof (T2) });
typeof (void), new[] { typeof (T1), typeof (T2) });
expr.CompileToMethod(builder);
return (Action<T1, T2>) Delegate.CreateDelegate(typeof (Action<T1, T2>), typeBuilder.CreateType().GetMethod("Method"));
}
public static Action<T1, T2, T3> CompileToDelegate<T1, T2, T3>(Expression<Action<T1, T2, T3>> expr)
public static Action<T1, T2, T3> CompileToDelegate<T1, T2, T3>(Expression<Action<T1, T2, T3>> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var typeBuilder = CreateTypeBuilder();
var typeBuilder = CreateTypeBuilder(access);
var builder = typeBuilder.DefineMethod("Method",
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
typeof (void), new[] { typeof (T1), typeof (T2) , typeof (T3) });
typeof (void), new[] { typeof (T1), typeof (T2), typeof (T3) });
expr.CompileToMethod(builder);
return (Action<T1, T2, T3>) Delegate.CreateDelegate(typeof (Action<T1, T2, T3>), typeBuilder.CreateType().GetMethod("Method"));
}
public static Func<TResult> CompileToDelegate<TResult>(Expression<Func<TResult>> expr)
public static Func<TResult> CompileToDelegate<TResult>(Expression<Func<TResult>> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var typeBuilder = CreateTypeBuilder();
var typeBuilder = CreateTypeBuilder(access);
var builder = typeBuilder.DefineMethod("Method",
MethodAttributes.Public | MethodAttributes.Static,
@@ -501,9 +631,9 @@ namespace Umbraco.Core
return (Func<TResult>) Delegate.CreateDelegate(typeof (Func<TResult>), typeBuilder.CreateType().GetMethod("Method"));
}
public static Func<T1, TResult> CompileToDelegate<T1, TResult>(Expression<Func<T1, TResult>> expr)
public static Func<T1, TResult> CompileToDelegate<T1, TResult>(Expression<Func<T1, TResult>> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var typeBuilder = CreateTypeBuilder();
var typeBuilder = CreateTypeBuilder(access);
var builder = typeBuilder.DefineMethod("Method",
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
@@ -511,25 +641,25 @@ namespace Umbraco.Core
expr.CompileToMethod(builder);
return (Func<T1,TResult>) Delegate.CreateDelegate(typeof (Func<T1, TResult>), typeBuilder.CreateType().GetMethod("Method"));
return (Func<T1, TResult>) Delegate.CreateDelegate(typeof (Func<T1, TResult>), typeBuilder.CreateType().GetMethod("Method"));
}
public static Func<T1, T2, TResult> CompileToDelegate<T1, T2, TResult>(Expression<Func<T1, T2, TResult>> expr)
public static Func<T1, T2, TResult> CompileToDelegate<T1, T2, TResult>(Expression<Func<T1, T2, TResult>> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var typeBuilder = CreateTypeBuilder();
var typeBuilder = CreateTypeBuilder(access);
var builder = typeBuilder.DefineMethod("Method",
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
typeof(TResult), new[] { typeof (T1), typeof(T2) });
typeof (TResult), new[] { typeof (T1), typeof (T2) });
expr.CompileToMethod(builder);
return (Func<T1, T2, TResult>) Delegate.CreateDelegate(typeof (Func<T1, T2, TResult>), typeBuilder.CreateType().GetMethod("Method"));
}
public static Func<T1, T2, T3, TResult> CompileToDelegate<T1, T2, T3, TResult>(Expression<Func<T1, T2, T3, TResult>> expr)
public static Func<T1, T2, T3, TResult> CompileToDelegate<T1, T2, T3, TResult>(Expression<Func<T1, T2, T3, TResult>> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var typeBuilder = CreateTypeBuilder();
var typeBuilder = CreateTypeBuilder(access);
var builder = typeBuilder.DefineMethod("Method",
MethodAttributes.Public | MethodAttributes.Static, // CompileToMethod requires a static method
@@ -540,9 +670,9 @@ namespace Umbraco.Core
return (Func<T1, T2, T3, TResult>) Delegate.CreateDelegate(typeof (Func<T1, T2, T3, TResult>), typeBuilder.CreateType().GetMethod("Method"));
}
public static TMethod CompileToDelegate<TMethod>(Expression<TMethod> expr)
public static TMethod CompileToDelegate<TMethod>(Expression<TMethod> expr, AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var typeBuilder = CreateTypeBuilder();
var typeBuilder = CreateTypeBuilder(access);
GetMethodParms<TMethod>(out var parameterTypes, out var returnType);
@@ -556,8 +686,11 @@ namespace Umbraco.Core
}
public static TMethod[] CompileToDelegates<TMethod>(params Expression<TMethod>[] exprs)
=> CompileToDelegates(AssemblyBuilderAccess.RunAndCollect, exprs);
public static TMethod[] CompileToDelegates<TMethod>(AssemblyBuilderAccess access, params Expression<TMethod>[] exprs)
{
var typeBuilder = CreateTypeBuilder();
var typeBuilder = CreateTypeBuilder(access);
GetMethodParms<TMethod>(out var parameterTypes, out var returnType);
@@ -579,11 +712,15 @@ namespace Umbraco.Core
return methods;
}
private static TypeBuilder CreateTypeBuilder()
private static TypeBuilder CreateTypeBuilder(AssemblyBuilderAccess access = DefaultAssemblyBuilderAccess)
{
var assemblyName = new AssemblyName("Umbraco.Core.DynamicAssemblies." + Guid.NewGuid().ToString("N"));
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);
var module = assembly.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll");
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, access);
var module = (access & AssemblyBuilderAccess.Save) > 0
? assembly.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll")
: assembly.DefineDynamicModule(assemblyName.Name); // has to be transient
return module.DefineType("Class", TypeAttributes.Public | TypeAttributes.Abstract);
}
}

View File

@@ -652,7 +652,7 @@
<Compile Include="Models\PublishedContent\PublishedContentEnumerable.cs" />
<Compile Include="Models\PublishedContent\PublishedContentExtensionsForModels.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModel.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelAttribute.cs" />
<Compile Include="Models\PublishedContent\PublishedModelAttribute.cs" />
<Compile Include="Models\PublishedContent\PublishedModelFactory.cs" />
<Compile Include="Models\PublishedContent\PublishedContentType.cs" />
<Compile Include="Models\PublishedContent\PublishedContentTypeConverter.cs" />

View File

@@ -140,6 +140,8 @@ namespace Umbraco.Tests.Benchmarks
// but, our utilities know how to do it!
_expressionMethod3 = expr.CompileToDelegate();
_expressionMethod4 = ReflectionUtilities.GetCtor<Foo, IFoo>();
// however, unfortunately, the generated "compiled to delegate" code cannot access private stuff :(
}
public IFoo IlCtor(IFoo foo)

View File

@@ -1,73 +1,57 @@
using System.Linq;
using NUnit.Framework;
using Umbraco.Core;
namespace Umbraco.Tests.DynamicsAndReflection
{
[TestFixture]
public class ReflectionTests
{
[Test]
public void GetBaseTypesIsOk()
{
// tests that the GetBaseTypes extension method works.
var type = typeof(Class2);
var types = type.GetBaseTypes(true).ToArray();
Assert.AreEqual(3, types.Length);
Assert.Contains(typeof(Class2), types);
Assert.Contains(typeof(Class1), types);
Assert.Contains(typeof(object), types);
types = type.GetBaseTypes(false).ToArray();
Assert.AreEqual(2, types.Length);
Assert.Contains(typeof(Class1), types);
Assert.Contains(typeof(object), types);
}
[Test]
public void GetInterfacesIsOk()
{
// tests that GetInterfaces gets _all_ interfaces
// so the AllInterfaces extension method is useless
var type = typeof(Class2);
var interfaces = type.GetInterfaces();
Assert.AreEqual(2, interfaces.Length);
Assert.Contains(typeof(IInterface1), interfaces);
Assert.Contains(typeof(IInterface2), interfaces);
}
// TypeExtensions.AllInterfaces was broken an not used, has been commented out
//
//[Test]
//public void AllInterfacesIsBroken()
//{
// // tests that the AllInterfaces extension method is broken
//
// var type = typeof(Class2);
// var interfaces = type.AllInterfaces().ToArray();
// Assert.AreEqual(3, interfaces.Length); // should be 2!
// Assert.Contains(typeof(IInterface1), interfaces);
// Assert.Contains(typeof(IInterface2), interfaces);
// Assert.AreEqual(2, interfaces.Count(i => i == typeof(IInterface1))); // duplicate!
// Assert.AreEqual(1, interfaces.Count(i => i == typeof(IInterface2)));
//}
interface IInterface1
{ }
interface IInterface2 : IInterface1
{
void Method();
}
class Class1 : IInterface2
{
public void Method() { }
}
class Class2 : Class1
{ }
}
}
using System.Linq;
using NUnit.Framework;
using Umbraco.Core;
namespace Umbraco.Tests.Clr
{
[TestFixture]
public class ReflectionTests
{
[Test]
public void GetBaseTypesIsOk()
{
// tests that the GetBaseTypes extension method works.
var type = typeof(Class2);
var types = type.GetBaseTypes(true).ToArray();
Assert.AreEqual(3, types.Length);
Assert.Contains(typeof(Class2), types);
Assert.Contains(typeof(Class1), types);
Assert.Contains(typeof(object), types);
types = type.GetBaseTypes(false).ToArray();
Assert.AreEqual(2, types.Length);
Assert.Contains(typeof(Class1), types);
Assert.Contains(typeof(object), types);
}
[Test]
public void GetInterfacesIsOk()
{
// tests that GetInterfaces gets _all_ interfaces
// so the AllInterfaces extension method is useless
var type = typeof(Class2);
var interfaces = type.GetInterfaces();
Assert.AreEqual(2, interfaces.Length);
Assert.Contains(typeof(IInterface1), interfaces);
Assert.Contains(typeof(IInterface2), interfaces);
}
interface IInterface1
{ }
interface IInterface2 : IInterface1
{
void Method();
}
class Class1 : IInterface2
{
public void Method() { }
}
class Class2 : Class1
{ }
}
}

View File

@@ -0,0 +1,126 @@
using System;
using NUnit.Framework;
using Umbraco.Core;
namespace Umbraco.Tests.Clr
{
[TestFixture]
public class ReflectionUtilitiesTests
{
[Test]
public void Test()
{
// cannot ctor a private class
var ctor1 = ReflectionUtilities.GetCtor<PrivateClass>();
Assert.Throws<MethodAccessException>(() => _ = ctor1());
// cannot private ctor a public class
var ctor2 = ReflectionUtilities.GetCtor<PublicClass, int>();
Assert.Throws<MethodAccessException>(() => _ = ctor2(0));
// can public ctor a public class
var ctor3 = ReflectionUtilities.GetCtor<PublicClass, string>();
Assert.IsNotNull(ctor3(string.Empty));
// works if not a dynamic assembly
var ctor4 = ReflectionUtilities.GetCtor<PrivateClass>(false);
Assert.IsNotNull(ctor4());
// we need the dynasm flag everywhere, because in some cases we do not
// want to create a dynasm that will stay around - eg when using the
// generated stuff only a few times
// collectible assemblies - created with RunAndCollect...
// https://msdn.microsoft.com/en-us/library/dd554932(v=vs.100).aspx
// with restrictions, but we could try it?
// so...
// GetCtor<PrivateClass>(ReflectionUtilities.Compile.None)
// should we find a way for a dynamic assembly to access private stuff?
}
[Test]
public void SingleDynAsmTest()
{
var expr1 = ReflectionUtilities.GetCtorExpr<PrivateClass>();
//var ctor2 = ReflectionUtilities.GetCtorExpr<PublicClass, int>();
//var ctor3 = ReflectionUtilities.GetCtorExpr<PublicClass, string>();
// creates one single dynamic assembly containing all methods
var ctors = ReflectionUtilities.CompileToDelegates(expr1);
var ctor1 = ctors[0];
// still, cannot access private stuff
Assert.Throws<MethodAccessException>(() => _ = ctor1());
}
[Test]
public void MoreTest()
{
// can get a ctor via generic and compile
var expr1 = ReflectionUtilities.GetCtorExpr<Class1>();
var ctor1 = ReflectionUtilities.Compile(expr1);
Assert.IsInstanceOf<Class1>(ctor1());
// direct
var ctor1A = ReflectionUtilities.GetCtor<Class1>();
Assert.IsInstanceOf<Class1>(ctor1A());
// can get a ctor via type and compile
var expr2 = ReflectionUtilities.GetCtorExpr<Func<Class1>>(typeof (Class1));
var ctor2 = ReflectionUtilities.Compile(expr2);
Assert.IsInstanceOf<Class1>(ctor2());
// direct
var ctor2A = ReflectionUtilities.GetCtor<Func<Class1>>(typeof (Class1));
Assert.IsInstanceOf<Class1>(ctor2A());
// if type is unknown (in a variable)
var ctor2B = ReflectionUtilities.GetCtor<Func<object>>(typeof (Class1));
Assert.IsInstanceOf<Class1>(ctor2B());
// cannot get a ctor for a private class
var ctorP1 = ReflectionUtilities.GetCtor<Class1P>();
Assert.Throws<MethodAccessException>(() => ctorP1());
// unless we don't compile to an assembly
var ctorP1A = ReflectionUtilities.GetCtor<Class1P>(access: ReflectionUtilities.NoAssembly);
Assert.IsInstanceOf<Class1P>(ctorP1A());
// so...
// if I create a dynamic method delegate by writing IL it's fast and I can access private stuff
// if I create an expression it's easier than IL but it's slower
// if I compile the expression, the speed is same as delegate but then I cannot access private stuff
// so ... what should I do?!
}
// todo
// - figure out the private/public thing
// - implement the dynasm enumeration
// - figure out ctors in model factory - ie the casting thing
// this is how we could indicate what to do?
private enum DynAsm
{
None,
Run,
RunAndCollect
}
private class PrivateClass { }
public class PublicClass
{
private PublicClass(int i) { }
public PublicClass(string s) { }
}
public class Class1 { }
public class Class2 { }
private class Class1P { }
}
}

View File

@@ -217,6 +217,7 @@
<Compile Include="Cache\CacheRefresherComponentTests.cs" />
<Compile Include="Cache\RefresherTests.cs" />
<Compile Include="Cache\SnapDictionaryTests.cs" />
<Compile Include="Clr\ReflectionUtilitiesTests.cs" />
<Compile Include="Collections\OrderedHashSetTests.cs" />
<Compile Include="CoreThings\CallContextTests.cs" />
<Compile Include="Components\ComponentTests.cs" />
@@ -388,7 +389,7 @@
<Compile Include="Web\Controllers\FilterAllowedOutgoingContentAttributeTests.cs" />
<Compile Include="Web\Controllers\MediaControllerUnitTests.cs" />
<Compile Include="CoreXml\FrameworkXmlTests.cs" />
<Compile Include="DynamicsAndReflection\ReflectionTests.cs" />
<Compile Include="Clr\ReflectionTests.cs" />
<Compile Include="Macros\MacroParserTests.cs" />
<Compile Include="Migrations\Upgrades\ValidateV7UpgradeTest.cs" />
<Compile Include="Models\ContentExtensionsTests.cs" />

View File

@@ -12,8 +12,15 @@
file everytime Umbraco builds.
-->
<!--
FIXME
for historical reasons, this file tries to cleanup everything as much as it can
BUT it means that it needs to be kept in sync with web.Template.config very closely
else it can start screwing everything - in fact it is quite bad at the moment, so
I am going to comment it out for the time being
-->
<!-- ensure sections and groups -->
<!--
<configSections>
<section name="urlrewritingnet" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<section name="FileSystemProviders" xdt:Locator="Match(name)" xdt:Transform="Remove" />
@@ -31,14 +38,12 @@
</sectionGroup>
</configSections>
<!-- remove sections -->
<urlrewritingnet xdt:Transform="Remove" />
<FileSystemProviders xdt:Transform="Remove" />
<system.web.webPages.razor xdt:Transform="Remove" />
<system.web.extensions xdt:Transform="Remove" />
<system.codedom xdt:Transform="Remove" />
<!-- prepare umbracoConfiguration section -->
<umbracoConfiguration xdt:Transform="Remove" />
<umbracoConfiguration xdt:Transform="InsertBefore(/configuration/appSettings)">
<settings configSource="config\umbracoSettings.config" />
@@ -47,7 +52,6 @@
<HealthChecks configSource="config\HealthChecks.config" />
</umbracoConfiguration>
<!-- prepare appSettings -->
<appSettings xdt:Transform="Remove" xdt:Locator="Condition(@configSource != '')" />
<appSettings xdt:Transform="InsertIfMissing">
<add key="umbracoConfigurationStatus" value="" />
@@ -67,14 +71,12 @@
<add key="owin:appStartup" value="UmbracoDefaultOwinStartup" xdt:Transform="InsertIfMissing" xdt:Locator="Match(key)" />
</appSettings>
<!-- prepare connectionStrings -->
<connectionStrings xdt:Transform="Remove" xdt:Locator="Condition(@configSource != '')" />
<connectionStrings xdt:Transform="InsertIfMissing">
<remove name="umbracoDbDSN" />
<add name="umbracoDbDSN" connectionString="" providerName="" />
</connectionStrings>
<!-- prepare system.data -->
<system.data>
<DbProviderFactories>
<add name="Microsoft SQL Server Compact Data Provider 4.0" xdt:Locator="Match(name)" xdt:Transform="SetAttributes(type)"
@@ -315,7 +317,7 @@
</system.web>
<!-- prepare system.webServer -->
<!-x- prepare system.webServer -x->
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="UrlRewriteModule" xdt:Transform="Remove" xdt:Locator="Match(name)" />
@@ -330,7 +332,7 @@
<add name="Channels_Word" xdt:Transform="Remove" xdt:Locator="Match(name)" />
</handlers>
<!-- increase default upload file size limit -->
<!-x- increase default upload file size limit -x->
<security xdt:Transform="InsertIfMissing">
<requestFiltering xdt:Transform="InsertIfMissing">
<requestLimits maxAllowedContentLength="1073741824" xdt:Transform="InsertIfMissing" />
@@ -338,10 +340,8 @@
</security>
</system.webServer>
<!-- prepare runtime -->
<runtime>
<!-- ensure proper assembly bindings -->
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Transform="Remove" xdt:Locator="Condition(_defaultNamespace:assemblyIdentity[@name='System.Web.Helpers']])" />
@@ -440,8 +440,8 @@
<bindingRedirect oldVersion="0.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
</dependentAssembly>
<!-- below is temp, see https://github.com/dotnet/roslyn/issues/12255 -->
<!-- was just removing, but uh why?! -->
<!-xx- below is temp, see https://github.com/dotnet/roslyn/issues/12255 -xx->
<!-xx- was just removing, but uh why?! -xx->
<dependentAssembly xdt:Transform="Remove" xdt:Locator="Condition(_defaultNamespace:assemblyIdentity[@name='System.Collections.Immutable']])" />
<dependentAssembly xdt:Transform="Insert">
@@ -457,5 +457,5 @@
</assemblyBinding>
</runtime>
-->
</configuration>

View File

@@ -80,7 +80,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
var ctor = _listCtors.GetOrAdd(type, t =>
{
var listType = typeof(List<>).MakeGenericType(t);
return ReflectionUtilities.GetCtor(listType);
return ReflectionUtilities.GetCtor<Func<object>>(listType);
});
elements = (IList) ctor();