实体框架 - 在 SQL 数据库中保存文档的快照
本文关键字:保存 文档 快照 数据库 框架 SQL 实体 | 更新日期: 2023-09-27 18:35:39
>背景 我正在使用EF6,可以自由更改数据库。
我今天遇到了这个问题。假设我有:
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Address
{
public int Id { get; set; }
public int CompanyId { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
}
public class Invoice
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public bool IsCompleted { get; set; }
}
允许用户更新Company
和Address
。还允许用户更新Invoice
。但由于它是财务文档,如果用户将IsCompleted
标记为 true,它必须以某种方式保存地址的快照。
目前,它是通过以下方式完成的:
public class Invoice
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public bool IsCompleted { get; set; }
//Auditing fields
public string CompanyName { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
}
我认为这很难理解。我在想:
选项 1:将审核保存到其自己的审核表中:
public class Invoice
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int AddressId { get; set; }
public bool IsCompleted { get; set; }
//Null if IsCompleted = false.
public DateTime? CompletedTimeStamp { get; set; }
}
public class CompanyAudit
{
public int CompanyId { get; set; }
public string Name { get; set; }
public DateTime TimeStamp { get; set; }
}
public class AddressAudit
{
public int AddressId { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public DateTime TimeStamp { get; set; }
}
但是,如果我们确实更改了公司和地址的架构,这似乎需要创建很多表,并且需要做很多工作。此外,它不是很强大。如果没有一堆接线,我就无法将其重用于其他文档。但是,这是我主要在互联网上找到的。每个表对应一个审核表。
选项 2:将所有审核保存到同一表中:
public class Audit
{
public int DocumentId { get; set; }
public string DocumentType { get; set; }
public string JsonData { get; set; }
public DateTime TimeStamp { get; set; }
}
但是,这似乎不是很标准。我以前从未将 Json 数据保存到 SQL 数据库。这不好吗?如果是这样,可能会出什么问题?
我应该选择选项 1 还是选项 2?
简答
假设您使用的是 SQL Server,我建议您为该作业创建一些可 XML 序列化的 DTO,并使用 XML 数据类型将 XML 存储在专用列中。
稍长的答案
我已经走上了完全相同的道路。我们需要在打印数据时保存数据的快照,并且涉及许多表格,在此过程中会重复
。要求和评估
我们不想合并其他技术(例如Andreas提出的文件系统或某些NoSQL/文档数据库),而是将所有内容存储在SQL Server中,否则这将具有复杂的备份方案,部署,维护等。
我们想要一些易于理解的东西。新开发人员应熟悉所使用的技术。建筑不应该受到太大的影响。
对于序列化,有几个选项:XML,JSON,二进制格式化程序,DataContractSerializer,Protocol Buffers...我们的要求:易于版本控制(用于添加的属性或关系)、可读性、与 SQL Server 的一致性。
使用所有提到的格式应该可以轻松进行版本控制。可读性:XML和JSON在这里获胜。与SQL Server的一致性:XML在SQL Server中原生支持,这是我们的选择。
实现
我们做了几件事:
在我们的数据库项目中与现有实体并行创建其他 DTO,不是用于 EF,而是用于 XML 序列化。它们使用 XmlAttributes 进行批注,类似于一个复杂的自包含结构,其中包含保存文档数据所需的一切。根
InvoiceSnapshot
类具有支持序列化的Parse
和ToXml
方法。根据需要更新我们的实体以包含快照:
public string InvoiceXml { get; set; } public InvoiceSnapshot Invoice { get { return this.InvoiceXml != null ? InvoiceSnapshot.Parse(this.InvoiceXml) : null; } set { this.InvoiceXml = value != null ? value.ToXml() : null; } }
更新实体配置以创建 XML 列并忽略
InvoiceSnapshot
属性:public class InvoiceEntityConfig : EntityTypeConfiguration<InvoiceEntity> { public InvoiceEntityConfig() { this.Property(c => c.InvoiceXml).HasColumnType("xml"); this.Ignore(c => c.Invoice); } }
修改我们的业务对象,以便它们从实体(可编辑状态)或 XML-DTO(快照、只读状态)加载自身。我们在这两个接口上使用接口,它们有助于简化流程。
进一步的步骤
- 应在单独的标量列中添加常见查询的元数据,并为它们编制索引。仅当您确实要显示发票时,才检索 xml 数据。
- 您可以查看 SQL Server 可以为您执行有关 XML 的操作,尤其是当您需要基于属性进行查询时。它可以为它们编制索引,并且可以在查询中使用 XPath。
- 对 xml 进行签名或哈希处理,以确保快照数据不会被篡改。
选项2
但
更好的方法是存储生成发票 (pdf)以及它在普通文件中的所有还原。
您仍然可以使用帐单表格但是,如果某些客户数据发生变化,则无需担心用户重新打印旧文档。
要存储生成的文档,它几乎相同至于将模型存储在 JsonData 中,但您不需要保护模板和模板生成器的版本。
选项 1 只是蛮力,也许更好释放使用"事件存储"、"事件溯源"和"查询和命令"模式。
public class Document //or DocumentFile
{
public int DocumentId { get; set; }
public string DocumentType { get; set; }
public string FilePath { get; set; }
[Index]
public String Owner { get; set;} //exp. "Customer:Id", "Contact:Id" maybe just int
[Index]
public String Reference { get; set; } //exp. "Invoice:Id", "Contract:Id" maybe just int
public DateTime TimeStamp { get; set; } //maybe int Reversion
}