需要应用程序设计方面的帮助

发布于 2024-10-09 12:10:34 字数 844 浏览 2 评论 0原文

因此,我希望获得一些有关设计类和存储以下情况数据的最佳方法的反馈:

我有一个名为“任务”的界面,如下所示:

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;}
}

所以我认为我可以在数据库中创建一个任务表,其中包含与 ITask 接口匹配的列和将更具体任务的所有属性推入单个列中的列(或者甚至可以将任务对象序列化为单个列)

或者

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

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

我已经开始研究插件架构和策略模式,但我不知道在哪里可以解决我存储和访问数据的问题。

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

So, I'd love some feedback on the best way to design the classes and store the data for the following situation:

I have an interface called Tasks that looks like this:

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

I would like the ability to create different types of Tasks depending on who is using the application...for example:

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;}
}

So the way I see it I can create a Tasks table in the database with columns that match the ITask interface and a column that shoves all of the properties of more specific tasks in a single column (or maybe even serialize the task object into a single column)

OR

Create a table for each task type to store the properties that are unique to that type.

I really don't like either solution right now. I need to be able to create different types of Tasks ( or any other class) that all share a common core set of properties and methods through a base interface, but have the ability to store their unique properties in a fashion that is easy to search and filter against without having to create a bunch of database tables for each type.

I've starting looking into Plug-In architecture and the strategy pattern, but I don't see where either would address my problem with storing and accessing the data.

Any help or push in the right direction is greatly appreciated!!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

您的好友蓝忘机已上羡 2024-10-16 12:10:34

你的第二种方法(每种类型一个表)是解决这个问题的规范方法 - 虽然它需要更多的努力来实现,但它更适合大多数数据库的关系模型,并保持一致和内聚数据的表示。每个具体类型使用一个表的方法效果很好,并且与大多数 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的好处是它很容易实现,并且保留行之间的一一对应关系,使查询变得容易。不幸的是,这也有一些缺点。首先,列名完全不提供信息,您必须参考一些元数据字典才能了解哪些列映射到哪种类型的任务的哪个字段。第二个缺点是大多数数据库将表上的列数限制为相对较小的数字(通常为 50 - 300 列)。因此,您只能使用有限的数字、字符串、日期时间等列。因此,如果您输入的日期时间字段数量超出了表支持的数量,则必须使用字符串字段来存储日期,或者创建多个扩展表。

预先警告,大多数 ORM 库不提供对这两种建模模式的内置支持。

Your second approach (one table per type) is the canonical way to solve this problem - while it requires a bit more effort to implement it fits better with the relational model of most databases and preserves a consistent and cohesive representation of the data. The approach of using one table per concrete type works well, and is compatible with most ORM libraries (like EntityFramework and NHibernate).

There are, however, a couple of alternative approaches sometimes used when the number of subtypes is very large, or subtypes are created on the fly.

Alternative #1: The Key-Value extension table. This is a table with one row per additional field of data you wish to store, a foreign key back to the core table (Task), and a column that specifies what kind of field this is. It's structure is typically something like:

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)

Alternative #2: The Type-Mapped Extension Table. In this alternative, you create a table with a bunch of nullable columns of different data types (numbers, strings, date/time, etc) with names like DATA01, DATA02, DATA03 ... and so on. For each kind of Task, you select a subset of the columns and map them to particular fields. So, DATA01 may end up being the BuildVersion for a SoftwareTask and an AccountName for a SalesTask. In this approach, you must manage some metadata somewhere that control which column you map specific fields to. A type-mapped table will often look something like:

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...

The main benefit of option #1 is that you can dynamically add as many different fields as you need, and you can even support a level of backward compatibility. A significant downside, however, is that even simple queries can become challenging because fields of the objects are pivoted into rows in the table. Unpivoting turns out to be an operation that is both complicated and often poorly performing.

The benefits of option #2 is that it's easy to implement, and preserves a 1-to-1 correspondence betweens rows, making queries easy. Unfortunately, there are some downsides to this as well. The first is that the column names are completely uninformative, and you have to refer to some metadata dictionary to understand which columns maps to which field for which type of task. The second downside is that most databases limit the number of columns on a table to a relatively small number (usually 50 - 300 columns). As a result, you can only have so many numeric, string, datetime, etc columns available to use. So if you type ends up having more DateTime fields than the table supports you have to either use string fields to store dates, or create multiple extension tables.

Be forewarned, most ORM libraries do not provide built-in support for either of these modeling patterns.

一场信仰旅途 2024-10-16 12:10:34

您可能应该借鉴 ORM 如何处理这个问题,例如 TPH/TPC/TPT

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

You should probably take a lead from how ORMs deal with this, like TPH/TPC/TPT

Given that ITask is an interface you should probably go for TPC (Table per Concrete Type). When you make it a baseclass, TPT and TPH are also options.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文