I've noticed that after running the following code, I receive a 'column X does not allow DBNull.Value' error
I've been considering adding an if else statement which relates to a column being null or white space, in order to populate a NULL string field with "NULL".
As somebody brand new to development, who did not write the original code, I was curious to know if this is a solution that you would agree with or whether there is a better/simpler way to eliminate the DBNull error?
using (sqlConn)
{
sqlConn.Open();
dropUpdateCmd.ExecuteNonQuery();
createUpdateCmd.ExecuteNonQuery();
using (var bulkCopy = new SqlBulkCopy(sqlConn))
{
foreach (DataColumn col in dt.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
bulkCopy.BulkCopyTimeout = 600;
bulkCopy.DestinationTableName = "Destination.GenericTable";
bulkCopy.WriteToServer(dt);
}
mergeCmd.ExecuteNonQuery();
dropUpdateCmd.ExecuteNonQuery();
sqlConn.Close();
}
It is always best to put in the real data. So if it should be NULL and not some other value then you need to talk to whoever is in charge of the database to get it changed. However, I would assume that NULL is not being allowed for a reason. In either case, if you are not sure I would recommend that you talk to someone that knows how it was intended to be. If the table not allowing NULL was intended as I assume it was then we can assume that the data you are trying to insert is not supposed to contain NULL and measures should be taken to either take out the NULL values, change them or prevent them from being added the first place.
Related
I'm trying to insert into a table in a C# program. I have this insert command:
var insertSql = #"INSERT INTO dbo.[Case]
VALUES (#Id, #IsDeleted, #CaseNumber, #ContactId, #AccountId, #ParentId, #SuppliedName, #SuppliedEmail, #SuppliedPhone, #SuppliedCompany, #Type, #RecordTypeId, #Status, #Reason, #Origin...
And then I've got many lines adding in the parameters like so:
var command = new SqlCommand(insertSql, easySoftConn);
if (case2.Id != null)
command.Parameters.AddWithValue("#Id", case2.Id);
else
command.Parameters.AddWithValue("#Id", DBNull.Value);
if (case2.IsDeleted != null)
{
if (case2.IsDeleted == "true")
command.Parameters.AddWithValue("#IsDeleted", 1);
else
command.Parameters.AddWithValue("#IsDeleted", 0);
}
else
command.Parameters.AddWithValue("#IsDeleted", DBNull.Value);
if (case2.CaseNumber != null)
command.Parameters.AddWithValue("#CaseNumber", case2.CaseNumber);
else
command.Parameters.AddWithValue("#CaseNumber", DBNull.Value);
if (case2.ContactId != null)
command.Parameters.AddWithValue("#ContactId", case2.ContactId);
else
command.Parameters.AddWithValue("#ContactId", DBNull.Value);
...
When I finally execute the insert:
try
{
command.ExecuteNonQuery();
}
catch (System.Data.SqlClient.SqlException e)
{
CLog.Write(e.Message.ToString(), CLog.ErrLvl.Error);...
}
I get the error:
String or binary data would be truncated
My issue is, the error doesn't tell me which column would be truncated. I've got 80 columns I'm inserting into, and I'd rather not go through them one-by-one. Is there a way to get the error handling to tell me exactly which field is throwing the error?
EDIT: I have a full stack trace in my log file but it still doesn't tell me which column, I just shortened it to the actual error here.
Switching to using strongly typed data access would head this one off sooner:
Add a dataset file to your project
Open it, right click the surface, add tableadapter, set connection parameters, add a query of SELECT * FROM [Case]
FInish the wizard, a datatable and adapter are generated. The DB is used to drive the creation, so all the string columns have a MaxLength property in the dataset that comes from the DB
Attempting to add a row to this table will now cause an error like "unable to set column XYZ, the value violates the MaxLength limit for the column"
Data access code looks like:
var dt = new YourDataSetNameHere.CaseDataTable();
dt.AddCaseRow(put, your, values, here, you , dont, need, to worry, about, null, this, or, data, type, that, because, VS, handles, it, all, for, you, in, the, DataSet.Designer.cs, file);
new YourDataSetNameHereTableAdapters.CaseTableAdapter().Update(dt); //save the new row;
So it'll save you a boatload of time writing boring data access code too
Depending on your SQL version you can apply a KB to get this to show more data as stated here - Link
It effectively starts to show messages like the following
Msg 2628, Level 16, State 6, Procedure ProcedureName, Line Linenumber
String or binary data would be truncated in table '%.*ls', column
'%.*ls'. Truncated value: '%.*ls'.
This came from this great post Link which goes much further to explain how you can try and search for the column should this not be possible. The post also talks about how you can do manual searching although I'd imagine if the list of columns is too large that may be something you want to avoid.
Looks like the value of one or more of your parameters has more length than the table cell can contain.
You should to look at table column definitions.
My objective is to connect to an mdb database (stored locally), go through it and get the following information:
-The names of all the tables present in the database
-The name of the columns AND their type in each table of the database.
While developing the code I used the code indicated in the answer to the following stackoverflow question
need to find the type of the column of DB
I implemented the code written in the accepted answer and tailored it to my needs. Here is what I have so far:
DataSet dtImportedTables = new DataSet();
Globals.strSQLQuery = "SELECT * FROM {0}";
//try
//{
Globals.conConnection = new OleDbConnection(Globals.strConnection);
Globals.conConnection.Open();
foreach (DataRow row in Globals.tblSchemaTable.Rows)
{
DataTable dt = new DataTable();
OleDbCommand command = new OleDbCommand(String.Format(Globals.strSQLQuery, row["TABLE_NAME"] as String), Globals.conConnection);
dt.Load(command.ExecuteReader(CommandBehavior.SchemaOnly));
dtImportedTables.Tables.Add(dt);
}
Globals.conConnection.Close();
string temp="";
int k;
foreach (DataTable dt in dtImportedTables.Tables)
{
k = 0;
foreach (DataColumn dc in dt.Columns)
{
Globals.arrColumnNamesList[k] = dc.ColumnName;
Globals.arrColumnTypesList[k] = dc.DataType.ToString();
temp += k+ ") "+Globals.arrColumnNamesList[k] + Environment.NewLine + Globals.arrColumnTypesList[k] + Environment.NewLine;
k++;
}
}
Note the following:
-I am using Visual Studio 2012 and this is a Windows Form App, programmed in C#
-The connection works fine (according to debug at least), and the database in question is made up of just one table containing several columns.
-The full debug description of the error is:
$exception{"Object reference not set to an instance of an object."}
System.Exception {System.NullReferenceException}
-Both Globals.arrColumnTypesList and Globals.arrColumnNamesList are arrays of type string.
-On the third instruction line I commented out the "try" section for debug purposes (i.e. if left intact the program would simply not execute the whole section because of the error I am inquiring about, thus not letting the debugger warn me of the error).
-I had to modify both foreach statements from the original version found in the stackoverflow question mentioned above. As it was written the compiler would give me an error.
-The problem occurs here:
Globals.arrColumnTypesList[k] = dc.DataType.ToString();
While looking through other questions about this exception, i found that it is thrown typically if the expression returns a NULL value (in this case, as I understand it, it would mean that dc.DataType is NULL). However, in the debug tool of VisualStudio, DataType is not NULL at all, it contains in fact the value {"System.String"}. I figured that the ToString() method could have been the cause of this strange behaviour so I changed the expression to the following:
Globals.arrColumnTypesList[k] = dc.DataType.Name;
It gave me the same error and this time dc.DataType.Name had a value of "String", clearly of type string.
Can anybody help me figure out what is going on? Is the debugger perhaps misleading me indicating the error is on that line while the real problem could be someplace else?
Have you initialized Globals.arrColumnTypesList? If that field is null, there would be a NullReferenceException.
Most likely, you never initialize Globals.arrColumnTypesList. Try something like this before the loop:
Globals.arrColumnTypesList = new List<string>();
Or (depending on the type of arrColumnTypesList):
Globals.arrColumnTypesList = new string[dt.Columns.Count];
You could try debugging the code in debug mode and turning exceptions on - this will stop the code at the point the exception occurs. In general this is a useful feature that allows you to quickly find where an exception is being thrown.
To do this, in VS2012, go to Debug -> Exceptions and tick the 'Thrown' box for Common Language Runtime Exceptions. (Or press Ctrl + D, E to get the same dialog).
I am getting this error: Data Could not be read. Data is Null. This method or property cannot be called on Null values.
I know the Database has NULL values in some fields. I just want to handle them and continue filling in the next row. Here's some code:
rdr is the SqlDataReader
if (rdr[EmailID] != null)
{
//this blows up on this line on the 32nd iteration of the loop when searching for an extended group.
EmpNewData.SelectSingleNode("/my:myFields/my:Emp/my:EmpData/my:email", NamespaceManager).SetValue(rdr.GetString(EmailID));
}
else
{
EmpNewData.SelectSingleNode("/my:myFields/my:Emp/my:EmpData/my:email", NamespaceManager).SetValue("No.Email");
}
I could handle this with the Stored Procedure, but I'd really like to know how to handle this. Above is one of many iterations I've tried.
Thanks.
I figured this out. IsDBNull seems to take care of it. I ran thru the code in debug to check where the ordinal sat. It was 14 in this case.
int EmailID = rdr.GetOrdinal("EmailID");
Then when using GetString:
if (!(rdr.IsDBNull(14)))
{
EmpNewData.SelectSingleNode("/my:myFields/my:Emp/my:EmpData/my:email", NamespaceManager).SetValue(rdr.GetString(EmailID));
}
else
{
EmpNewData.SelectSingleNode("/my:myFields/my:Emp/my:EmpData/my:email", NamespaceManager).SetValue("No Email");
}
I hope this will help somebody.
This was originally a problem with Crystal Reports but I tracked down the issue to a single line of code:
foreach (DataRow dr in ds.Tables["CurrentScheduleFields"].Rows)
if (dr["MY_FIELD"].ToString() == string.Empty)
dr["MY_FIELD"] = 0;
I am using an Oracle database. The field is actually a Numeric(2). We are using straight ADO.NET and no Nullable types in this case. In one case, the field in question has a value of an int and the above line of code is fine. The other case, the value of the field is DBNull.Value, and the above or something is converting the 0 to "0" ( a string).
dr is not a `DataReader foreach (DataRow dr in ds.Tables["CurrentScheduleFields"].Rows)
Does anyone know why?
My browser is not letting me click on the Add Comments, so I am adding stuff here. Why can't I do if (dr["MY_FIELD"] == DBNull.Value)
dr["MY_FIELD"] = 0;
the debugger hits that and converts to a string with a value of "0", not 0. I guess the first answer handles this, "So the problem may be that the first "MY_FIELD" is null, convincing ADO.NET that the type for that column is a string." So it's now string with DBNull.Value in it?
My browser is locked down. I can't click on anything except edit this but:
ANSWER: Handle this on the Crystal side by changing the formula to handle a string for this value and not an int. It works!
In some cases, when dealing with DataTable, I've seen it try to "guess" at the type of a column based on the value in that column in the first row in your set. So the problem may be that the first "MY_FIELD" is null, convincing ADO.NET that the type for that column is a string.
You may want to set the schema for your DataTable before you try to load it.
I have designed a dataset using VS2008 dataset designer. In one of the datatables, I have set "AllowDBNull" property of most of the columns to be False. However, still if I create a DataRow containing null values for these columns, this datatable accepts this row, without any error.
Am I not understanding something here? Please advice. Thank you.
Edit Mike Spross' excellent explanation however, brings forth another question. How do we check text fields if they are System.DBNull? It is surprising that DataSets are not considering a string "" as System.DBNull and throwing an exception. Or is it not?
Edit I think I have found the problem and reason. I am initializing a new row of the DataTable, before filling in the values to that row. While initializing the row, default value for string, ie, "" might be being filled in that column. I think that's it? Any ideas about this?
The short answer is:
System.DBNull.Value != null
The longer answer is:
In C#, the concept of a NULL value in SQL is represented by the Value property of the System.DBNull class. When dealing with a database, the more familiar C# null doesn't actually mean "null value."
When you set a database column to null, ADO.NET will initialize the column to whatever the default value is for that column (for example, an int column would be initialized to 0). That is, using null can actually cause a non-null value to end up in the database, and therefore you won't get an error.
If you instead set a database column to System.DBNull.Value, the column will actually be set to NULL. This is the situation that AllowDBNulls == false will prevent you from doing.
Regarding your "bonus" ;-) question: NULL (no string) and "" (empty string) are two different things. So it's perfectly reasonable to treat them differently. It's the distinction between null and DBNull that is messing things up. If nullable types had been available at the time of designing ADO.NET, things probably would be a lot easier. But before .NET 2.0, there was no way to represent e.g. a "null integer".
Are you exactly assigning NULL values or an empty string to those columns? If you don't assign any value to a column, it will default to NULL (if a DEFAULT constraint is not imposed). Else you can assign a NULL value by doing -
ds.Tables[0].Rows[0]["Col"] = null;
If you are assigning an Empty string to those columns, it's not equal to NULL.
And if you have a NULL value in a column which has been marked as NOT NULLABLE, it will throw an error -
Column 'Col1' does not allow nulls.
EDIT:
By NOT NULLABLE, I mean AllowDBNull = false.
Your code seems correct. Can you try trimming the text?
Here's the whole code -
DataTable dt = new DataTable();
DataColumn column = new DataColumn("Col1");
column.AllowDBNull = false;
dt.Columns.Add(column);
DataRow dr = dt.NewRow();
dr["Col1"] = null;
dt.Rows.Add(dr);