Skip to content

Commit

Permalink
增加匿名对象的支持
Browse files Browse the repository at this point in the history
  • Loading branch information
jiniannet committed Jul 26, 2021
1 parent 053a678 commit ef49c58
Show file tree
Hide file tree
Showing 26 changed files with 579 additions and 219 deletions.
6 changes: 2 additions & 4 deletions src/JinianNet.JNTemplate.Test/ConfigTests.cs
Expand Up @@ -127,15 +127,13 @@ public void TestAutoMode()
[Fact]
public void TestGlobalData()
{
Console.WriteLine(Field.Version);
var engine = new EngineBuilder().Build();
engine.Configure(o =>o.Data.Set("name","jntemplate"));

var templateContent ="hello,${name} ${version}";
var templateContent ="hello,${name}";
var template = engine.CreateTemplate(templateContent);
template.Set("version", Field.Version);
var render = template.Render();
Assert.Equal($"hello,jntemplate {Field.Version}", render);
Assert.Equal($"hello,jntemplate", render);
}
}
}
17 changes: 17 additions & 0 deletions src/JinianNet.JNTemplate.Test/TagsTests.cs
Expand Up @@ -401,6 +401,23 @@ public void TestListOfTOutput()
Assert.StartsWith("System.Collections.Generic.List`1", render);
}

/// <summary>
/// 测试Anonymous object
/// </summary>
[Fact]
public void TestAnonymousObject()
{
var templateContent = "${model.Id}";
var template = Engine.CreateTemplate(templateContent);
template.SetAnonymousObject("model", new
{
Name = "user",
Id = 48
});
var render = template.Render();
Assert.Equal("48", render);

}
///// <summary>
///// 测试标签大小写
///// </summary>
Expand Down
Expand Up @@ -21,5 +21,7 @@ public override void Render(TextWriter writer)
{
Render(writer, this.Context);
}
/// <inheritdoc />
public abstract bool EnableCompile { get; }
}
}
152 changes: 2 additions & 150 deletions src/JinianNet.JNTemplate/CodeCompilation/TemplateCompiler.cs
Expand Up @@ -30,98 +30,7 @@ public static T GenerateDefaultValue<T>()
return default(T);
}

/// <summary>
/// Adds a new property to the type, with the given name, attributes, calling convention, and property signature.
/// </summary>
/// <param name="type">The return type of the property.</param>
/// <param name="typeBuilder">The <see cref="TypeBuilder"/>.</param>
/// <param name="name">The name of the property. name cannot contain embedded nulls.</param>
public static void ImplementationProperty(Type type, TypeBuilder typeBuilder, string name)
{
FieldBuilder customerNameBldr = typeBuilder.DefineField($"_{name.ToLower()}", type, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.HasDefault, type, null);

MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;

MethodBuilder getBuilder = typeBuilder.DefineMethod($"get_{name}", getSetAttr, type, Type.EmptyTypes);

ILGenerator getIl = getBuilder.GetILGenerator();

getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, customerNameBldr);
getIl.Emit(OpCodes.Ret);

MethodBuilder setBuilder = typeBuilder.DefineMethod($"set_{name}", getSetAttr, typeof(void), new Type[] { type });
ILGenerator setIl = setBuilder.GetILGenerator();

setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, customerNameBldr);
setIl.Emit(OpCodes.Ret);

propertyBuilder.SetGetMethod(getBuilder);
propertyBuilder.SetSetMethod(setBuilder);
}


/// <summary>
/// define object
/// </summary>
/// <param name="baseType">base type</param>
/// <returns></returns>
public static object DefineObjectFrom(Type baseType)
{
var name = baseType.FullName.GetHashCode().ToString();
var assName = $"{ typeof(TemplateCompiler).Namespace}.Entity{name}";
var typeBuilder = DefineType(assName);
var ps = baseType.GetProperties();
foreach (var p in ps)
{
ImplementationProperty(p.PropertyType, typeBuilder, p.Name);
}
var type =
#if NETSTANDARD2_0
typeBuilder.AsType();
#else
typeBuilder.CreateType();
#endif
return type.CreateInstance();
}

/// <summary>
/// copy property
/// </summary>
/// <param name="value">ori object</param>
/// <returns></returns>
public static object CopyObject(object value)
{
return CopyObject(value.GetType(), value);
}

/// <summary>
/// Copies all properties of an object
/// </summary>
/// <param name="baseType">type</param>
/// <param name="value">ori object</param>
/// <returns></returns>
public static object CopyObject(Type baseType, object value)
{
var result = DefineObjectFrom(baseType);
var resultType = result.GetType();
var ps = baseType.GetProperties();
foreach (var p in ps)
{
#if NET40 || NET20
var data = p.GetValue(value,null);
resultType.GetProperty(p.Name).SetValue(result, data,null);
#else
var data = p.GetValue(value);
resultType.GetProperty(p.Name).SetValue(result, data);
#endif
}
return result;
}


