![](/img/trans.png)
[英]String or binary data would be truncated.\r\nThe statement has been terminated. c#
[英]How to solve the "String or binary data would be truncated.\r\nThe statement has been terminated." error?
我有一個更新一些記錄的程序。 當我執行它時,我得到以下異常
"字符串或二進制數據將被截斷。\r\n語句已終止。"
我發現當參數長度大於變量長度時會發生這種情況。 我再次檢查更改大小。 但是沒有用。 再次出現同樣的異常。 我該如何解決這個問題? 請幫忙
這是我的更新代碼
bool isFinished = dba.update(desingnation, title, initials, surname, fullname, callingName, civilSatatus, natinality, nic, birthday, passport,
hometp, mobiletp, province, district, division, electorate, gramaNiladhari, takafull, p_city,
c_city, p_hno, c_hno, tokens_P, tokens_C, previousEmployeements, bank, branch, type, account, gender, educatinalQ, languageE, languageS, languageT, empNo, appNo);
if (isFinished)
{
WebMsgBox.Show("Successfully Inserted!");
}
else
{
WebMsgBox.Show("Some Errors Occured");
}
}
else
{
WebMsgBox.Show("Some feilds are not valid");
}
}
}
這是將參數傳遞給存儲過程的代碼
try
{
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandType = CommandType.Text;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = connection;
cmd.CommandTimeout = 0;
cmd.Transaction = transactions;
/*=======================Update employee details================================*/
cmd.CommandText = "update_HS_HR_EMPLOYEE_AADM";
cmd.Parameters.Add("@appNo", SqlDbType.Int).Value = appNo;
cmd.Parameters.Add("@CALLING_NAME", SqlDbType.VarChar).Value = callingName;
cmd.Parameters.Add("@INITIALS", SqlDbType.VarChar).Value = initials;
cmd.Parameters.Add("@SURNAME", SqlDbType.VarChar).Value = surname;
cmd.Parameters.Add("@TITLE", SqlDbType.VarChar).Value = title;
cmd.Parameters.Add("@NAME", SqlDbType.VarChar).Value = fullname;
cmd.Parameters.Add("@FULLNAME", SqlDbType.VarChar).Value = fullname + " " + surname;
cmd.Parameters.Add("@NIC", SqlDbType.VarChar).Value = nic;
cmd.Parameters.Add("@BDY", SqlDbType.VarChar).Value = birthday;
cmd.Parameters.Add("@GENDER", SqlDbType.VarChar).Value = gender;
cmd.Parameters.Add("@NATIONALITY", SqlDbType.VarChar).Value = natinality;
cmd.Parameters.Add("@CIVILSTATUS", SqlDbType.VarChar).Value = civilSatatus;
cmd.Parameters.Add("@DESIGNATION", SqlDbType.VarChar).Value = desingnation;
cmd.Parameters.Add("@P_ADD1", SqlDbType.VarChar).Value = p_hno;
cmd.Parameters.Add("@P_ADD2", SqlDbType.VarChar).Value = tokens_P[0];
if (tokens_P.Length > 1)
cmd.Parameters.Add("@P_ADD3", SqlDbType.VarChar).Value = tokens_P[1];
else
cmd.Parameters.Add("@P_ADD3", SqlDbType.VarChar).Value = "";
cmd.Parameters.Add("@P_CITY", SqlDbType.VarChar).Value = p_city;
cmd.Parameters.Add("@TP_HOME", SqlDbType.VarChar).Value = hometp;
cmd.Parameters.Add("@TP_MOBILE", SqlDbType.VarChar).Value = mobiletp;
cmd.Parameters.Add("@PROVINCE", SqlDbType.VarChar).Value = province;
cmd.Parameters.Add("@DISTRICT", SqlDbType.VarChar).Value = district;
cmd.Parameters.Add("@C_ADD1", SqlDbType.VarChar).Value = c_hno;
cmd.Parameters.Add("@C_ADD2", SqlDbType.VarChar).Value = tokens_C[0];
cmd.Parameters.Add("@PER_GNDIV_CODE", SqlDbType.VarChar).Value = gramaNiladhari;
cmd.Parameters.Add("@PER_DSDIV_CODE", SqlDbType.VarChar).Value = division;
cmd.Parameters.Add("@TAKAFUL", SqlDbType.VarChar).Value = takafull;
cmd.Parameters.Add("@PASSPORT_NO", SqlDbType.VarChar).Value = passport;
if (tokens_C.Length > 1)
cmd.Parameters.Add("@C_ADD3", SqlDbType.VarChar).Value = tokens_C[1];
else
cmd.Parameters.Add("@C_ADD3", SqlDbType.VarChar).Value = "";
cmd.Parameters.Add("@C_CITY", SqlDbType.VarChar).Value = c_city;
cmd.Parameters.Add("@ELECTORATE", SqlDbType.VarChar).Value = electorate;
//int appNO = int.Parse((cmd.ExecuteScalar().ToString()));
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
}
}
這是存儲過程
ALTER PROCEDURE [dbo].[update_HS_HR_EMPLOYEE_AADM]
@appNo Int,
@CALLING_NAME VARCHAR(50),
@INITIALS VARCHAR(50),
@SURNAME VARCHAR(50),
@TITLE VARCHAR(50),
@NAME VARCHAR(50),
@FULLNAME VARCHAR(100),
@NIC VARCHAR(15),
@BDY VARCHAR(50),
@GENDER CHAR(1),
@NATIONALITY VARCHAR(50),
@CIVILSTATUS VARCHAR(50),
@DESIGNATION VARCHAR(50),
@P_ADD1 VARCHAR(50),
@P_ADD2 VARCHAR(50),
@P_ADD3 VARCHAR(50),
@P_CITY VARCHAR(50),
@TP_HOME VARCHAR(50),
@TP_MOBILE VARCHAR(50),
@PROVINCE VARCHAR(50),
@DISTRICT VARCHAR(50),
@C_ADD1 VARCHAR(50),
@C_ADD2 VARCHAR(50),
@C_ADD3 VARCHAR(50),
@C_CITY VARCHAR(50),
@ELECTORATE VARCHAR(50),
@PER_GNDIV_CODE VARCHAR(50),
@PER_DSDIV_CODE VARCHAR(50),
@TAKAFUL VARCHAR(50),
@PASSPORT_NO VARCHAR(50)
AS
BEGIN
update [HS_HR_EMPLOYEE_AADM]
SET
[EMP_CALLING_NAME]=@CALLING_NAME
,[EMP_MIDDLE_INI]=@INITIALS
,[EMP_SURNAME]=@SURNAME
,[EMP_TITLE]=@TITLE
,[EMP_NAMES_BY_INI]=@NAME
,[EMP_FULLNAME]=@FULLNAME
,[EMP_NIC_NO]=@NIC
,[EMP_BIRTHDAY]=@BDY
,[EMP_GENDER]=@GENDER
,[NAT_CODE]=@NATIONALITY
,[EMP_MARITAL_STATUS]=@CIVILSTATUS
,[EMP_DATE_JOINED]=GETDATE()
,[EMP_CONFIRM_FLG]=0
,[CT_CODE]='000008'
,[DSG_CODE]=@DESIGNATION
,[CAT_CODE]='000001'
,[EMP_PER_ADDRESS1]=@P_ADD1
,[EMP_PER_ADDRESS2]=@P_ADD2
,[EMP_PER_ADDRESS3]=@P_ADD3
,[EMP_PER_CITY]=@P_CITY
,[EMP_PER_TELEPHONE]=@TP_HOME
,[EMP_PER_MOBILE]=@TP_MOBILE
,[EMP_PER_PROVINCE_CODE]=@PROVINCE
,[EMP_PER_DISTRICT_CODE]=@DISTRICT
,[EMP_TEM_ADDRESS1]=@C_ADD1
,[EMP_TEM_ADDRESS2]=@C_ADD2
,[EMP_PER_ELECTORATE_CODE]=@ELECTORATE
,[EMP_TEM_ADDRESS3]=@C_ADD3
,[EMP_TEM_CITY]=@C_CITY
,[EMP_PER_GNDIV_CODE]=@PER_GNDIV_CODE
,[EMP_PER_DSDIV_CODE]=@PER_DSDIV_CODE
,[EMP_PASSPORT_NO]=@TAKAFUL
,[EMP_TAK]=@PASSPORT_NO
where App_no = @appNo
END
在 C# 代碼中的 SqlDBType.Varchar 中指定與存儲過程中指定的大小相匹配的 varchar 大小,例如。
cmd.Parameters.Add("@CALLING_NAME", SqlDbType.VarChar, 50).Value = callingName;
對應於存儲過程中的參數@CALLING_NAME VARCHAR(50)
。 這確保了在傳遞給存儲過程時不會超過大小。
如果未為字符串參數指定長度,ADO.NET 將獲取可能超過存儲過程 VARCHAR 參數中指定的大小的任意長度值。
同樣在前端確保在文本框中輸入的字符數不超過相應的參數大小。 這可以使用MaxLength
屬性來完成,或者如果大小超過,則使用 JQuery/Javascript 提示用戶消息。
對其他參數執行此操作並檢查。
指定的錯誤"String or binary data would be truncated.\r\nThe statement has been terminated."
當您嘗試插入高於指定列大小的值時顯示,當我們查看給定過程時,我們無法識別每列的大小,因此最好交叉檢查具有您提供的值的列。
我可以說@GENDER
可能會導致類似的問題,因為它在過程中被定義為@GENDER CHAR(1),
但是您將一個字符串傳遞給該方法並作為SqlDbType.VarChar
傳遞,而不是因為您必須給出值作為字符。 對於這個特定領域
String or binary data would be truncated
錯誤告訴您您正在丟失數據。 關於這個錯誤的一個惱人的事情是它沒有告訴你問題與哪一列有關,並且在這樣的場景中(有很多列),它很難診斷。
如果您有合適的 SQL Server 版本(請參閱下面的超鏈接頁面),您可以打開 Trace Flag 460 (這可能需要重新啟動)以准確告訴您問題與哪個表和列有關。
如果沒有,這是我的更多手動方法......之后有一些關於如何在沒有此錯誤的情況下靜默截斷參數的信息(這不好)。
我查找哪些列有問題的提示是找到它發生的場景,以便您可以輕松地重復它 - 這特別容易做到,您對此方法進行了單元測試。 現在修改存儲過程以注釋掉一半的列,然后重試。
現在您已經確定了哪些列有問題,對照存儲的 proc 參數定義、C# 參數定義和 C# 變量中的值檢查表中每個列的定義。 您可能需要一直跟蹤到在用戶界面中輸入值的位置,並確保有適當的限制以防止值太大。
但是,請注意,由於參數強制會發生靜默截斷,因此值得使您的varchar
、 nvarchar
和varbinary
參數大於列大小:
SQL 服務器會默默地強制你的值是參數的任何類型。 例如...
DECLARE @Varchar VARCHAR(8) = 'I will be truncated'; DECLARE @Decimal92 DECIMAL(9,2) = 123.456; DECLARE @Int INT = 123.456; SELECT @Varchar, @Decimal92, @Int;
將輸出...
I will b 123.46 123
這可能會讓人感到意外,因為 SQL 會抱怨這樣的事情:
BEGIN TRANSACTION; CREATE TABLE tbl_Test(MyColumn NVARCHAR(5) NOT NULL); INSERT INTO dbo.tbl_Test (MyColumn) VALUES (N'I will be truncated'); ROLLBACK TRANSACTION;
通過說String or binary data would be truncated.
然而下面的代碼並沒有抱怨,默默地強制值並插入記錄:
BEGIN TRANSACTION; CREATE TABLE tbl_Test(MyColumn NVARCHAR(5) NOT NULL); DECLARE @MyColumn NVARCHAR(5)=N'I will be truncated' INSERT INTO dbo.tbl_Test (MyColumn) VALUES (@MyColumn); ROLLBACK TRANSACTION;
因此,如果您想了解發生的截斷問題,您需要確保您的參數具有比它要進入的列更大的容量。 例如,如果我們只更改一個字符...
BEGIN TRANSACTION; CREATE TABLE tbl_Test(MyColumn NVARCHAR(5) NOT NULL); DECLARE @MyColumn NVARCHAR(6)=N'I will be truncated' INSERT INTO dbo.tbl_Test (MyColumn) VALUES (@MyColumn); ROLLBACK TRANSACTION;
...會給出截斷錯誤。 但是,請注意這不是一個完整的解決方案,因為如果我嘗試使用不同的值......
CREATE TYPE dbo.MyTableType AS TABLE (MyColumn NVARCHAR(5) NOT NULL);
GO
BEGIN TRANSACTION;
DECLARE @MyColumn NVARCHAR(5)=N'I will be truncated'
DECLARE @MyTable AS dbo.MyTableType;
INSERT INTO @MyTable (MyColumn) VALUES (@MyColumn);
CREATE TABLE dbo.tbl_Test (MyColumn NVARCHAR(5) NOT NULL);
INSERT INTO dbo.tbl_Test SELECT MyColumn FROM @MyTable;
ROLLBACK TRANSACTION;
GO
DROP TYPE dbo.MyTableType;
然后它會默默地強制,在空格之后截斷,然后(因為它是一個 varchar)該值刪除了尾隨空格,並且它插入而不抱怨。
所以唯一可以確定的方法是讓你的參數比它需要的大幾個字符,因為可能不會有多個空格連續。 您可以對所有內容使用 VARCHAR(MAX),但有人擔心這可能會對性能產生影響。
這一點特別重要的一個地方是加密值。 如果加密值被截斷,則您無法解密它們。 因此,您需要確保您的 VARBINARY 參數的大小大於相關列,以便您得到錯誤而不是插入截斷的值。 在這種情況下,我相信一個更大的字符就足夠了,因為沒有修剪 VARBINARY。 好吧,顯然 VarBinarys 會從末尾修剪尾隨的“nul”(ASCII = 0)字符; 但僅當 ANSI_PADDING 設置為 OFF 時,但正如 Microsoft 所說,它應該始終設置為“ON”。 本節還介紹了具有不同設置的不同字段類型會發生什么修剪。
值得一提的是,SQL 甚至與它的執行方式不一致。 如果我們用 DECIMAL 重試原始示例...
BEGIN TRANSACTION; CREATE TABLE tbl_Test(MyColumn DECIMAL(9,2) NOT NULL); INSERT INTO dbo.tbl_Test (MyColumn) VALUES (123.456); SELECT * FROM tbl_Test ROLLBACK TRANSACTION;
它不會抱怨值有太多小數位,它只是默默地強制它。 如果我通過一個比列具有更多小數位的參數來執行此操作,情況也是如此......
BEGIN TRANSACTION; CREATE TABLE tbl_Test(MyColumn DECIMAL(9,2) NOT NULL); DECLARE @MyColumn DECIMAL(9,3)=123.456 SELECT @MyColumn INSERT INTO dbo.tbl_Test (MyColumn) VALUES (@MyColumn); SELECT * FROM tbl_Test ROLLBACK TRANSACTION;
然而,如果我給它一個太大的價值......
BEGIN TRANSACTION; CREATE TABLE tbl_Test(MyColumn DECIMAL(9,2) NOT NULL); INSERT INTO dbo.tbl_Test (MyColumn) VALUES (1234567890.456); SELECT * FROM tbl_Test ROLLBACK TRANSACTION;
然后它會抱怨Arithmetic overflow error converting numeric to data type numeric.
(就像我先將該值放入參數中一樣)。
要提到的另一件事與參數強制和加密有關。 想象一個場景,您有一個類型為 DECIMAL(9,2) 的 SQL 列和一個相同類型的參數,並且您從 C# 代碼中為其提供了一個點網“十進制”。 如果您的代碼中的“小數”有很多小數位,那么這種無聲強制將有效地要求 SQL 為您進行舍入。 這很好......直到您決定加密該列,因為現在您正在加密的值將比 SQL DECIMAL 列能夠保存的值長得多,因此可能比您允許的要大(在您的 VARBINARY 長度)。 在這種情況下,您需要確保在加密之前將該值四舍五入到正確的小數位數。
關於參數的修剪尾隨空格。 它只是根據需要修剪...這表明它已經從參數中修剪了一個空格,但在 N 之后留下了剩余的 4 個空格。
BEGIN TRANSACTION; CREATE TABLE tbl_Test(MyColumn NVARCHAR(5) NOT NULL); DECLARE @MyColumn NVARCHAR(6)=N'N complain' SELECT @MyColumn +'|',LEN(@MyColumn),DATALENGTH(@MyColumn) INSERT INTO dbo.tbl_Test (MyColumn) VALUES (@MyColumn); SELECT MyColumn +'|',LEN(MyColumn),DATALENGTH(MyColumn) FROM dbo.tbl_Test ROLLBACK TRANSACTION;
關於此的另一個學習點...相同的概念適用於與表定義相關的用戶定義的表類型。
這是一個示例腳本來演示該問題。 請注意,類型的創建和刪除必須在事務之外完成。
CREATE TYPE dbo.MyTableType AS TABLE (MyColumn NVARCHAR(5) NOT NULL); GO BEGIN TRANSACTION; DECLARE @MyColumn NVARCHAR(5)=N'I will be truncated' DECLARE @MyTable AS dbo.MyTableType; INSERT INTO @MyTable (MyColumn) VALUES (@MyColumn); CREATE TABLE dbo.tbl_Test (MyColumn NVARCHAR(5) NOT NULL); INSERT INTO dbo.tbl_Test SELECT MyColumn FROM @MyTable; ROLLBACK TRANSACTION; GO DROP TYPE dbo.MyTableType;
我有幾個建議來確定這一點:(選擇任何 1,然后移動到其他,如果問題仍然存在。順序無關緊要,無論是哪個建議,你都輕松了)。
建議 1:打開 sql profiler 並跟蹤請求。 從探查器獲取查詢並直接在 SQL Server 上運行。 你可能會得到更多的錯誤細節。
腳步:
建議 2:嘗試使用 SQL Server management studio 插入數據。
腳步:
table
-> 單擊Script Table as
-> 插入到。 建議3:使用此代碼獲取數據長度與傳遞的數據之間的差異或在傳遞給DB時直接截斷。
腳步:
DbContextExtension
。using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
namespace Data.Context
{
public class DbContextExtension
{
private Dictionary<IProperty, int> _maxLengthMetadataCache;
public void AutoTruncateStringToMaxLength(DbContext db)
{
var entries = db?.ChangeTracker?.Entries();
if (entries == null)
{
return;
}
var maxLengthMetadata = PopulateMaxLengthMetadataCache(db);
foreach (var entry in entries)
{
var propertyValues = entry.CurrentValues.Properties.Where(p => p.ClrType == typeof(string));
foreach (var prop in propertyValues)
{
if (entry.CurrentValues[prop.Name] != null)
{
var stringValue = entry.CurrentValues[prop.Name].ToString();
if (maxLengthMetadata.ContainsKey(prop))
{
var maxLength = maxLengthMetadata[prop];
stringValue = TruncateString(stringValue, maxLength);
}
entry.CurrentValues[prop.Name] = stringValue;
}
}
}
}
private Dictionary<IProperty, int> PopulateMaxLengthMetadataCache(DbContext db)
{
_maxLengthMetadataCache ??= new Dictionary<IProperty, int>();
var entities = db.Model.GetEntityTypes();
foreach (var entityType in entities)
{
foreach (var property in entityType.GetProperties())
{
var annotation = property.GetAnnotations().FirstOrDefault(a => a.Name == "MaxLength");
if (annotation != null)
{
var maxLength = Convert.ToInt32(annotation.Value);
if (maxLength > 0 && !_maxLengthMetadataCache.ContainsKey(property))
{
_maxLengthMetadataCache[property] = maxLength;
}
}
}
}
return _maxLengthMetadataCache;
}
private static string TruncateString(string value, int maxLength)
{
if (string.IsNullOrEmpty(value)) return value;
return value.Length <= maxLength ? value : value.Substring(0, maxLength);
}
}
}
public class DocumentRepository : IDocumentRepository
{
private readonly DbContext _context;
public DocumentRepository(DbContext context)
{
_context = context;
}
public async Task CreateDocument(Document obj)
{
//Feel free to make it extension method or use it as DI. To make the example easier, I am creating the object here.
var dbExtensions = new DbContextExtension();
dbExtensions.AutoTruncateStringToMaxLength(_context);
await _context.Documents.AddAsync(obj);
await _context.SaveChangesAsync();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.