【问题标题】:Need Help With Application Design在应用程序设计方面需要帮助
【发布时间】:2010-12-29 21:15:36
【问题描述】:

所以,我希望得到一些关于在以下情况下设计类和存储数据的最佳方式的反馈:

我有一个名为 Tasks 的界面,如下所示:

interface ITask
{
    int ID{ get; set;}
    string Title {get; set;}
    string Description{get; set;}
}

我希望能够根据谁在使用应用程序来创建不同类型的任务...例如:

public class SoftwareTask: ITask
{
    //ITask Implementation
    string BuildVersion {get; set;}
    bool IsBug {get; set;}

}

public class SalesTask: ITask
{
    //ITask Implementation
    int AccountID {get; set;}
    int SalesPersonID {get; set;}
}

因此,按照我的看法,我可以在数据库中创建一个 Tasks 表,其中包含与 ITask 接口匹配的列以及将更具体任务的所有属性推送到单个列中的列(或者甚至可以序列化任务对象成单列)

为每个任务类型创建一个表来存储该类型独有的属性。

我现在真的不喜欢这两种解决方案。我需要能够创建不同类型的任务(或任何其他类),它们都通过基本接口共享一组共同的核心属性和方法,但能够以易于搜索的方式存储其独特的属性并进行过滤,而不必为每种类型创建一堆数据库表。

我已经开始研究插件架构和策略模式,但我看不出两者都可以解决我在存储和访问数据方面的问题。

非常感谢任何帮助或朝着正确的方向推动!!!

【问题讨论】:

  • 您应该创建一个二进制字段而不是“isBug”,并且 2 = 错误,4 = 增强 8 = 另一种。这样它可能会变得更加灵活。只是一个建议。这就是所谓的旗帜。更多这里dreamincode.net/forums/topic/15494-c%23-flags
  • 上面列出的属性无关紧要,只是为了说明子类型将有一些独特的属性添加到 ITask。我不会在 SoftwareTask 类上创建 IsBug 属性....我只是想举个例子....但是感谢您查看 Flavio!

标签: c# asp.net design-patterns architecture mvp


【解决方案1】:

您的第二种方法(每种类型一个表)是解决此问题的规范方法 - 虽然它需要更多的努力来实现它更适合大多数数据库的关系模型并保留数据的一致和内聚的表示。每个具体类型使用一个表的方法效果很好,并且与大多数 ORM 库(如 EntityFramework 和 NHibernate)兼容。

但是,当子类型的数量非常多或动态创建子类型时,有时会使用几种替代方法。

备选方案#1:键值扩展表。这是一个表,每个附加字段都有一行您希望存储的数据,外键返回核心表(任务),和一列指定这是什么类型的字段。它的结构通常是这样的:

TaskExt Table
=================
TaskID     : Number (foreign key back to Task)
FieldType  : Number or String (this would be AccountID, SalesPersonID, etc)
FieldValue : String  (this would be the value of the associated field)

替代方案 #2:类型映射扩展表。 在此替代方案中,您创建一个包含一堆不同数据类型(数字、字符串、日期/时间等)的可空列的表名称如 DATA01、DATA02、DATA03 ... 等等。对于每种任务,您选择列的子集并将它们映射到特定字段。因此,DATA01 可能最终成为 SoftwareTask 的 BuildVersion 和 SalesTask 的 AccountName。在这种方法中,您必须在某处管理一些元数据,以控制您将特定字段映射到哪个列。类型映射表通常看起来像:

TaskExt Table
=================
TaskID   : Number  (foreign key back to task)
Data01   : String
Data02   : String
Data03   : String
Data04   : String
Data05   : Number
Data06   : Number
Data07   : Number
Data08   : Number
Data09   : Date
Data10   : Date
Data11   : Date
Data12   : Date
// etc...

选项 #1 的主要好处是您可以根据需要动态添加任意数量的不同字段,甚至可以支持一定程度的向后兼容性。然而,一个显着的缺点是,即使是简单的查询也可能变得具有挑战性,因为对象的字段被旋转到表中的行中。事实证明,反透视是一项既复杂又通常性能不佳的操作。

选项 #2 的好处是它易于实现,并且保留了行之间的 1 对 1 对应关系,使查询变得容易。不幸的是,这也有一些缺点。第一个是列名完全没有信息,您必须参考一些元数据字典才能了解哪些列映射到哪个字段用于哪种类型的任务。第二个缺点是大多数数据库将表上的列数限制为相对较小的数量(通常为 50 - 300 列)。因此,您只能使用这么多的数字、字符串、日期时间等列。因此,如果您键入的 DateTime 字段数超过表支持的数量,则必须使用字符串字段来存储日期,或创建多个扩展表。

请注意,大多数 ORM 库不提供对这两种建模模式的内置支持。

【讨论】:

  • 嗯...是的,我在项目中使用 NHibernate,目前我的大多数类型都映射到一个表。我只是认为为每个“子类型”创建一个表可能会导致性能问题。例如,我将有一个任务列表,它将显示所有打开的任务(无论子类型如何)。如果我有 10 种不同类型的任务,在我看来,访问 11 个表来构建结果集将是一个昂贵的查询。我不喜欢备选方案#2,因为它太快变得令人困惑,而且是维护的噩梦。我也在努力牢记搜索和过滤功能。
  • @Cognotronic:TPT 模型偶尔会出现性能问题 - 但可以通过在超类型表上添加鉴别器字段并应用适当的索引来解决这些问题。我最初不会担心性能 - 从正确建模数据开始。然后进行测试和优化,直到达到可接受的性能水平。
【解决方案2】:

您可能应该带头了解 ORM 如何处理此问题,例如 TPH/TPC/TPT

鉴于 ITask 是一个接口,您可能应该选择 TPC(每个具体类型的表)。当您将其设为基类时,TPT 和 TPH 也是选项。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-09
    • 2011-08-06
    • 1970-01-01
    • 2011-04-27
    • 2017-03-02
    相关资源
    最近更新 更多