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

在Entity Framework Core中使用C# 9 Record作为实体属性的问题

EF Core 中使用 C# 9 Record 作为值对象的问题解决方法

问题根源

EF Core 默认会将所有引用类型(包括 C# 9 Record)识别为独立实体,而非值对象。这就导致它会对 ClientId 执行实体的验证逻辑:

  1. 首先尝试寻找无参构造函数实例化实体,因此抛出“找不到合适构造函数”的错误;
  2. 当你添加无参构造后,EF 又会要求实体必须有主键或标记为无键,于是触发第二个错误。

解决方案:将 Record 配置为值对象(值转换)

你需要在 DbContext 的 OnModelCreating 方法中明确告诉 EF Core:ClientId 是 Client 实体的一个值对象属性,需要通过值转换映射到数据库列,而非当作独立实体处理。

步骤1:优化 ClientId(增强易用性)

给 ClientId 添加静态工厂方法,方便从数据库存储的编码值反向创建实例,避免直接访问私有字段:

public record ClientId
{
    private readonly byte[] _bytes;

    public ClientId(byte[] bytes)
    {
        if (bytes is null || bytes.Length != 32)
            throw new ArgumentException($"'{nameof(bytes)}' must be 32 bytes long");

        _bytes = bytes;
    }

    public string Value => Base64UrlEncoder.Encode(_bytes);

    // 静态工厂方法:从Base64Url字符串创建ClientId
    public static ClientId FromEncodedString(string encodedValue)
    {
        var bytes = Base64UrlEncoder.DecodeBytes(encodedValue);
        return new ClientId(bytes);
    }
}

步骤2:配置 EF Core 值转换

在你的 DbContext 中,为 Client 实体的 ClientId 属性配置转换规则:

public class YourDbContext : DbContext
{
    public DbSet<Client> Clients { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // 替换为你的数据库连接字符串
        optionsBuilder.UseSqlServer("YourConnectionString");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Client>(entity =>
        {
            // 配置Client实体的主键
            entity.HasKey(c => c.Id);

            // 配置ClientId值对象的转换规则
            entity.Property(c => c.ClientId)
                .HasConversion(
                    // 将ClientId转换为数据库存储的字符串(Base64Url编码)
                    clientId => clientId.Value,
                    // 从数据库字符串反向创建ClientId实例
                    encodedValue => ClientId.FromEncodedString(encodedValue)
                )
                // 指定数据库列类型:Base64Url编码32字节对应44字符长度
                .HasColumnType("varchar(44)")
                // 根据业务需求设置是否必填
                .IsRequired();
        });
    }
}

步骤3:移除无参构造函数

配置值转换后,EF Core 会通过你定义的转换逻辑创建 ClientId 实例,不再需要无参构造函数。你可以删除之前添加的无参构造,保持值对象的约束校验逻辑(确保传入的字节数组是32字节)。

替代方案:直接映射字节数组

如果你希望数据库存储原始字节数组而非字符串,也可以调整转换逻辑直接映射 byte[]:

entity.Property(c => c.ClientId)
    .HasConversion(
        clientId => clientId._bytes,
        bytes => new ClientId(bytes)
    )
    .HasColumnType("varbinary(32)")
    .IsRequired();

注意:这种方式需要确保 _bytes 字段的访问权限允许 EF Core 读取(比如改为 internal,如果 DbContext 和 ClientId 在同一个程序集),或者添加内部方法暴露字节数组。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 16:21:10