【问题标题】:Best way to save type of class in database?在数据库中保存类类型的最佳方法?
【发布时间】:2013-08-27 23:08:12
【问题描述】:

在数据库中表示“类型”的好方法是什么?

我有一个基类Action,它被许多子类继承。 ActionIdName 等成员,它们对应于数据库中名为 action 的表中的等效列。所以action 表看起来像这样:

id | name | type 

type 列表示它是什么操作。在我的代码中,它们对应于从父 Action 派生的子类。现在如何将类的类型保存到数据库中的类型字段

db 中的类型列可以是任何数据类型。

选项:

  1. action.GetType().ToString() 保存为数据库中的字符串。并通过使用反射将类型的字符串表示形式转换为其原始类型,从 db 中获取操作类型。 但是如果将来类名发生变化,这将是一个问题。

  2. 创建一个枚举来表示每个子类并用TypeAttribute 装饰它,例如:

    public abstract class Action
    {
        public enum Kind 
        {
            [Type(typeof(ControlAction))]
            ControlAction = 1, 
    
            [Type(typeof(UpdateAction))]
            UpdateAction = 2, 
    
            etc 
        }
    
        public abstract Kind ActionType { get; }
    }
    
    public class ControlAction : Action { public override Kind ActionType { get { return Kind.ControlAction; } } }
    public class UpdateAction : Action { public override Kind ActionType { get { return Kind.UpdateAction; } } }
    //etc
    

    这看起来不错,除了从这里开始我必须为每个类创建一个枚举。而且感觉要做的工作太多了。

  3. 构建一个单独的 <int, Type> 静态哈希表,将类与 int 值联系起来。 可能有点难以阅读。

有没有更好的解决方案?

【问题讨论】:

  • 你想创建一个 ORM 吗?或者你真的需要(可能是边缘情况)将类类型信息放在可以被其他应用程序重用的数据库中?
  • @edsioufi 不,这与 orm 无关。 ControlActionUpdateAction 等每个子操作都是客户端创建的操作。每种操作类型都有自己的一组客户需要的信息。现在我需要一种方法来将客户端创建的操作保存到数据库中。稍后当我从 db 加载这些不同的操作时,我需要将 ControlActionUpdateAction 分开。

标签: c# database-design types


【解决方案1】:

我会从带有哈希表的第 3 个解决方案开始,因为它似乎在设计上更简洁。我会把它的管理委托给数据库!

毕竟,这不是关系数据库最擅长的,在两个实体之间创建关系(在您的情况下是操作和类型)吗?另一个优点是你最终得到了一个规范化的模式(当然,到目前为止,类型表只有一列,即它的名称,但是规范化允许你在将来需要它们时轻松地向类型添加额外的属性,这就是为什么它作为一种设计更干净)。

架构应该是这样的:

动作表

action_id(PK) | name | type_id (int, FK to Type table)

类型表

type_id(PK) | type_name

现在,如果将来类的名称发生变化,您是安全的(从您的第一个字符串类型的命题开始)。实际上,您所要做的就是更改相应Type 表行中的type_name 值,而您的所有Action 行仍将通过type_id 链接到该行,永远不会创建后更改(这里没问题,因为它不具有任何“商业意义”)。

您的哈希表来自 3(Type 表)的可读格式,因为 RDMBS 负责管理哈希表的键(type_id PK)。

请注意,您不必将您的类绑定到对应于 type_id 列的 int 值,而是通过对照 Class 类型从 Type 表中获取 type_id ( type_name)。

【讨论】:

  • 此外,使用字符串作为 ActionType,如果 actionType 已经存在,请检查 Type 表并使用 id 代替(如果不存在,将其添加到 type 表中),您可以创建类似缓存的东西(dictionary 用于缓存带有 id 的 actiontype 名称)
  • @edsioufi,我不确定当类名更改时它有什么帮助。什么应该进入类型表中的type_name? Jeroen 提出了一个很好的观点,这就是你的意思吗?
  • 不,在您编辑后似乎。如何在类型表中进行类型查找?如果type_name 只是类的Type 的字符串表示形式,那么这是行不通的。
  • @edsioufi 我现在明白了。你说的是,如果类名发生变化,我必须更改type_coulmn 中的字符串名称。不错。但我可能会实现一些更简单的东西.. :)
  • @nawfal 酷:)。 TBH 这确实是一个简单的关系数据库用例,您只需要一个连接来获取所有数据并查找要插入。但当然是你的电话:)。
【解决方案2】:

我最终使用了选项 2,但属性更少。像这样的:

public abstract class Action
{
    public enum Kind 
    {
        ControlAction = 1, 

        UpdateAction = 2, 

        etc 
    }

    public abstract Kind ActionType { get; }
}

public class ControlAction : Action { public override Kind ActionType { get { return Kind.ControlAction; } } }
public class UpdateAction : Action { public override Kind ActionType { get { return Kind.UpdateAction; } } }

这样做的最大优势是(即使这意味着更多的输入),它强制将数值与类类型相关联

现在 int 的类只是

var value = (int)instance.ActionType;

非常快。

但是要将 int 转换为类实例(或类类型),我必须为每个子操作类型创建一个实例,并比较其 ActionType 属性以匹配输入的 int 值。这会很慢。但我可以缓存一些东西并让它更快。像

static readonly Dictionary<Action.Kind, Type> actionTypes = 
   GetDefaultInstanceOfAllActions().ToDictionary(x => x.ActionType, x => x.GetType());
public static Action ToAction(this Action.Kind value)
{
    return (Action)Activator.CreateInstance(actionTypes[value]);
}

GetDefaultInstanceOfAllActions 进行一些反射(一次)以获得所有类型的操作(为此我使用类似this answer 的东西)。我什至可以通过going the expression route. 使实例化更快

好处:

  1. 创建新类时更轻松(无属性)。

  2. 强制将 int 绑定到类类型。

  3. 速度适中,缓存充足。

【讨论】:

    【解决方案3】:

    我会选择您的第一个选项并使用反射。您似乎更有可能想要添加新的操作类型而不是更改现有的类名,因此使用反射序列化类型的便利性更有用。 然后,您可以只使用一个实用程序类来序列化操作并从它们的类型字符串中恢复它们。

    【讨论】:

    • 类名可能会改变。我不负责那个。未来的开发人员不应该措手不及。我也不序列化。
    猜你喜欢
    • 1970-01-01
    • 2014-07-10
    • 2021-09-29
    • 2014-08-04
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 2012-07-31
    • 2013-01-08
    相关资源
    最近更新 更多