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

C# EF Core SQLite:如何持久化Frame3D矩阵属性?

解决方案:将Frame3D矩阵映射到SQLite多列

针对你的场景(数据量小、低更新频率,需ACID和文件存储),以下是几种可行的EF Core实现方案,避免修改Frame3D类或使用原生SQL的繁琐:


方案1:影子属性 + 自动读写拦截(推荐)

通过EF Core的影子属性创建16个数据库列,重写SaveChanges自动写入矩阵值,再通过查询投影重构Frame3D,完全不需要修改现有实体类。

步骤1:定义影子属性

在DbContext的OnModelCreating中为Relationship添加16个对应矩阵元素的影子属性:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var relationshipEntity = modelBuilder.Entity<Relationship>();
    // 为4x4矩阵创建16个影子属性,列名对应Transform_0到Transform_15
    for (int i = 0; i < 16; i++)
    {
        relationshipEntity.Property<double>($"Transform_{i}")
            .HasColumnName($"Transform_{i}");
    }
}

步骤2:重写SaveChanges自动写入矩阵值

在DbContext中重写保存方法,将Frame3D的数组值同步到影子属性:

public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
    SyncTransformToShadowProperties();
    return base.SaveChanges(acceptAllChangesOnSuccess);
}

public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
{
    SyncTransformToShadowProperties();
    return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
}

private void SyncTransformToShadowProperties()
{
    // 跟踪新增或修改的Relationship实体
    var changedRelationships = ChangeTracker.Entries<Relationship>()
        .Where(e => e.State is EntityState.Added or EntityState.Modified);

    foreach (var entry in changedRelationships)
    {
        var transform = entry.Entity.Transform;
        if (transform?.Values == null || transform.Values.Length != 16)
            continue;

        // 将矩阵数组值赋值给对应的影子属性
        for (int i = 0; i < 16; i++)
        {
            entry.Property($"Transform_{i}").CurrentValue = transform.Values[i];
        }
    }
}

步骤3:查询时重构Frame3D

封装查询扩展方法,自动从影子属性组装Frame3D:

public static IQueryable<Relationship> IncludeTransform(this IQueryable<Relationship> query)
{
    return query.Select(r => new Relationship
    {
        // 复制Relationship的其他属性
        Id = r.Id,
        // ... 你的其他实体属性
        Transform = new Frame3D
        {
            Values = new[]
            {
                EF.Property<double>(r, "Transform_0"),
                EF.Property<double>(r, "Transform_1"),
                EF.Property<double>(r, "Transform_2"),
                EF.Property<double>(r, "Transform_3"),
                EF.Property<double>(r, "Transform_4"),
                EF.Property<double>(r, "Transform_5"),
                EF.Property<double>(r, "Transform_6"),
                EF.Property<double>(r, "Transform_7"),
                EF.Property<double>(r, "Transform_8"),
                EF.Property<double>(r, "Transform_9"),
                EF.Property<double>(r, "Transform_10"),
                EF.Property<double>(r, "Transform_11"),
                EF.Property<double>(r, "Transform_12"),
                EF.Property<double>(r, "Transform_13"),
                EF.Property<double>(r, "Transform_14"),
                EF.Property<double>(r, "Transform_15"),
            }
        }
    });
}

使用时直接调用扩展方法即可:

var relationships = await dbContext.Relationships.IncludeTransform().ToListAsync();

方案2:原生SQL(省心之选)

如果你对EF的高级特性没兴趣,原生SQL确实是最直接的方式,适合数据量小的场景:

写入示例

var relationship = new Relationship { /* 初始化其他属性 */ };
var matrixValues = relationship.Transform.Values;

await dbContext.Database.ExecuteSqlRawAsync(
    @"INSERT INTO Relationships (Id, [其他列名], Transform_0, Transform_1, ..., Transform_15)
      VALUES (@Id, @OtherProp, @T0, @T1, @T2, @T3, @T4, @T5, @T6, @T7, @T8, @T9, @T10, @T11, @T12, @T13, @T14, @T15)",
    relationship.Id, relationship.OtherProp,
    matrixValues[0], matrixValues[1], matrixValues[2], matrixValues[3],
    matrixValues[4], matrixValues[5], matrixValues[6], matrixValues[7],
    matrixValues[8], matrixValues[9], matrixValues[10], matrixValues[11],
    matrixValues[12], matrixValues[13], matrixValues[14], matrixValues[15]);

读取示例

先定义一个包含所有列的DTO:

public class RelationshipDto
{
    public int Id { get; set; }
    // 其他属性
    public double Transform_0 { get; set; }
    // ... 直到Transform_15
}

查询后转换为实体:

var dtos = await dbContext.Database.SqlQueryRaw<RelationshipDto>(
    @"SELECT Id, [其他列名], Transform_0, ..., Transform_15 FROM Relationships")
    .ToListAsync();

var relationships = dtos.Select(dto => new Relationship
{
    Id = dto.Id,
    // 其他属性赋值
    Transform = new Frame3D
    {
        Values = new[]
        {
            dto.Transform_0, dto.Transform_1, ..., dto.Transform_15
        }
    }
}).ToList();

方案3:二进制序列化(备选)

如果接受单字段存储,可将矩阵数组序列化为二进制byte[],完全不损失精度:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Relationship>()
        .Property(r => r.Transform)
        .HasConversion(
            transform => TransformToBytes(transform),
            bytes => BytesToTransform(bytes));
}

// 辅助转换方法
private byte[] TransformToBytes(Frame3D transform)
{
    using var ms = new MemoryStream();
    using var writer = new BinaryWriter(ms);
    foreach (var val in transform.Values)
        writer.Write(val);
    return ms.ToArray();
}

private Frame3D BytesToTransform(byte[] bytes)
{
    using var ms = new MemoryStream(bytes);
    using var reader = new BinaryReader(ms);
    var values = new double[16];
    for (int i = 0; i < 16; i++)
        values[i] = reader.ReadDouble();
    return new Frame3D { Values = values };
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 19:24:52