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
相关产品推荐
相关产品推荐

