我甚至不確定這是否可能..在一個方法中,我創建了一個動態程序集,定義了一個類型,併爲構造函數發送了IL那種類型。此方法以IEnumerable<Action>
作爲參數,我希望能夠在我生成的類中使用該引用。如何在使用IL動態生成的類中使用本地對象Emit
我已經寫了一些數據庫遷移助手與FluentMigrator或MigratorDotNet一起工作,我試圖實現單元測試來驗證正確的功能。藉助FluentMigrator,我可以實例化一個跑步者並將其傳遞給Migration類的實例。但是,使用MigratorDotNet時,它需要我爲它傳遞一個程序集,它會掃描Migration類以實例化並運行 - 因此是動態生成。
這是基本I類動態是子類:
public class ActionMigration : Migration
{
private readonly IEnumerable<Action<Migration>> _up;
private readonly IEnumerable<Action<Migration>> _down;
public ActionMigration(Action<Migration> migration) : this(migration, migration) { }
public ActionMigration(Action<Migration> up, Action<Migration> down) : this(new[] { up }, new[] { down }) { }
public ActionMigration(IEnumerable<Action<Migration>> actions) : this(actions, actions) { }
public ActionMigration(IEnumerable<Action<Migration>> up, IEnumerable<Action<Migration>> down) { _up = up; _down = down; }
public override void Down() => _down?.ForEach(m => m(this));
public override void Up() => _up?.ForEach(m => m(this));
}
這是我的代碼生成的動態實現:
private Assembly BuildMigrationAssembly(IEnumerable<Action<Migration>> actions)
{
var assemblyName = $"mdn_test_{Guid.NewGuid()}";
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyBuilder.GetName().Name, assemblyName + ".dll");
BuildMigrationClass(moduleBuilder, 1, actions);
return assemblyBuilder;
}
private void BuildMigrationClass(ModuleBuilder moduleBuilder, long version, IEnumerable<Action<Migration>> actions)
{
var baseType = typeof(ActionMigration);
var typeBuilder = moduleBuilder.DefineType($"Migration{version}",
TypeAttributes.Public | TypeAttributes.Class |
TypeAttributes.AutoClass | TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
baseType);
var migAttrType = typeof(MigrationAttribute);
var migAttrCtor = migAttrType.GetConstructor(new[] { typeof(long) });
typeBuilder.SetCustomAttribute(migAttrCtor, BitConverter.GetBytes(version));
var baseCtor = baseType.GetConstructor(new[] { typeof(IEnumerable<Action<Migration>>) });
var ctor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);
var ilg = ctor.GetILGenerator();
ilg.Emit(OpCodes.Ldarg_0);
// how can I pass the local 'actions' object to the base constructor here?
ilg.Emit(OpCodes.Call, baseCtor);
ilg.Emit(OpCodes.Nop);
ilg.Emit(OpCodes.Nop);
ilg.Emit(OpCodes.Ret);
}
我打開了一個項目,並創造了一些樣品子班檢查:
namespace MdnTest
{
[Migration(1)]
public class Migration1 : EasyMigrator.Tests.Integration.Migrators.MigratorDotNet.ActionMigration
{
public Migration1() : base(new List<Action<Migration>>()) { }
}
}
或者:
namespace MdnTest
{
[Migration(1)]
public class Migration1 : EasyMigrator.Tests.Integration.Migrators.MigratorDotNet.ActionMigration
{
static private readonly IEnumerable<Action<Migration>> _actions;
public Migration1() : base(_actions) { }
}
}
這是IL它們產生:
.class public auto ansi beforefieldinit
MdnTest.Migration1
extends [EasyMigrator.Tests]EasyMigrator.Tests.Integration.Migrators.MigratorDotNet/ActionMigration
{
.custom instance void [Migrator.Framework]Migrator.Framework.MigrationAttribute::.ctor(int64)
= (01 00 01 00 00 00 00 00 00 00 00 00) // ............
// int64(1) // 0x0000000000000001
.method public hidebysig specialname rtspecialname instance void
.ctor() cil managed
{
.maxstack 8
// [14 31 - 14 66]
IL_0000: ldarg.0 // this
IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<class [mscorlib]System.Action`1<class [Migrator.Framework]Migrator.Framework.Migration>>::.ctor()
IL_0006: call instance void [EasyMigrator.Tests]EasyMigrator.Tests.Integration.Migrators.MigratorDotNet/ActionMigration::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<class [mscorlib]System.Action`1<class [Migrator.Framework]Migrator.Framework.Migration>>)
IL_000b: nop
// [14 67 - 14 68]
IL_000c: nop
// [14 69 - 14 70]
IL_000d: ret
} // end of method Migration1::.ctor
} // end of class MdnTest.Migration1
或者:
.class public auto ansi beforefieldinit
MdnTest.Migration1
extends [EasyMigrator.Tests]EasyMigrator.Tests.Integration.Migrators.MigratorDotNet/ActionMigration
{
.custom instance void [Migrator.Framework]Migrator.Framework.MigrationAttribute::.ctor(int64)
= (01 00 01 00 00 00 00 00 00 00 00 00) // ............
// int64(1) // 0x0000000000000001
.field private static initonly class [mscorlib]System.Collections.Generic.IEnumerable`1<class [mscorlib]System.Action`1<class [Migrator.Framework]Migrator.Framework.Migration>> _actions
.method public hidebysig specialname rtspecialname instance void
.ctor() cil managed
{
.maxstack 8
// [15 31 - 15 45]
IL_0000: ldarg.0 // this
IL_0001: ldsfld class [mscorlib]System.Collections.Generic.IEnumerable`1<class [mscorlib]System.Action`1<class [Migrator.Framework]Migrator.Framework.Migration>> MdnTest.Migration1::_actions
IL_0006: call instance void [EasyMigrator.Tests]EasyMigrator.Tests.Integration.Migrators.MigratorDotNet/ActionMigration::.ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<class [mscorlib]System.Action`1<class [Migrator.Framework]Migrator.Framework.Migration>>)
IL_000b: nop
// [15 46 - 15 47]
IL_000c: nop
// [15 48 - 15 49]
IL_000d: ret
} // end of method Migration1::.ctor
} // end of class MdnTest.Migration1
我不知道如何將這種適應我想要實現。可我只是在IL加載指令中插入一個對動態程序集上下文之外存在的本地對象的引用?表達式可以幫助嗎?也許試圖在構造函數中傳遞它是一種錯誤的方式 - 也許是改寫up和down的實現(我可以像一個Action的函數句柄併發出一個調用它,而不是傳入參考IEnumerable?)。
我很熟悉程序集和IL,並在審查操作後,我開始認爲我可能無法做我想做的事情。
那麼,這是甚至可能的,如果有的話,是否有人可以推動我朝着正確的方向發展?
對於好奇,完整的代碼是here。
不知道這是否讓你更接近或沒有:http://stackoverflow.com/q/8419839 –
@CALohse它沒有。我從來沒有找到一種方法來直接發佈任何類型的引用到我發佈IL時的動作 - 但是工作是將該動作存儲在靜態字典中,並在動態程序集中發出調用以致電回到一個靜態函數中,該靜態函數返回動態類的正確動作然後調用。我必須在代碼中添加一個答案。 –