# EF Core 列元数据提供器
Air.Cloud.EntityFrameWork.Core 提供列元数据扩展点,用于在 DbContext.OnModelCreating 阶段按实体属性动态修改 EF Core 关系型元数据,例如列类型、列名、精度或长度。
Short (en-us): Air.Cloud.EntityFrameWork.Core provides a column metadata extension point that runs during DbContext.OnModelCreating and can adjust EF Core relational metadata such as column type, column name, precision, or length for each entity property.
# 适用场景
- Kingbase 项目需要把
DateTime明确映射为timestamp without time zone。 - Oracle 或其他数据库需要集中处理
string、decimal、DateTime等 CLR 类型的数据库类型。 - 业务项目希望通过特性覆盖个别字段的类型,而不是为每个实体编写完整的
IEntityTypeBuilder<TEntity>。
该扩展点只在 EF Core 模型构建阶段执行。模型已经缓存后,运行时修改注册或规则不会自动刷新;需要动态刷新模型时,应配合 DbContextMode.Dynamic 使用。
# 注册方式
框架不会自动注册默认实现,数据库模块也不会默认启用列元数据改写。业务项目需要显式注册自己的实现:
services.AddSingleton<IColumnMetadataProvider, MyColumnMetadataProvider>();
DefaultColumnMetadataProvider 是可继承的基础类,不会自动生效。
# Kingbase 示例
业务项目可以定义一个 Kingbase 专用提供器,把 DateTime 统一映射为 timestamp without time zone:
using Air.Cloud.EntityFrameWork.Core.Entities.Configures;
namespace FCJ.Practitioners.Repository
{
public class KingbaseColumnMetadataProvider : DefaultColumnMetadataProvider
{
protected override IReadOnlyDictionary<Type, string> TypeMappings => new Dictionary<Type, string>
{
{ typeof(DateTime), "timestamp without time zone" }
};
}
}
然后在业务项目的 Startup.ConfigureServices 中注册,注册时机必须早于 DbContext 第一次创建:
services.AddSingleton<IColumnMetadataProvider, KingbaseColumnMetadataProvider>();
# 类型映射示例
using Air.Cloud.EntityFrameWork.Core.Entities.Configures;
public sealed class MyColumnMetadataProvider : DefaultColumnMetadataProvider
{
protected override IReadOnlyDictionary<Type, string> TypeMappings { get; } = new Dictionary<Type, string>
{
[typeof(DateTime)] = "timestamp without time zone",
[typeof(string)] = "varchar(255)",
[typeof(decimal)] = "numeric(18,2)"
};
}
DateTime? 会按 DateTime 的映射处理,因此不需要为可空值类型重复配置。
# 属性级覆盖
可以使用 ColumnTypeAttribute 覆盖类型映射表:
using Air.Cloud.EntityFrameWork.Core.Entities.Attributes;
public sealed class OrderEntity
{
public DateTime CreatedAt { get; set; }
[ColumnType(typeof(string), "varchar(64)")]
public string? OrderNo { get; set; }
}
默认提供器先应用 TypeMappings,再应用 ColumnTypeAttribute,所以属性级声明拥有最终列类型。
如果只想声明该字段按公共类型映射处理,可以只传 CLR 类型:
[ColumnType(typeof(string))]
public string? CommonCode { get; set; }
这会先校验属性类型是 string,然后使用 TypeMappings[typeof(string)]。如果同时传入数据库类型名称,则使用特性上的类型名称强制覆盖公共映射:
[ColumnType(typeof(string), "varchar(64)")]
public string? OrderNo { get; set; }
# 自定义 Apply
需要改列名或其他 EF Core 元数据时,可以重写 Apply:
public sealed class MyColumnMetadataProvider : DefaultColumnMetadataProvider
{
protected override IReadOnlyDictionary<Type, string> TypeMappings { get; } = new Dictionary<Type, string>
{
[typeof(DateTime)] = "timestamp without time zone"
};
public override void Apply(ColumnMetadataContext context)
{
base.Apply(context);
var columnName = ToSnakeCase(context.PropertyInfo.Name);
context.Property.SetColumnName(columnName);
}
}
ColumnMetadataContext 会提供当前的 ModelBuilder、EntityTypeBuilder、实体元数据、属性元数据、PropertyInfo、DbContext 和 DbContext 定位器。
# 注意事项
- 未注册
IColumnMetadataProvider时,框架不会改变任何列元数据。 - 多次调用
SetColumnType时,后一次会覆盖前一次。 ColumnTypeAttribute的typeof(...)必须与属性类型匹配;可空值类型按基础类型匹配。- 该扩展点面向模型元数据,不负责运行时值转换。
← 数据源配置 MySQL Provider →