【问题标题】:Should I use a nullable int when working with int object IDs?在使用 int 对象 ID 时,我应该使用可为空的 int 吗?
【发布时间】:2009-07-07 16:00:36
【问题描述】:

如果我在代码中使用 int 类型的 BookID,它对应于数据库表中的 int 主键字段(我正在使用 C# 和 SQL Server)......这是最佳实践吗?当我在代码中传递 ID 时,使用可为空的 int 并检查 null 以查看 BookID 是否存在:

if (BookID != null) {

或者,分配一个与数据库中的实际值不对应的整数值(例如0或-1)是否更好:

if (BookID > 0) {

编辑: 为了进一步说明,假设我只想返回书的 ID,而不是整个书对象。在数据库中,主键是不可为空的,但是如果我要对不存在的“我的书名”的 BookID 执行查询,那么我将得不到任何结果。这应该反映为 null 吗?

例子:

BookID = GetBookID("my book title");

【问题讨论】:

  • 只获得图书 ID 后的目标是什么?
  • 我会以与在我的答案中获取书籍相同的方式实现 GetBookId() - TryGetBookId() 如果不查找书籍是有效的(可能是因为书名来自用户输入并且可能是错误)或 GetBookId() 如果总是期望该方法返回有效的书本 ID,则抛出异常。如果您真的不喜欢 TryGet 模式,下一个最佳解决方案是返回 null,但仍然比返回 -1 或任何其他幻数好得多。

标签: c# primary-key nullable


【解决方案1】:

如果你有一个允许在数据库中为 null 的值,你应该使用Nullable<T> 并避免引入Magic Numbers。但是如果BookId 是一个(主)键,它应该不能为空(除了它被用作外键)。

更新

对于查询数据库并没有找到匹配记录的问题,有几种解决方案。我更喜欢抛出异常,因为如果应用程序试图获取不存在的记录,这通常表示错误。

Book book = Book.GetById(id);

在这种情况下,如果返回值为 null,您会怎么做?可能这一行之后的代码想要对这本书做一些事情,在 null 的情况下,该方法通常什么也不做

  1. 抛出一个可以在GetById() 中更好地完成的异常(除了调用者可能有更多关于异常的上下文信息)
  2. 立即返回 null 或需要调用者处理的错误代码,但这实际上是在重新发明异常处理系统

如果绝对有效,没有找到匹配的记录,建议使用 TryGet 模式。

Book book;
if (Book.TryGetById(out book))
{
    DoStuffWith(book);
}
else
{
    DoStuffWithoutBook();
}

我相信这比返回 null 更好,因为 null 是一种神奇的值,我不喜欢看到实现细节(引用类型或可空值类型可以采用名为 null 的特殊值)业务逻辑。缺点是你失去了可组合性——你不能再写Book.GetById(id).Order(100, supplier)

以下内容为您提供了可组合性,但有一个非常大的问题 - 这本书可能会在调用 Exists()GetById() 之间被删除,因此我强烈建议不要使用这种模式。

if (Book.Exists(id))
{
    Book book = Book.GetById(id).Order(100, supplier);
}
else
{
    DoStuffWithoutBook();
}

【讨论】:

  • +1 很好的一点是,它通常不应为空,至少在这种情况下是这样。
  • 虽然 BookID 主键实际上在数据库中不可为空,但如果我进行查询但未找到,那么我的结果将为空,对吗? (因此我的困境,我在想同样的事情。它不能在数据库中为“null”,但如果记录实际上不存在......)
  • 第二种可能性也有缺点,当书存在而不是1时它使用两个数据库调用。使用TryGet模式。
  • 我认为,您可以使用 Null object pattern 而不是 TryGet 模式。在大多数情况下,它允许不检查 null,例如 == null。确实,这取决于情况。
【解决方案2】:

我更喜欢使用可为空的 int - 这更直接映射到数据库中的实际内容。

此外,很多时候没有适当的值可用于 null。使用 -1 表示 null 是不好的做法,IMO,因为 -1 通常是数据库中的有效值。

【讨论】:

    【解决方案3】:

    Db 中的主键通常不能为空,在大多数情况下,程序中的值也不能为空。但是在某些情况下,可能需要指示未找到的 Find() 函数,然后可以使用可为空的值。当你传递结果形式 Find。喜欢:

    int? foundBookID = FindBook(title);
    if (foundBookID.HasValue)
    {
       int BookID = foundBookID.Value;  // not nullable
       ...
    }
    

    【讨论】:

    • 所以,如果我的对象有一个不可为空的 BookID 字段,但我想指示“未找到”,那么我应该将“null”返回给我的 BookID?例如:BookID = Find("我的书");
    • 是的,但我会称它为 var foundBookId 之类的。见答案。
    【解决方案4】:

    恕我直言,最好有(伪代码)

    public struct Identifier {
    
       private int? presistanceID;
    
       public Identifier(int presistanceID){
         presistanceID = presistanceID;
       }
    
       public bool IsSetted {
         get {presistanceID.hasvalue};
       }
    }
    
    if (book.BookID.IsSetted){
    }
    
    book.BookID.ToString();
    

    这是可读的,提供基本的类型检查,允许两个不变量(分配的 ID 和未分配的)并且对于 NullRerenceException 是安全的。

    【讨论】:

      【解决方案5】:

      听起来像是一个加载的问题。你问的方式,是的,肯定使用可为空的 int。我能想到的例外是,如果有很多现有代码已经使用/检查了 0 来表示未找到。在这种情况下,一致性可能是首选。可空类型相对较新,因此检查 0 并不罕见。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-08
        • 2020-04-24
        • 1970-01-01
        • 2011-04-03
        相关资源
        最近更新 更多