Created
November 12, 2019 13:05
-
-
Save QiMata/368efca0829362327c8e6ea8b6678e9b to your computer and use it in GitHub Desktop.
Showcasing how to generate proto files dynamically using protobuf-net
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ClassGenerator | |
{ | |
private readonly ModuleBuilder _moduleBuilder; | |
public ClassGenerator() | |
{ | |
var an = new AssemblyName("DynamicProtoAssembly"); | |
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an,AssemblyBuilderAccess.Run); | |
_moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicProtoModule"); | |
} | |
public Type CreateType(Type typeToCopy) | |
{ | |
TypeBuilder tb = _moduleBuilder.DefineType(typeToCopy.Name + "Proto", | |
TypeAttributes.Public | | |
TypeAttributes.Class | | |
TypeAttributes.AutoClass | | |
TypeAttributes.AnsiClass | | |
TypeAttributes.BeforeFieldInit | | |
TypeAttributes.AutoLayout, | |
null); | |
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName); | |
var ci = typeof(ProtoContractAttribute).GetConstructor(new Type[0]); | |
var builder = new CustomAttributeBuilder(ci,new object[0]); | |
tb.SetCustomAttribute(builder); | |
var propertiesToCopy = typeToCopy.GetProperties(); | |
for (int i = 0; i < propertiesToCopy.Length; i++) | |
{ | |
var propertyInfo = propertiesToCopy[i]; | |
CreateProperty(tb,propertyInfo.Name,propertyInfo.PropertyType,i); | |
} | |
return tb.CreateType(); | |
} | |
private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType, int i) | |
{ | |
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private); | |
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null); | |
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes); | |
ILGenerator getIl = getPropMthdBldr.GetILGenerator(); | |
getIl.Emit(OpCodes.Ldarg_0); | |
getIl.Emit(OpCodes.Ldfld, fieldBuilder); | |
getIl.Emit(OpCodes.Ret); | |
MethodBuilder setPropMthdBldr = | |
tb.DefineMethod("set_" + propertyName, | |
MethodAttributes.Public | | |
MethodAttributes.SpecialName | | |
MethodAttributes.HideBySig, | |
null, new[] { propertyType }); | |
ILGenerator setIl = setPropMthdBldr.GetILGenerator(); | |
Label modifyProperty = setIl.DefineLabel(); | |
Label exitSet = setIl.DefineLabel(); | |
setIl.MarkLabel(modifyProperty); | |
setIl.Emit(OpCodes.Ldarg_0); | |
setIl.Emit(OpCodes.Ldarg_1); | |
setIl.Emit(OpCodes.Stfld, fieldBuilder); | |
setIl.Emit(OpCodes.Nop); | |
setIl.MarkLabel(exitSet); | |
setIl.Emit(OpCodes.Ret); | |
propertyBuilder.SetGetMethod(getPropMthdBldr); | |
propertyBuilder.SetSetMethod(setPropMthdBldr); | |
var ci = typeof(ProtoMemberAttribute).GetConstructor(new [] { typeof(int) }); | |
var builder = new CustomAttributeBuilder(ci, new object[] { i + 1 }); | |
propertyBuilder.SetCustomAttribute(builder); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ContextFinder | |
{ | |
public IEnumerable<Type> GetAllTypesInContextDbSets(Assembly assembly) | |
{ | |
return GetContextTypes(assembly) | |
.Select(x => GetDataSetTypes(x)) | |
.SelectMany(x => x) | |
.Select(x => x.GetGenericArguments()[0]); | |
} | |
private IEnumerable<Type> GetContextTypes(Assembly assembly) | |
{ | |
return assembly.GetTypes() | |
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(DbContext))); | |
} | |
private IEnumerable<Type> GetDataSetTypes(Type context) | |
{ | |
return context.GetProperties() | |
.Select(x => x.PropertyType) | |
.Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(DbSet<>)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
if (args.Length < 1) | |
{ | |
Console.WriteLine("The first argument should be a path to the assembly"); | |
return; | |
} | |
Assembly assembly = Assembly.LoadFile(args[0]); | |
ContextFinder finder = new ContextFinder(); | |
var types = finder.GetAllTypesInContextDbSets(assembly); | |
ClassGenerator generator = new ClassGenerator(); | |
var protoTypes = types.Select(x => generator.CreateType(x)); | |
foreach (var protoType in protoTypes) | |
{ | |
Console.WriteLine(GenerateProtoFile(protoType)); | |
} | |
} | |
private static string GenerateProtoFile(Type protoType) | |
{ | |
MethodInfo methodInfo = typeof(Serializer).GetMethod(nameof(Serializer.GetProto),new [] {typeof(ProtoSyntax)}); | |
MethodInfo genericMethod = methodInfo.MakeGenericMethod(protoType); | |
return (string) genericMethod.Invoke(null, new object[] { ProtoSyntax.Proto3 }); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment