Nullable DateTime with SQLDataReader

本文关键字:SQLDataReader with DateTime Nullable | 更新日期: 2023-09-27 18:21:24

我几乎不想问这个问题,这个问题以前似乎已经被问了一百万次了,但即使我在研究另一个问题,我似乎仍然无法在我的情况下弄清楚这一点。

我读到DateTime是一个可以为NULL的类型,我尝试了一些例子,但我试图弄清楚它在我的SQLDATAREADER失败的数据库中是否为NULL。

错误

System.Data.SqlTypes.SqlNullValueException:数据为Null。不能"对Null值调用"此方法或属性

细节类别

private DateTime? startingDate;
public DateTime? StartingDate
{
    get{ return startingDate; }
    set{ startingDate = value; }
}
// constructor
Public DetailsClass(DateTime? startingDate)
{
    this.startingDate = startingDate;
}

DBClass

   using (SqlConnection con = new SqlConnection(connectionString))
            using (SqlCommand cmd = con.CreateCommand())
            {
                List<DetailsClass> details = new List<DetailsClass>();
                DetailsClass dtl;
                try
                {
                    con.Open();
                    cmd.CommandText = "Stored Procedure Name";
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue("@MyParameter", myparameter);
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            dtl = new DetailsClass((
                                reader.GetInt32(reader.GetOrdinal("MEMBERSHIPGEN"))),
                                reader.IsDBNull(1) ? null : reader.GetString(reader.GetOrdinal("EMAIL")),
                                reader.GetDateTime(reader.GetOrdinal("STARTINGDATE")));

                            details.Add(dtl);
                        }
                        reader.Close();
                        return details;
                    }
                }

Nullable DateTime with SQLDataReader

这里有一个从读取器中获取值的辅助方法

public static class ReaderExtensions {
  public static DateTime? GetNullableDateTime(this SqlDataReader reader, string name){ 
       var col = reader.GetOrdinal(name);
       return reader.IsDBNull(col) ? 
                   (DateTime?)null :
                   (DateTime?)reader.GetDateTime(col);
  }
}

关于如何使用以回应评论的更新

using (SqlConnection con = new SqlConnection(connectionString))
        using (SqlCommand cmd = con.CreateCommand())
        {
            List<DetailsClass> details = new List<DetailsClass>();
            DetailsClass dtl;
            try
            {
                con.Open();
                cmd.CommandText = "Stored Procedure Name";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@MyParameter", myparameter);
                using (SqlDataReader reader = cmd.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        dtl = new DetailsClass((
                            reader.GetInt32(reader.GetOrdinal("MEMBERSHIPGEN"))),
                            reader.IsDBNull(1) ? null : reader.GetString(reader.GetOrdinal("EMAIL")),
                            reader.GetNullableDateTime("STARTINGDATE"));

                        details.Add(dtl);
                    }
                    reader.Close();
                    return details;
                }
            }

还要注意,您使用的是reader.IsDBNull(1),然后是reader.GetOrdinal。可能应该是reader.IsDBNull(reader.GetOrdinal("EMAIL"))

替换

DateTime startingDate;

带有

DateTime? startingDate;

问号将其标记为可为null的值,读者应该能够将startingdate设置为null,而不是引发异常。

您也可以在阅读器工作时检查空值,并用空字符串替换空值

while(reader.read())
{
  //column is an int value of your column. I.e: if the column ist the 8th column, set column to 7 (0-based)
  StartingDate = (reader.IsDBNull(column)) ? null : reader.GetOrdinal("STARTINGDATE"));
 //instead of null you could also return a specific date like 1.1.1900 or String.Empty
}

我知道这个问题已经得到了回答,但进一步简化了代码以处理其他可为null的变量类型。上面的答案仅限于一种特定的变量类型。这样做的好处是,它还允许您包含一个默认值,而不是将其设置为null。

public static T GetDataType<T>( this SqlDataReader r, string name, object def = null )
{
     var col = r.GetOrdinal(name);
     return r.IsDBNull(col) ? (T)def : (T)r[name];
}

然后在代码中,您可以使用诸如

...
while(reader.Read())
{
    data1Bool = reader.GetDataType<bool?>("data1"); // if it's null then we'll set it as null
    data2intNotNull = reader.GetDataType<int>("data2", 0); // if the data is null, then we'll set to 0
    data3date = reader.GetDataType<DateTime?>("data3", DateTime.Now); // same example as above, but instead of setting to 0, I can default it to today's date.
}
...

我遇到了关于null值对象的其他问题,这让我节省了几个小时来弄清楚发生了什么。(WPF在编译运行时没有解释这个问题。)

你能告诉我们调试时到底是哪一行抛出了错误吗。这会更容易。

if (! reader.IsDBNull(reader.GetOrdinal("STARTINGDATE"))) {
    obj.startingDate = reader.GetDateTime(reader.GetOrdinal("STARTINGDATE"));
}

如果它是DBNull,则无需显式分配null,因为它是一个可为null的类型,因此默认情况下它将包含null。

好的,根据您更新的代码(注意注释):

while (reader.Read()) {
    dtl = new DetailsClass((reader.GetInt32(reader.GetOrdinal("MEMBERSHIPGEN"))),
    // here you are checking null for email
    reader.IsDBNull(1) ? null : reader.GetString(reader.GetOrdinal("EMAIL")),
    // here you are not checking null for startingdate ?
    reader.GetDateTime(reader.GetOrdinal("STARTINGDATE")));
    details.Add(dtl);
}

让我们用更详细的方式试试:

while (reader.Read()) {
    dtl = new DetailsClass();
    dtl.membershipgen = reader.IsDBNull(reader.GetOrdinal("MEMBERSHIPGEN")) ? null : reader.GetInt32(reader.GetOrdinal("MEMBERSHIPGEN"));
    dtl.email = reader.IsDBNull(reader.GetOrdinal("EMAIL")) ? null : reader.GetString(reader.GetOrdinal("EMAIL")),
    dtl.startingdate = reader.IsDBNull(reader.GetOrdinal("STARTINGDATE")) ? null : reader.GetDateTime(reader.GetOrdinal("STARTINGDATE")));
    details.Add(dtl);
}

试试这个:

使startingDate变量nullable,如下所示:

DateTime? startingDate;

现在,当从SqlDataReader对象检索值时,您需要使用IsDbNull方法,该方法将确定该值是否为从数据库返回的NULL,如下所示:

if !reader.IsDBNull(reader.GetOrdinal("STARTINGDATE"))
{
    startingDate = reader.GetDateTime(reader.GetOrdinal("STARTINGDATE"));
}
else
{
    startingDate = null;
}

注意:我不确定您将startingDate分配给从数据库读取的值的位置,因为它没有显示在发布的代码中。这就是为什么我在示例中直接分配它,但您可能需要调整示例逻辑的位置。