C# 针对实体允许自定义属性的最佳设计
我对C#和WPF比较陌生,正在着手创建一个库存管理系统。该系统将有客户,所有客户都有不同的库存需求 库存将存储在一个表中,表中有列,如ProductId、CustomerId、BookQty、OnHandQty、OnOrderQty等。每个客户都希望针对这些产品存储不同的属性,如批号、使用日期、产品日期等。我希望使其尽可能动态,以便在设置客户时,我们定义他们希望如何管理库存 我的问题是,从数据库的角度来看,根据库存存储这些属性的最佳设计是什么C# 针对实体允许自定义属性的最佳设计,c#,wpf,nhibernate,C#,Wpf,Nhibernate,我对C#和WPF比较陌生,正在着手创建一个库存管理系统。该系统将有客户,所有客户都有不同的库存需求 库存将存储在一个表中,表中有列,如ProductId、CustomerId、BookQty、OnHandQty、OnOrderQty等。每个客户都希望针对这些产品存储不同的属性,如批号、使用日期、产品日期等。我希望使其尽可能动态,以便在设置客户时,我们定义他们希望如何管理库存 我的问题是,从数据库的角度来看,根据库存存储这些属性的最佳设计是什么 在股票表中有单独的字段,如TagString1、T
- 在股票表中有单独的字段,如
TagString1、TagString2、TagString3、TagDate1、TagDate2、TagDate3、TagInt1、TagIn2、TagInt3
- 具有单个字段,只需对非字符串值执行强制转换,例如
(并具有一个查找表,以定义哪些列以及列标题中存在哪些数据类型)Tag1、Tag2、Tag3
- 把这些放在一张单独的桌子上
- XML数据类型
- 键/对值表
- 动态更改表结构(认为这是一个坏主意,但将此想法抛诸脑后)
解决方案需要是可索引的(这是一个词吗?),性能是值得关注的。大多数数据查询都将通过NHibernate完成,因此我并不十分担心通过原始SQL进行查询的复杂性。首先,这与WPF无关 至于数据库设计,我将创建一个
CustomerAttributes
带有Id、AttributeName和AttributeType(日期、字符串、数字等)
然后创建一个包含ProductAttributes
的表,其中包含ProductId、CustomerId、AttributeId和AttributeValue列
如果您希望解决方案可索引,请避免使用XML。如果您确实在业务逻辑(搜索/显示和报告除外)中使用了这些值(批号、按日期使用、产品日期),则应将它们作为适当的列添加到表中 但是,如果它们只用于搜索/显示或报告,我更喜欢使用垂直表(stockId、key、value),这样它会更灵活,即使您可以走得更远一点,使用每种类型的列,例如(stockId、key、stringValue、DateValue等),这样您就可以优化查询,而不必进行强制转换或转换
在搜索、显示和报告方面,我能想到的最佳方法。这是一个非常常见的场景,在现实世界中,您将看到您提到的所有解决方案(叹气)。假设它不是一个玩具应用程序,那么您将拥有(或者某些客户可能拥有)许多自定义属性,并且您还需要围绕这些属性执行实际工作(查询、更新等) 在库存表中有单独的字段 这是最简单的解决方案(这是它与良好性能结合在一起的唯一好处)。但是,它有许多缺点:
- 维护是一件痛苦的事情,当您的空闲列用完时,您将需要更改数据库模式
- 如果需要新的列类型(例如,存储产品的仓库的地理位置),则需要添加更多列
- 它无法解释自己。当您在查询中看到
时,您不了解其中包含的内容,这对于查询编写和调试来说很容易出错。不要低估这一点:您可以节省几天的开发时间,但每次自定义安装时都会支付这些费用TagInt1
- 如果每个客户对该列都有自己的用途,那么您就不能编写通用实用程序(例如迁移、更新和导入/导出)。对于每个客户,它们都需要定制(同样,在安装过程中您需要支付)
- 它浪费了DB资源(但它们可能很少,可以忽略,DB引擎非常适合为空字段优化磁盘空间)
- 您的源代码将具有更多配置点,例如,要构建查询,您需要读取映射表,将“批次代码”(用户在UI中看到的)转换为
(您在查询中编写的内容)。您还需要执行类型检查(以验证用户输入),然后映射表还必须包含列类型。更多的代码需要编写,更多的代码需要测试,更多的bug需要修复TagString1
SELECT
M.Name, A.Value
FROM
ProductAttribute AS A
INNER JOIN
ProductAttributeMetadata AS M ON M.Id = A.KeyId
WHERE
M.ProductId = @ProductId
Id, KeyId, DisplayOrder, DisplayName, DbValue
---------------------------------------------
-- Tags
SELECT
*
FROM
Product
WHERE
TagString3 LIKE '001-000-A%'
-- Dynamic
SELECT
*
FROM
Product
WHERE
LotCode LIKE '001-000-A%'
-- Key/Value
SELECT
P.*
FROM
Product AS P
INNER JOIN
ProductAttribute AS A ON P.Id = A.ProductId
INNER JOIN
ProductAttributeMetadata AS M ON A.KeyId = M.Id
WHERE
M.Name = 'LotCode' AND A.Value LIKE '001-000-A%'
-- XML
SELECT
*
FROM
Product
WHERE
XmlAttributes.exist('/data[contains(LotCode,"001-000-A")]') = 1