可空对象必须具有值 #2

5

我正在尝试重复使用我一直以来使用的相同代码,但现在它遇到了一个错误。

我正在循环遍历各种用户表,在其中执行以下操作:

DateTime dcdt = (DateTime)u.DateCreated;
DateTime lldt = (DateTime)u.LastLogon;
userRow["DateCreated"] = dcdt.ToShortDateString();

在循环内部,我遇到了错误:

System.InvalidOperationException: Nullable object must have a value.
错误突出显示了“lldt”行,而不是先出现的“dcdt”。这本身就很奇怪。数据库中这两个字段都已经勾选了“允许为空”。它们可能都为空,也可能都不为空。

通过智能感知,这两个值都列为DateTime?类型。

我不明白为什么ASP.NET不允许我输出空值日期。如果它是空/空值,那么逻辑上来说,ASP.NET应该什么都不输出。

我还能以其他方式输出空值日期吗?我尝试添加if语句以防止尝试转换空日期,但这没有帮助,毫无意义。

6个回答

12

正如你所说,u.LastLogon 的数据类型是 DateTime?。这意味着它可能有值,也可能没有值。通过强制转换为 DateTime,你要求它必须有一个值。而在这种情况下,它没有值。

根据你想要做什么,你可能需要检查 HasValue 属性:

userRow["LastLogon"] = u.LastLogin.HasValue ? 
                       (object) u.LastLogin.ToShortDateString() : DBNull.Value;
如果你的数据库中LastLogon列是DateTime类型,那么你应该能够执行以下操作:
userRow["LastLogon"] = u.LastLogin.HasValue ? 
                       (object) u.LastLogin.Value : DBNull.Value;

那我该怎么办呢?不转换吗?还是继续在 DateTime 上使用 ShortDateString() 而非 DateTime? - Dexter
1
“u.LastLogon.Value.ToShortDateString()”为什么不能正常工作? - John Saunders
显然它没有工作是因为 DBNull.Value 这行代码。因为 userRow["LastLogon"] 应该是字符串类型。用 String.Empty 替换它就可以了。对于造成的困扰,非常抱歉。 - Dexter
userRow["LastLogon"] = u.LastLogon.HasValue ? u.LastLogon.Value.ToShortDateString() : String.Empty <--- 这个可以用;非常感谢! - Dexter
字符串是 userRow["LastLogon"] 列的类型。 u.LastLogon.Value.ToShortDateString() 是可以工作的,我真的需要转换成对象吗? - Dexter
显示剩余2条评论

1

看起来你正在尝试在一个没有值(确实是 null)的 DateTime 上调用一个方法 (dcdt.ToShortDateString())。请尝试这样做:

dcdt.HasValue ? dcdt.ToShortDateString() : String.Empty;

编辑(重新阅读问题):同时,不要尝试转换为DateTime。保留可空性。

编辑#2(基于评论):

尝试这个:

if (dcdt.HasValue) 
{ userRow["DateCreated"] = dcdt.ToShortDateString(); } 
else
{ userRow = DbNull.Value }

u.LastLogon.HasValue ? u.LastLogon.Value.ToShortDateString() : DBNull.Value; 或者 u.LastLogon.HasValue ? u.LastLogon.ToShortDateString() : DBNull.Value; 无法正常工作。它不能像那样编译。LastLogon 是 DateTime? 类型。 - Dexter
这是因为 DBNull.Value 不是字符串 - 表达式必须返回一致的数据类型。我会进行适当的编辑。 - Chris Shain
我认为我解决了它。我使用了这个:userRow["LastLogon"] = u.LastLogon.HasValue ? u.LastLogon.Value.ToShortDateString() : String.Empty; - Dexter

1

我看到 Dexter 问如何处理这个问题。嗯,我会创建一个扩展程序。

static class DateTimeExtensions
{
    public static string ToString(this DateTime? dateTime, string format)
    {
        return dateTime.HasValue ? dateTime.Value.ToString(format) : String.Empty;
    }
}

然后你可以执行:

DateTime? dt = null;
DateTime? dt2 = DateTime.Now;
Console.WriteLine(dt.ToString("dd-MM-yy"));
Console.WriteLine(dt2.ToString("dd-MM-yy"));

请注意,如果对象为空,我可以在可空类型上调用扩展方法。

这是一个不错的解决方案。 - Răzvan Flavius Panda

1
你需要在数据访问代码中执行类似以下的操作:
DataTable  dt       = ExecuteSomeQuery() ;
object     value    = dt.Rows[0]["nullable_datetime_column"] ;
DateTime?  instance = value != null && value is DateTime ? (DateTime?)value : (DateTime?)null ) ;

如果返回的列为NULL,则将其作为System.DBNull返回,否则将其作为DateTime实例(或适当映射类型 - int、string等)返回。因此,在尝试转换之前,您需要检查从查询返回的对象的类型。

它已经是DateTime类型了。现在我想使用DateTime类中的ShortDateTimeString函数将其转换为字符串。但它不让我这样做。 - Dexter
1
在这种情况下,string s = instance.HasValue ? instance.Value.ToString() : "" ; 就可以解决问题了。 - Nicholas Carey
是的,没错。但是有人以正确的格式比你更早地回答了这个问题。 - Dexter

0
问题是.NET的null和SQL的NULL不同。 SQL Null 是 System.DBNull。所以它在.NET中是一个[非null]值。

那么我该如何使它要么不打印任何内容,要么在可用时打印日期? - Dexter
从技术上讲,Frazell是正确的,但我相信翻译是自动完成的。如果您的datetime列为null,您将看到一个.net null。但这不是问题,您必须编写一些代码来检查它是否为null并打印nothing。正如您所见,最简洁的方法是使用?.。ASP.NET不会为您执行此操作。 - O'Rooney

0

简短回答

DateTime? dateTime = u.LastLogon?.ToShortDateString()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接