问题
I'm trying to figure out what's causing a Cannot perform runtime binding on a null reference
error from this code:
var query = "SELECT Id, UserName, List_Order, LoggedIn " +
"FROM AspNetUsers" +
"WHERE LoggedIn = 1" +
"ORDER BY List_Order ASC";
var conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
var cmd = new SqlCommand(query, conn);
conn.Open();
var rdr = cmd.ExecuteReader();
var n = 0;
while(rdr.Read())
{
if (Convert.ToString(rdr["UserName"]) != null)
{
ViewBag.speakers[n] = new string[4] {
Convert.ToString(rdr["Id"]),
Convert.ToString(rdr["UserName"]),
Convert.ToString(rdr["List_Order"]),
Convert.ToString(rdr["LoggedIn"])
};
//Exception Details: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot
//perform runtime binding on a null reference
n++;
}
}
The n++
increment seems to be the cause of this error and I don't understand why.
Updated code to reflect possible solutions. However, the error still remains.
Tried this with the same result:
if (!string.IsNullOrWhiteSpace(Convert.ToString(rdr["UserName"]))) {
List<string> speakers = new List<string>();
speakers.Add(Convert.ToString(rdr["Id"]));
speakers.Add(Convert.ToString(rdr["UserName"]));
speakers.Add(Convert.ToString(rdr["List_Order"]));
speakers.Add(Convert.ToString(rdr["LoggedIn"]));
ViewBag.speakers[n] = speakers;
n++;
}
回答1:
There are two issues in your code:
Consider this:
public ActionResult Index()
{
int n = 0;
ViewBag.speakers[n] = 5;
return View();
}
This simplified piece of code throws Cannot perform runtime binding on a null reference
since speakers is not defined (null reference).
You can fix it by defining speakers
in the dynamic ViewBag before the loop:
ViewBag.speakers = new List<string>();
The second issue:
ViewBag.speakers[n] = speakers;
speakers in your code is a List, you might want to define ViewBag.speakers
as a List<List<string>>
and call .Add(speakers)
instead of accessing using an index (you might get index was out of range)
回答2:
Calling the .ToString()
method on a null column value can result in the Cannot perform runtime binding on a null reference
. Use Convert.ToString()
instead; it will return an empty string if you attempt to convert a null
value and won't require additional code for null
checking.
回答3:
ViewBag
is a dynamic object and when you try to do assignments to its properties, the null check will happen in runtime. You get this error because ViewBag.speakers
is either null, or it throws an exception while trying to access its nth
slot with indexer (maybe its size is less than n
, or it doesn't define an indexer at all). You should initialize ViewBag.speakers
before adding elements to it.
var query = "SELECT Id, UserName, List_Order, LoggedIn " +
"FROM AspNetUsers" +
"WHERE LoggedIn = 1" +
"ORDER BY List_Order ASC";
var conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
var cmd = new SqlCommand(query, conn);
conn.Open();
var rdr = cmd.ExecuteReader();
ViewBag.speakers = new List<string[]>();
while(rdr.Read())
{
if (Convert.ToString(rdr["UserName"]) != null)
{
var speakers = new string[4] {
Convert.ToString(rdr["Id"]),
Convert.ToString(rdr["UserName"]),
Convert.ToString(rdr["List_Order"]),
Convert.ToString(rdr["LoggedIn"])
};
ViewBag.speakers.Add(speakers);
}
}
回答4:
Your problem seems originated from usage of Read
method after ExecuteReader
, which gives null reference on dynamic object ViewBag.speakers
at certain point that enough to throw runtime binding exception on view side.
Using DataTable.Load
and simple for-loop to iterate DataTable
contents, I modified Gökhan Kurt's answer to this one:
var query = "SELECT Id, UserName, List_Order, LoggedIn " +
"FROM AspNetUsers" +
"WHERE LoggedIn = 1" +
"ORDER BY List_Order ASC";
using (var conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
using (var cmd = new SqlCommand(query, conn))
{
conn.Open();
var dt = new DataTable();
ViewBag.speakers = new List<string[]>();
var rdr = cmd.ExecuteReader();
dt.Load(rdr);
for (int n = 0; n < dt.Rows.Count; n++)
{
if (!String.IsNullOrWhiteSpace(Convert.ToString(dt.Rows[n]["UserName"])))
{
// speakers returns String[4] array instead of null value
var speakers = new String[4] {
Convert.ToString(dt.Rows[n]["Id"]),
Convert.ToString(dt.Rows[n]["UserName"]),
Convert.ToString(dt.Rows[n]["List_Order"]),
Convert.ToString(dt.Rows[n]["LoggedIn"])
};
ViewBag.speakers.Add(speakers);
}
}
conn.Close();
}
}
NB: SqlDataReader.Read()
is forward-only, it will returns false
after last row has reached. The ViewBag.speakers
assigned to null reference if the DataReader.Read
method returns false, thus while loop is not executed. Using DataTable.Load
method keeps returned data from ExecuteReader
even after SqlDataReader
is closed or reached last row.
Secondly, you may look at reader.Read() only read once even when there are multiple rows to read problem, but IMHO it seems only work when the row length is already known or fixed size. Since it is impossible to use SqlDataReader.RecordsAffected
before the data reader is closed, you need another way to get row count from SqlDataReader
.
References:
Populate data table from data reader
SqlDataReader.Read()
SqlDataReader.RecordsAffected
DataTable.Load()
来源:https://stackoverflow.com/questions/40531522/mvc5-c-sharp-cannot-perform-runtime-binding-on-a-null-reference