I have a C# code which does lot of insert statements in a batch. While executing these statements, I got "String or binary data would be truncated" error and transaction roledback.
To find out the which insert statement caused this, I need to insert one by one in the SQLServer until I hit the error.
Is there clever way to findout which statement and which field caused this issue using exception handling? (SqlException)
In general, there isn't a way to determine which particular statement caused the error. If you're running several, you could watch profiler and look at the last completed statement and see what the statement after that might be, though I have no idea if that approach is feasible for you.
In any event, one of your parameter variables (and the data inside it) is too large for the field it's trying to store data in. Check your parameter sizes against column sizes and the field(s) in question should be evident pretty quickly.
This type of error occurs when the datatype of the SQL Server column has a length which is less than the length of the data entered into the entry form.
this type of error generally occurs when you have to put characters or values more than that you have specified in Database table like in that case: you specify
transaction_status varchar(10)
but you actually trying to store
_transaction_status
which contain 19 characters. that's why you faced this type of error in this code
Generally it is that you are inserting a value that is greater than the maximum allowed value. Ex, data column can only hold up to 200 characters, but you are inserting 201-character string
BEGIN TRY
INSERT INTO YourTable (col1, col2) VALUES (#val1, #val2)
END TRY
BEGIN CATCH
--print or insert into error log or return param or etc...
PRINT '#val1='+ISNULL(CONVERT(varchar,#val1),'')
PRINT '#val2='+ISNULL(CONVERT(varchar,#val2),'')
END CATCH
For SQL 2016 SP2 or higher follow this link
For older versions of SQL do this:
Get the query that is causing the problems (you can also use SQL Profiler if you dont have the source)
Remove all WHERE clauses and other unimportant parts until you are basically just left with the SELECT and FROM parts
Add WHERE 0 = 1 (this will select only table structure)
Add INTO [MyTempTable] just before the FROM clause
You should end up with something like
SELECT
Col1, Col2, ..., [ColN]
INTO [MyTempTable]
FROM
[Tables etc.]
WHERE 0 = 1
This will create a table called MyTempTable in your DB that you can compare to your target table structure i.e. you can compare the columns on both tables to see where they differ. It is a bit of a workaround but it is the quickest method I have found.
It depends on how you are making the Insert Calls. All as one call, or as individual calls within a transaction? If individual calls, then yes (as you iterate through the calls, catch the one that fails). If one large call, then no. SQL is processing the whole statement, so it's out of the hands of the code.
I have created a simple way of finding offending fields by:
Getting the column width of all the columns of a table where we're trying to make this insert/ update. (I'm getting this info directly from the database.)
Comparing the column widths to the width of the values we're trying to insert/ update.
Assumptions/ Limitations:
The column names of the table in the database match with the C# entity fields. For eg: If you have a column like this in database:
You need to have your Entity with the same column name:
public class SomeTable
{
// Other fields
public string SourceData { get; set; }
}
You're inserting/ updating 1 entity at a time. It'll be clearer in the demo code below. (If you're doing bulk inserts/ updates, you might want to either modify it or use some other solution.)
Step 1:
Get the column width of all the columns directly from the database:
// For this, I took help from Microsoft docs website:
// https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnection.getschema?view=netframework-4.7.2#System_Data_SqlClient_SqlConnection_GetSchema_System_String_System_String___
private static Dictionary<string, int> GetColumnSizesOfTableFromDatabase(string tableName, string connectionString)
{
var columnSizes = new Dictionary<string, int>();
using (var connection = new SqlConnection(connectionString))
{
// Connect to the database then retrieve the schema information.
connection.Open();
// You can specify the Catalog, Schema, Table Name, Column Name to get the specified column(s).
// You can use four restrictions for Column, so you should create a 4 members array.
String[] columnRestrictions = new String[4];
// For the array, 0-member represents Catalog; 1-member represents Schema;
// 2-member represents Table Name; 3-member represents Column Name.
// Now we specify the Table_Name and Column_Name of the columns what we want to get schema information.
columnRestrictions[2] = tableName;
DataTable allColumnsSchemaTable = connection.GetSchema("Columns", columnRestrictions);
foreach (DataRow row in allColumnsSchemaTable.Rows)
{
var columnName = row.Field<string>("COLUMN_NAME");
//var dataType = row.Field<string>("DATA_TYPE");
var characterMaxLength = row.Field<int?>("CHARACTER_MAXIMUM_LENGTH");
// I'm only capturing columns whose Datatype is "varchar" or "char", i.e. their CHARACTER_MAXIMUM_LENGTH won't be null.
if(characterMaxLength != null)
{
columnSizes.Add(columnName, characterMaxLength.Value);
}
}
connection.Close();
}
return columnSizes;
}
Step 2:
Compare the column widths with the width of the values we're trying to insert/ update:
public static Dictionary<string, string> FindLongBinaryOrStringFields<T>(T entity, string connectionString)
{
var tableName = typeof(T).Name;
Dictionary<string, string> longFields = new Dictionary<string, string>();
var objectProperties = GetProperties(entity);
//var fieldNames = objectProperties.Select(p => p.Name).ToList();
var actualDatabaseColumnSizes = GetColumnSizesOfTableFromDatabase(tableName, connectionString);
foreach (var dbColumn in actualDatabaseColumnSizes)
{
var maxLengthOfThisColumn = dbColumn.Value;
var currentValueOfThisField = objectProperties.Where(f => f.Name == dbColumn.Key).First()?.GetValue(entity, null)?.ToString();
if (!string.IsNullOrEmpty(currentValueOfThisField) && currentValueOfThisField.Length > maxLengthOfThisColumn)
{
longFields.Add(dbColumn.Key, $"'{dbColumn.Key}' column cannot take the value of '{currentValueOfThisField}' because the max length it can take is {maxLengthOfThisColumn}.");
}
}
return longFields;
}
public static List<PropertyInfo> GetProperties<T>(T entity)
{
//The DeclaredOnly flag makes sure you only get properties of the object, not from the classes it derives from.
var properties = entity.GetType()
.GetProperties(System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.DeclaredOnly)
.ToList();
return properties;
}
Demo:
Let's say we're trying to insert someTableEntity of SomeTable class that is modeled in our app like so:
public class SomeTable
{
[Key]
public long TicketID { get; set; }
public string SourceData { get; set; }
}
And it's inside our SomeDbContext like so:
public class SomeDbContext : DbContext
{
public DbSet<SomeTable> SomeTables { get; set; }
}
This table in Db has SourceData field as varchar(16) like so:
Now we'll try to insert value that is longer than 16 characters into this field and capture this information:
public void SaveSomeTableEntity()
{
var connectionString = "server=SERVER_NAME;database=DB_NAME;User ID=SOME_ID;Password=SOME_PASSWORD;Connection Timeout=200";
using (var context = new SomeDbContext(connectionString))
{
var someTableEntity = new SomeTable()
{
SourceData = "Blah-Blah-Blah-Blah-Blah-Blah"
};
context.SomeTables.Add(someTableEntity);
try
{
context.SaveChanges();
}
catch (Exception ex)
{
if (ex.GetBaseException().Message == "String or binary data would be truncated.\r\nThe statement has been terminated.")
{
var badFieldsReport = "";
List<string> badFields = new List<string>();
// YOU GOT YOUR FIELDS RIGHT HERE:
var longFields = FindLongBinaryOrStringFields(someTableEntity, connectionString);
foreach (var longField in longFields)
{
badFields.Add(longField.Key);
badFieldsReport += longField.Value + "\n";
}
}
else
throw;
}
}
}
The badFieldsReport will have this value:
'SourceData' column cannot take the value of
'Blah-Blah-Blah-Blah-Blah-Blah' because the max length it can take is
16.
It could also be because you're trying to put in a null value back into the database. So one of your transactions could have nulls in them.
Most of the answers here are to do the obvious check, that the length of the column as defined in the database isn't smaller than the data you are trying to pass into it.
Several times I have been bitten by going to SQL Management Studio, doing a quick:
sp_help 'mytable'
and be confused for a few minutes until I realize the column in question is an nvarchar, which means the length reported by sp_help is really double the real length supported because it's a double byte (unicode) datatype.
i.e. if sp_help reports nvarchar Length 40, you can store 20 characters max.
Checkout this gist.
https://gist.github.com/mrameezraja/9f15ad624e2cba8ac24066cdf271453b.
public Dictionary<string, string> GetEvilFields(string tableName, object instance)
{
Dictionary<string, string> result = new Dictionary<string, string>();
var tableType = this.Model.GetEntityTypes().First(c => c.GetTableName().Contains(tableName));
if (tableType != null)
{
int i = 0;
foreach (var property in tableType.GetProperties())
{
var maxlength = property.GetMaxLength();
var prop = instance.GetType().GetProperties().FirstOrDefault(_ => _.Name == property.Name);
if (prop != null)
{
var length = prop.GetValue(instance)?.ToString()?.Length;
if (length > maxlength)
{
result.Add($"{i}.Evil.Property", prop.Name);
result.Add($"{i}.Evil.Value", prop.GetValue(instance)?.ToString());
result.Add($"{i}.Evil.Value.Length", length?.ToString());
result.Add($"{i}.Evil.Db.MaxLength", maxlength?.ToString());
i++;
}
}
}
}
return result;
}
With Linq To SQL I debugged by logging the context, eg. Context.Log = Console.Out
Then scanned the SQL to check for any obvious errors, there were two:
-- #p46: Input Char (Size = -1; Prec = 0; Scale = 0) [some long text value1]
-- #p8: Input Char (Size = -1; Prec = 0; Scale = 0) [some long text value2]
the last one I found by scanning the table schema against the values, the field was nvarchar(20) but the value was 22 chars
-- #p41: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [1234567890123456789012]
In our own case I increase the sql table allowable character or field size which is less than the total characters posted from theĀ front end. Hence that resolve the issue.
Simply Used this:
MessageBox.Show(cmd4.CommandText.ToString());
in c#.net and this will show you main query , Copy it and run in database .
A table contain bigInt column named 'Phone' I want to insert null value to this column by entity frame work. I used following codes:
objRegister.Phone = Convert.ToInt64(txtPhone.Text ??null );
But i get this message: "Input string was not in a correct format.".
I change the code as shown in below:
objRegister.Phone = txtMobile.Text != null ?Convert.ToInt64(txtMobile.Text):((long?)null) ;
I got same message : "Input string was not in a correct format."
You could try this something like this:
long phone;
objRegister.Phone = long.TryParse(txtPhone.Text, out phone)
? (long?)phone
: (long?)system.DBNullValue;
Int64 phoneNumber;
Int64.TryParse(txtPhone.Text, out phoneNumber) == true ? phoneNumber : DBNull.Value;
You can use ?: Conditional Operator with Int64.TryParse check whether text is convertible to Int64. If convertible then assign convertible value else DBNull.Value
In C#, using SqlDataReader, is there a way to read a boolean value from the DB?
while (reader.Read())
{
destPath = reader["destination_path"].ToString();
destFile = reader["destination_file"].ToString();
createDir = reader["create_directory"].ToString();
deleteExisting = Convert.ToBoolean(reader["delete_existing"]);
skipIFolderDate = reader["skipifolderdate"].ToString();
useTempFile = reader["useTempFile"].ToString();
password = reader["password"].ToString();
}
In the code above, delete_existing is always a 1 or 0 in the DB. I read on MSDN that Convert.ToBoolean() does not accept a 1 or 0 as valid input. It only accepts true or false. Is there an alternative way to convert a DB value to a bool? Or do I need to do this outside of the SqlDataReader?
Also, I can not change the DB values, so please no answers saying, "Change the DB values from 1's and 0's to true's and false's."
Thanks!
If the type of delete_existing is a sqlserver 'bit' type, you can do :
var i = reader.GetOrdinal("delete_existing"); // Get the field position
deleteExisting = reader.GetBoolean(i);
or (but it will crash if delete_existing can be DBNull)
deleteExisting = (bool)reader["delete_existing"];
or better, this onebelow is DBNull proof and returns false if the column is DBNull
deleteExisting = reader["delete_existing"] as bool? ?? false;
Otherwise if the database type is int :
deleteExisting = (reader["delete_existing"] as int? == 1) ? true : false;
or if it is a varchar
deleteExisting = (reader["delete_existing"] as string == "1") ? true : false;
Casting works:
myVar = (bool)dataReader["myColumn"];
How about this?
deleteExisting = (reader["delete_existing"] as int?) == 1;
Boolean is probably the easist type to convert something to. Here's the 'Y', 'N' version:
deleteExisting = string.Equals(reader["delete_existing"] as string, "Y", StringComparision.OrdinalIgnoreCase);
If you are using CASE in SELECT and want to use GetBoolean then use CAST to change the column to bit before reading.
For eg:
SELECT CAST((CASE WHEN [Condition] THEN 1 ELSE 0 END) as bit) FROM Table_Name
then you can use
reader.GetBoolean(0)
deleteExisting = reader.GetBoolean(reader["delete_existing"]);
I want to return the last column of most recently inserted record from a database table. I keep getting this error:
A first chance exception of type 'System.InvalidCastException' occurred in SchoolManagement.exe
Additional information: Specified cast is not valid.
If there is a handler for this exception, the program may be safely continued
Code:
public int A()
{
string _connection = ConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString;
string stmt = "SELECT TOP 1 RegistrationNumber FROM tblStudentBiodata ORDER BY RegistrationNumber DESC";
int count = 0;
using (SqlConnection thisConnection = new SqlConnection(_connection))
{
using(SqlCommand cmdCount = new SqlCommand(stmt, thisConnection))
{
thisConnection.Open();
count = (int)cmdCount.ExecuteScalar();
}
}
return count;
}
Your cast to int is throwing the exception:
count = (int)cmdCount.ExecuteScalar();
Your conversion is not safe, and you are certainly not getting back an integer from the ExecuteScalar method. In fact, the ExecuteScalar method will return the result wrapped (boxed) into an Object so be aware that the object could contain any type (e.g. float, decimal, int etc.).
Also make sure you are not casting from a null value, since that would be returned if there are no records in the table. So make sure to add a check if the object is null before attempting to cast.
Per my explanation, check the type of RegistrationNumber column in SQL server and make sure to cast to the correct type in your C# code.
Here is a list of the type mappings between SQL Server and C#:
http://msdn.microsoft.com/en-us/library/cc716729%28v=vs.110%29.aspx
As already pointed in another answer, below line is causing the exception and fir sure RegistrationNumber column is not INT type. I doubt it would be SQL CHAR or VARCHAR column.
count = (int)cmdCount.ExecuteScalar();
In this case, instead of doing a direct casting, try casting it indirectly using AS operator and declare your count variable as nullable Int like
int? count = 0;
count = cmdCount.ExecuteScalar() as int?;
Then check and use it
if (count != null)
{
//Do something with it
}
I have a subroutine to insert a record to sql server table.
The code:
public void Insert_Met(int Counts, char Gender)
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("#Counts", Counts);
parameters.Add("#Gender", Gender);
// run a stored procedure ExecuteNonQuery
}
I other line code,
int counts = Convert.ToInt32(numberUpdowncontrol1.Text);
// from a control, maybe empty then it is null.
Insert_Met(counts,'M');
My question is sometimes Counts can be null, so how to change my code?
Thanks,
You could use int? counts instead of int counts, and check the value within your method:
public void InsertMet(int? counts, char gender)
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("#Counts", counts.HasValue ? counts.Value : (object)DBNull.Value);
parameters.Add("#Gender", gender);
// run a stored procedure ExecuteNonQuery
}
it is a numericupdown control in windows form. I feel that it is hard to assign a text to an int variable. How to change it?
In order to set the count value appropriately for the above, you could do:
int value = (int)numberUpdowncontrol1.Value;
int? counts = !string.IsNullOrEmpty(numberUpdowncontrol1.Text) ? value : (int?)null;
InsertMet(counts, 'M');
Following statement will always produce not null value
int counts = Convert.ToInt32(null); // Result is 0
int count = !(count == 0)? count ? (object)DBNull.Value
-If, count is not zero save the count value to database
-If it is zero save