Entity Framework自定义ModelBuilder调用UseModel时创建异常排查
Let me break down what's likely happening here and how to fix it:
First off, that error about AppUser.AvatarImage (a string type) not being mapped is a huge clue—EF Core does support string as a primitive type, so this almost always means your test's model configuration isn't matching what your production AppDbContext uses. Here's how to troubleshoot and fix it:
1. Check for Missing Configuration in Your Test Setup
Take a look back at your original AppDbContext.OnModelCreating method. Chances are, there's code handling AppUser.AvatarImage that you didn't replicate in your test's manual ModelBuilder setup. For example:
// Original OnModelCreating might have something like this modelBuilder.Entity<AppUser>() .Ignore(u => u.AvatarImage); // Or some explicit mapping configuration
If this code exists in production but not in your test, EF Core will treat AvatarImage as a regular entity property and fail to map it (even though it's a string) because it's missing that critical configuration step.
2. Ensure All Configuration Classes Are Loaded
When you call ApplyConfigurationsFromAssembly in your test, double-check that:
- Your
AppUserconfiguration class (e.g.,AppUserConfiguration) inherits fromIEntityTypeConfiguration<AppUser> - The configuration class is in the same assembly as your other entity configurations (or you're explicitly passing the correct assembly to the method)
- The configuration class has a public access modifier (EF Core can't discover internal/private configuration classes)
3. Unify Configuration Logic to Avoid Drift
The best way to prevent this kind of mismatch is to extract your shared model configuration into a reusable extension method, so both production and test code use the exact same setup. Here's how:
Create a static extension class:
public static class ModelBuilderExtensions { public static void ConfigureApplicationModels(this ModelBuilder modelBuilder) { // Load all entity configurations from the assembly modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly); // Add your Tenant.Profile JSON conversion logic modelBuilder.Entity<Tenant>() .Property(t => t.Profile) .HasConversion( v => JsonSerializer.Serialize(v, null), v => JsonSerializer.Deserialize<ProfileType>(v, null)); // Add any other global configuration from your original OnModelCreating here } }
Update your production AppDbContext to use this extension:
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.ConfigureApplicationModels(); }
Then use the same extension in your test setup:
// In TestWithSqlite.cs var modelBuilder = new ModelBuilder(); modelBuilder.ConfigureApplicationModels(); // Reuse the exact same logic var model = modelBuilder.Model; var options = new DbContextOptionsBuilder<AppDbContext>() .UseSqlite("DataSource=:memory:") .UseModel(model) .Options;
4. Simplify Your Test Setup (Alternative Approach)
If you don't need to manually build the model for your test, you can skip the UseModel step entirely and let the context handle configuration naturally. This is often simpler for unit tests:
var options = new DbContextOptionsBuilder<AppDbContext>() .UseSqlite("DataSource=:memory:") .Options; using var context = new AppDbContext(options); context.Database.OpenConnection(); // Required for in-memory Sqlite context.Database.EnsureCreated();
This way, the context will run its full OnModelCreating logic automatically, so you don't have to worry about missing configuration steps.
Content的提问来源于stack exchange,提问作者Yasir

