You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从EF6迁移至EF Core:数据库优先Scaffolding如何保留实体大小写?

Great question! I've run into similar casing headaches when scaffolding EF Core entities from legacy databases, especially with acronyms like XML. Let's walk through how to fix this while keeping your Humanizer-powered singularization logic intact.

Step 1: Fix Humanizer's Singularization to Preserve Case

The default Singularize() method in Humanizer normalizes casing (e.g., turning XMLDetails into XmlDetail). To retain your original uppercase acronyms, use the overload that accepts a keepCurrentCase parameter set to true:

"XMLDetails".Singularize(keepCurrentCase: true); // Returns "XMLDetail"
"Accessories".Singularize(keepCurrentCase: true); // Returns "Accessory"

Step 2: Implement a Custom IPluralizer

Create a custom pluralizer that wraps this case-preserving logic:

using Humanizer;
using Microsoft.EntityFrameworkCore.Scaffolding;

public class CustomCasePreservingPluralizer : IPluralizer
{
    public string Pluralize(string name)
    {
        return name.Pluralize(keepCurrentCase: true);
    }

    public string Singularize(string name)
    {
        return name.Singularize(keepCurrentCase: true);
    }
}

Step 3: Override the Naming Service to Retain Casing

EF Core's default scaffolding naming service still normalizes names (e.g., flattening XML to Xml when generating PascalCase). To fix this, build a custom INamingService that preserves your original casing structure:

using Microsoft.EntityFrameworkCore.Scaffolding;
using System.Text.RegularExpressions;

public class CasePreservingNamingService : INamingService
{
    private readonly INamingService _defaultNamingService;

    public CasePreservingNamingService(INamingService defaultNamingService)
    {
        _defaultNamingService = defaultNamingService;
    }

    public string GetClassName(string tableName, string schemaName)
    {
        // First singularize the table name with our custom logic
        var singularizedTableName = new CustomCasePreservingPluralizer().Singularize(tableName);
        // Convert to PascalCase while keeping acronyms uppercase
        return ConvertToPascalCaseWithAcronyms(singularizedTableName);
    }

    public string GetPropertyName(string columnName)
    {
        // Apply the same casing logic to column names
        return ConvertToPascalCaseWithAcronyms(columnName);
    }

    private string ConvertToPascalCaseWithAcronyms(string input)
    {
        // Split on underscores if your tables use snake_case naming
        var nameParts = input.Split('_', StringSplitOptions.RemoveEmptyEntries);
        
        return string.Concat(nameParts.Select(part => 
        {
            // Keep all-uppercase acronyms (like XML) as-is
            if (Regex.IsMatch(part, @"^[A-Z]{2,}$"))
            {
                return part;
            }
            // For mixed-case parts, ensure first letter is uppercase (preserving inner casing)
            return char.ToUpper(part[0]) + part.Substring(1);
        }));
    }

    // Delegate other naming tasks to the default service
    public string GetNavigationName(string foreignKeyPropertyName)
    {
        return _defaultNamingService.GetNavigationName(foreignKeyPropertyName);
    }

    public string GetRelationshipName(string foreignKeyPropertyName)
    {
        return _defaultNamingService.GetRelationshipName(foreignKeyPropertyName);
    }
}

Step 4: Register Custom Services with EF Core Scaffolding

Create an IDesignTimeServices implementation to tell EF Core to use your custom components:

using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.Extensions.DependencyInjection;

public class CustomScaffoldingDesignTimeServices : IDesignTimeServices
{
    public void ConfigureDesignTimeServices(IServiceCollection services)
    {
        // Replace the default pluralizer
        services.AddSingleton<IPluralizer, CustomCasePreservingPluralizer>();
        
        // Replace the default naming service with our case-preserving version
        services.AddSingleton<INamingService>(sp => 
            new CasePreservingNamingService(sp.GetRequiredService<INamingService>()));
    }
}

Step 5: Re-Run the Scaffold Command

Now when you execute your original scaffold command:

Scaffold-DbContext "MyConnectionString" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
  • Accessories will generate Accessory (correctly singularized with proper casing)
  • XMLDetails will generate XMLDetail (preserving the uppercase XML acronym instead of becoming Xmldetail)

Notes for EF Core 2.0.1

Since you're using EF Core 2.0.1 and targeting .NET Standard 2.0, all these interfaces (IPluralizer, INamingService, IDesignTimeServices) are fully supported. Just make sure you install a compatible version of Humanizer (Humanizer 2.x works perfectly with .NET Standard 2.0).

内容的提问来源于stack exchange,提问作者Arghya Chakrabarty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:46:20