/// <summary>
/// Generate Context
/// </summary>
Expand All @@ -133,63 +42,6 @@ private static CompileContext GenerateContext(string name, RuntimeOptions option
return GenerateContext(name, options, null);
}


/// <summary>
/// Constructs a TypeBuilder for a private type with the specified name in this module.
/// </summary>
/// <param name="assemblyName">The display name of the assembly.</param>
/// <returns></returns>
public static TypeBuilder DefineType(string assemblyName)
{
return DefineType(null, null, assemblyName, "DynamicMocule");
}

/// <summary>
/// Constructs a TypeBuilder for a private type with the specified name in this module.
/// </summary>
/// <param name="interfaceType">The interface that this type implements.</param>
/// <param name="parent">The type that the defined type extends.</param>
/// <param name="assemblyName">The display name of the assembly.</param>
/// <returns></returns>
public static TypeBuilder DefineType(Type interfaceType, Type parent, string assemblyName)
{
return DefineType(interfaceType, parent, assemblyName, "DynamicMocule");
}

/// <summary>
/// Constructs a TypeBuilder for a private type with the specified name in this module.
/// </summary>
/// <param name="interfaceType">The interface that this type implements.</param>
/// <param name="parent">The type that the defined type extends.</param>
/// <param name="assemblyName">The display name of the assembly.</param>
/// <param name="moduleName">The name of the dynamic module.</param>
/// <returns></returns>
public static TypeBuilder DefineType(Type interfaceType, Type parent, string assemblyName, string moduleName)
{
AssemblyBuilder assemblyBuilder
#if NET40 || NET20
=AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run);
#else
= AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run);
#endif
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName);
TypeBuilder typeBuilder;
if (parent != null)
{
typeBuilder = moduleBuilder.DefineType(assemblyName, TypeAttributes.Public, parent);
}
else
{
typeBuilder = moduleBuilder.DefineType(assemblyName, TypeAttributes.Public);
}
CustomAttributeBuilder customAttributeBuilder = new CustomAttributeBuilder(typeof(SerializableAttribute).GetConstructor(Type.EmptyTypes), new Type[] { });
typeBuilder.SetCustomAttribute(customAttributeBuilder);
if (interfaceType != null)
{
typeBuilder.AddInterfaceImplementation(interfaceType);
}
return typeBuilder;
}
/// <summary>
/// Create a compilation context.
/// </summary>
Expand Down Expand Up @@ -219,7 +71,7 @@ private static CompileContext GenerateContext(string name, RuntimeOptions option
private static ICompilerResult Compile(ITag[] tags, CompileContext ctx)
{
var baseType = typeof(CompilerResult);
TypeBuilder typeBuilder = DefineType(baseType.GetInterface(nameof(ICompilerResult)), baseType, $"{baseType.Namespace}.Template{ToHashCode(ctx.Name)}");
TypeBuilder typeBuilder = ObjectBuilder.DefineType(baseType.GetInterface(nameof(ICompilerResult)), baseType, $"{baseType.Namespace}.Template{ToHashCode(ctx.Name)}");
var targetMethod = baseType.GetMethodInfo("Render", new Type[] { typeof(TextWriter), typeof(TemplateContext) });
MethodBuilder method = typeBuilder.DefineMethod(targetMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, targetMethod.ReturnType, new Type[] { typeof(TextWriter), typeof(TemplateContext) });
ILGenerator methodGenerator = method.GetILGenerator();
Expand Down
6 changes: 5 additions & 1 deletion src/JinianNet.JNTemplate/CompileTemplate.cs
Expand Up @@ -14,6 +14,7 @@ namespace JinianNet.JNTemplate
/// </summary>
public class CompileTemplate : TemplateBase, ICompileTemplate, ITemplate
{

/// <summary>
/// Initializes a new instance of the <see cref="CompileTemplate"/> class
/// </summary>
Expand Down Expand Up @@ -53,7 +54,7 @@ public virtual void Render(TextWriter writer, TemplateContext context)
{
t.Render(writer, context);
}
catch(System.Exception e)
catch (System.Exception e)
{
context.AddError(e);
}
Expand All @@ -66,5 +67,8 @@ public virtual void Render(System.IO.TextWriter writer)
{
Render(writer, this.Context);
}

/// <inheritdoc />
public bool EnableCompile => true;
}
}
Expand Up @@ -10,7 +10,7 @@ namespace JinianNet.JNTemplate
/// <summary>
/// System fields.
/// </summary>
public class Field
public class Const
{
/// <summary>
/// Version
Expand All @@ -29,5 +29,8 @@ public class Field
internal const string KEY_IN = "in";
internal const string KEY_LAYOUT = "layout";
internal const string KEY_BODY = "body";


internal const string ANONYMOUS_TYPE_CACHE = "Anonymous-Type-";
}
}

0 comments on commit ef49c58

Please sign in to comment.