Skip to content

Instantly share code, notes, and snippets.

@QiMata
Created November 12, 2019 13:05
Show Gist options
  • Save QiMata/368efca0829362327c8e6ea8b6678e9b to your computer and use it in GitHub Desktop.
Save QiMata/368efca0829362327c8e6ea8b6678e9b to your computer and use it in GitHub Desktop.
Showcasing how to generate proto files dynamically using protobuf-net
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);
}
}
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<>));
}
}
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