在 Revit 插件中使用 EF Core 的 MissingFieldException (AmbientTransactionWarning)

MissingFieldException (AmbientTransactionWarning) using EF Core in Revit addin

我正在使用 Pomelo.EntityFrameworkCore.MySql (3.1.1) 将一些数据保存到 MySql。首次配置上下文时,出现此异常:

Exception thrown: 'System.MissingFieldException' in Pomelo.EntityFrameworkCore.MySql.dll

Field not found: 'Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.AmbientTransactionWarning'

这是我的 OnConfiguring:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    if (!optionsBuilder.IsConfigured)
    {
        // This works just fine, even though that type is then not available in `UseMySql`.
        var test = Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.AmbientTransactionWarning; 

        // Exception thrown here.
        optionsBuilder.UseMySql("server=localhost;database=test;uid=user;pwd=<password>;TreatTinyAsBoolean=true;", x => x.ServerVersion(new Version(5, 7, 29), ServerType.MySql)); 
    }
}

可能的复杂因素:该应用程序是 Autodesk Revit 的插件。我遇到了一些 dll 加载问题,我相信我已经解决了,但这是一个可能导致问题的非标准环境。我已经验证 Microsoft.EntityFrameworkCore.Relational,提供 AmbientTransactionWarning 的 dll 在调用 UseMySql 时被加载。此外,当 VS 因异常而暂停时,如果我在立即 window 中输入 Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.AmbientTransactionWarning,我不会收到错误。我还有另一个独立的 WPF 应用程序,它使用相同的数据库模型和 DbContext 对象,可以很好地与数据库通信。

我不确定如何进行调试。谢谢!

编辑

插件和 Revit (2020) 都使用 .NET Framework 4.7.2。

堆栈跟踪:

Field not found: 'Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.AmbientTransactionWarning'.

   at Microsoft.EntityFrameworkCore.MySqlDbContextOptionsExtensions.ConfigureWarnings(DbContextOptionsBuilder optionsBuilder)

   at Microsoft.EntityFrameworkCore.MySqlDbContextOptionsExtensions.UseMySql(DbContextOptionsBuilder optionsBuilder, String connectionString, Action`1 mySqlOptionsAction)

   at <MyAssembly>.DatabaseContext.OnConfiguring(DbContextOptionsBuilder optionsBuilder)

库加载

通常 Revit 插件会根据需要自动加载它们的依赖项。不过,有时不会,所以我添加了以下解析器,它手动加载在过去运行中无法自动加载的程序集:

    // Executed on first run of addin.
    AppDomain.CurrentDomain.AssemblyResolve += ResolveMissingAssemblies;

...

private System.Reflection.Assembly ResolveMissingAssemblies(object sender, ResolveEventArgs args)
{
    string[] dlls = new[]
    {
        "System.Memory",
        "System.Buffers",
        "System.Threading.Tasks.Extensions",
        "System.Runtime.CompilerServices.Unsafe",
        "Microsoft.EntityFrameworkCore.Relational",
        "Microsoft.EntityFrameworkCore",
        "MySqlConnector",
    };

    string dll = dlls.FirstOrDefault(name => args.Name.Contains(name));

    if (!string.IsNullOrEmpty(dll))
    {
        string filename = Path.Combine(
            Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
            $"{dll}.dll");

        if (File.Exists(filename))
        {
            return System.Reflection.Assembly.LoadFrom(filename);
        }
    }

    return null;
}

我看得越多,就越觉得有问题的字段在我的主程序集中可用,但在 EntityFrameworkCore 中不可用。我不确定为什么会这样或怎么会这样。我也尝试过使用 IlMerge 来组合插件的各个部分,但一直无法在那个方向上进行任何工作。

这可能是解决依赖程序集的问题。

您可能想要调试您的 ResolveMissingAssemblies() 方法(或记录您的事件处理程序无法解析的所有程序集)。

另外output/take 看看ResolveEventArgs.RequestingAssembly 属性,它告诉你当前程序集是哪个程序集的依赖项(了解依赖项树)。

Microsoft.EntityFrameworkCore.Relational 程序集,例如依赖于 Microsoft.EntityFrameworkCore,它依赖于其他 10 个库(其中一些又依赖于其他库)。

确保它们都被加载的一个简单方法是使您的事件处理程序更通用一些:

private System.Reflection.Assembly ResolveMissingAssemblies(object sender, ResolveEventArgs args)
{
    string filename = Path.Combine(
        Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
        $"{args.Name}.dll");

    return File.Exists(filename))
        ? System.Reflection.Assembly.LoadFrom(filename)
        : null;
}