I'm using Pomelo.EntityFrameworkCore.MySql (3.1.1) to save some data to MySql. When the context is first configured I'm getting this exception:
Exception thrown: 'System.MissingFieldException' in Pomelo.EntityFrameworkCore.MySql.dll
Field not found: 'Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.AmbientTransactionWarning'
Here's my 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));
}
}
Possible complicating factor: The app is an addin for Autodesk Revit. I've had some dll loading issues which I believe I've worked out, but it is a non-standard environment which could be causing issues. I've verified that Microsoft.EntityFrameworkCore.Relational
, the dll that provides AmbientTransactionWarning
, is loaded when UseMySql
is called. Also, while VS is paused on the exception, if I enter Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.AmbientTransactionWarning
in the Immediate window, I don't get an error. I also have another stand-alone WPF app that uses the same database model and DbContext object, which communicates with the database just fine.
I'm not sure how to proceed in debugging this. Thanks!
Edit
Both the addin and Revit (2020) are using .NET Framework 4.7.2.
Stacktrace:
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)
Library Loading
Typically Revit addins load their dependencies automatically as needed. Occasionally not, though, so I have added the following resolver which manually loads assemblies which in past runs have failed to load automatically:
// 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;
}
The more I look at this the more it seems that somehow the field in question is available in my main assembly but not in EntityFrameworkCore. I'm not sure why or how that would be the case. I've also tried using IlMerge to combine various parts of the addin, but haven't been able to get anything working in that direction.
This is likely an issue in resolving dependent assemblies.
You probably want to debug your ResolveMissingAssemblies()
method (or log all assemblies that your event handler is unable to resolve).
Also output/take a look at the ResolveEventArgs.RequestingAssembly
property, that tells you what assembly the current one is a dependency of (to understand the dependency tree).
The Microsoft.EntityFrameworkCore.Relational
assembly e.g. depends on Microsoft.EntityFrameworkCore
, which depends on 10 other libraries (some of which depend on other libraries again).
A simple way to ensure they are all being loaded, is to make your event handler a bit more generic:
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;
}