如何在SQL Server中存储C#的Complex结构体?
我在C#类中定义了包含复数类型的实体:
public class DataToStore { [Key] public int Id { get; set; } public System.Numerics.Complex ComplexValue { get; set; } }
需要将该数据存储到SQL Server数据库,但SQL中没有直接对应的复数类型。用Entity Framework Core生成数据库架构时,创建迁移报错:
The property 'DataToStore.ComplexValue' could not be mapped because it is of type 'Complex', which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
我清楚报错原因,但不知道SQL中该怎么存这个数据。不局限于用EF Core,手动写SQL实现也可以,目标是能把数据存入并原样取出,请问最佳存储方式是什么?是否需要转换类型?
下面是几种可行的存储方案,按需选择:
方案1:拆分实部和虚部分开存储(最推荐)
System.Numerics.Complex本质由两个double类型的**实部(Real)和虚部(Imaginary)**组成,拆分存储是最直观、性能最优的方式:
用EF Core实现
修改实体类,把ComplexValue标记为不映射,拆分出实部和虚部的持久化属性:
public class DataToStore { [Key] public int Id { get; set; } [NotMapped] public System.Numerics.Complex ComplexValue { get => new System.Numerics.Complex(RealPart, ImaginaryPart); set { RealPart = value.Real; ImaginaryPart = value.Imaginary; } } public double RealPart { get; set; } public double ImaginaryPart { get; set; } }
EF Core会自动将RealPart和ImaginaryPart映射到SQL Server的FLOAT类型(对应C#的double),业务代码中直接使用ComplexValue即可,无需关心底层存储转换。
手动SQL实现
直接在表中创建两个FLOAT类型字段:
CREATE TABLE DataToStore ( Id INT PRIMARY KEY IDENTITY(1,1), RealPart FLOAT NOT NULL, ImaginaryPart FLOAT NOT NULL );
插入数据时拆分实部虚部:
INSERT INTO DataToStore (RealPart, ImaginaryPart) VALUES (3.14, 2.71);
查询后在C#中重新构造Complex对象:
var complex = new System.Numerics.Complex(reader.GetDouble(1), reader.GetDouble(2));
方案2:序列化为二进制存储
如果不想拆分字段,可以把Complex对象序列化为二进制,存储到SQL Server的VARBINARY类型中:
用EF Core实现
添加二进制存储属性,ComplexValue标记为不映射,手动实现序列化逻辑:
public class DataToStore { [Key] public int Id { get; set; } [NotMapped] public System.Numerics.Complex ComplexValue { get => DeserializeComplex(ComplexBinary); set => ComplexBinary = SerializeComplex(value); } public byte[] ComplexBinary { get; set; } private byte[] SerializeComplex(System.Numerics.Complex complex) { using var ms = new MemoryStream(); using var writer = new BinaryWriter(ms); writer.Write(complex.Real); writer.Write(complex.Imaginary); return ms.ToArray(); } private System.Numerics.Complex DeserializeComplex(byte[] data) { using var ms = new MemoryStream(data); using var reader = new BinaryReader(ms); var real = reader.ReadDouble(); var imaginary = reader.ReadDouble(); return new System.Numerics.Complex(real, imaginary); } }
EF Core会自动将ComplexBinary映射到SQL Server的VARBINARY(MAX)类型。
手动SQL实现
创建表时定义VARBINARY字段:
CREATE TABLE DataToStore ( Id INT PRIMARY KEY IDENTITY(1,1), ComplexBinary VARBINARY(MAX) NOT NULL );
插入前在C#中完成序列化,查询后反序列化即可还原Complex对象。
方案3:序列化为JSON存储
把Complex序列化为JSON字符串,存储到SQL Server的NVARCHAR或JSON类型中:
用EF Core实现
借助System.Text.Json实现序列化逻辑:
public class DataToStore { [Key] public int Id { get; set; } [NotMapped] public System.Numerics.Complex ComplexValue { get => JsonSerializer.Deserialize<System.Numerics.Complex>(ComplexJson); set => ComplexJson = JsonSerializer.Serialize(value); } public string ComplexJson { get; set; } }
如果使用SQL Server 2016及以上版本,还可以通过Fluent API将ComplexJson映射为原生JSON类型:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<DataToStore>() .Property(d => d.ComplexJson) .HasColumnType("JSON"); }
手动SQL实现
创建表:
CREATE TABLE DataToStore ( Id INT PRIMARY KEY IDENTITY(1,1), ComplexJson NVARCHAR(MAX) NOT NULL );
插入时存入JSON格式字符串(例如{"Real":3.14,"Imaginary":2.71}),查询后反序列化为Complex对象即可。
- 拆分字段:性能最优,支持SQL层面的数值查询/计算(比如筛选实部大于某个值的记录),推荐优先使用。
- 二进制存储:存储空间小,序列化/反序列化速度快,但无法在SQL中直接操作复数的数值属性。
- JSON存储:可读性好,方便手动查看数据,但序列化开销比二进制大;SQL Server 2016+支持JSON查询,但性能仍不如拆分字段。
内容的提问来源于stack exchange,提问作者Jamie Burns

