我尝试使用SqlBulkCopy
作为一次执行多个INSERT的方法,但由于某些原因,我在运行WriteToServer(DataTable)
时遇到了唯一的约束违规。关于这个SqlException
的奇怪之处在于它是这样说的。
我的表架构:
CREATE TABLE Product (
ID INT IDENTITY (1, 1) PRIMARY KEY,
Name NVARCHAR(450) UNIQUE NOT NULL, -- Unique constraint being called
BulkInsertID NCHAR(6) -- Column the constraint is being called on
);
我能想到为什么会发生这种情况的唯一原因是因为我在DataColumn
内分配列名时混淆了列名,但我多次检查它们,我找不到它们的任何问题。
Minimal, Complete and Verifiable Example:
class Program
{
private static SqlConnection connection;
private static string connectionURL = "Server=ASUS-X750JA\\DIRECTORY;Database=directory;Integrated Security=True;";
private static Random _random = new Random();
public static SqlConnection openConnection()
{
connection = new SqlConnection(connectionURL);
connection.Open();
Console.WriteLine("Opened connection to DB");
return connection;
}
public static void closeConnection()
{
connection.Close();
Console.WriteLine("Closed connection to DB");
}
static void Main(string[] args)
{
List<string> productNames = new List<string>();
productNames.Add("Diamond");
productNames.Add("Gold");
productNames.Add("Silver");
productNames.Add("Platinum");
productNames.Add("Pearl");
addProducts(productNames);
}
private static void addProducts(List<string> productNames)
{
const string tableName = "Product";
DataTable table = new DataTable(tableName);
string bulkInsertID;
do
{
bulkInsertID = generateID();
} while (isDuplicateBulkInsertID(tableName, bulkInsertID));
DataColumn nameColumn = new DataColumn("Name");
nameColumn.Unique = true;
nameColumn.AllowDBNull = false;
DataColumn bulkInsertIDColumn = new DataColumn("BulkInsertID");
bulkInsertIDColumn.Unique = false;
bulkInsertIDColumn.AllowDBNull = true;
table.Columns.Add(nameColumn);
table.Columns.Add(bulkInsertIDColumn);
foreach (string productName in productNames)
{
DataRow row = table.NewRow();
row[nameColumn] = productName;
row[bulkInsertIDColumn] = bulkInsertID;
table.Rows.Add(row);
}
using (SqlConnection connection = openConnection())
{
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName = table.TableName;
bulkCopy.WriteToServer(table);
}
}
}
/// <summary>
/// Generates random 6-character string but it's not like GUID so may need to check for duplicates
/// </summary>
/// <returns></returns>
public static string generateID()
{
char[] _base62chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray();
int length = 6;
var sb = new StringBuilder(length);
for (int i = 0; i < length; i++)
sb.Append(_base62chars[_random.Next(62)]);
return sb.ToString();
}
public static bool isDuplicateBulkInsertID(string tableName, string bulkInsertID)
{
string query = string.Format("SELECT BulkInsertID FROM {0} WHERE BulkInsertID = @bulkinsertid", tableName);
SqlCommand command = new SqlCommand(query, openConnection());
SqlParameter bulkInsertIDParam = new SqlParameter("@bulkinsertid", SqlDbType.NChar, bulkInsertID.Length);
bulkInsertIDParam.Value = bulkInsertID;
command.Parameters.Add(bulkInsertIDParam);
command.Prepare();
Task<SqlDataReader> asyncTask = command.ExecuteReaderAsync();
SqlDataReader reader = asyncTask.Result;
bool isDuplicate = reader.HasRows;
closeConnection();
return isDuplicate;
}
}
屏幕截图中显示的唯一约束属于Name
列,但重复的键值正在发送到BulkInsertID
列,我不知道为什么会抛出错误。
编辑:我刚刚更改了架构,使用uniqueidentifier
作为bulkInsertID
列,并将row[bulkInsertIDColumn] = bulkInsertID
更改为row[bulkInsertIDColumn] = Guid.NewGuid().ToString()
。当我重新编写代码时,我发现生成的GUID已经运行但是当我查看表时,GUID位于名称列中。所以我可以得出结论,这不是服务器问题,而是程序中的一个问题。
答案 0 :(得分:2)
因为您有一个标识列,所以批量插入会尝试将nameColumn
插入ID
(并忽略它,因为该列是标识列)并且bulkInsertIDColumn
插入{{1} }}。只需在插入中添加以下内容即可告诉它转到正确的列。
Name
另一个选项是向using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.ColumnMappings.Add("Name", "Name"); //NEW
bulkCopy.ColumnMappings.Add("BulkInsertID", "BulkInsertID"); //NEW
bulkCopy.DestinationTableName = table.TableName;
bulkCopy.WriteToServer(table);
}
添加ID
列,并且不要在其中添加任何值。
table
答案 1 :(得分:0)
看起来它会对列UNIQUE
抛出BulkInsertID
约束违规但是从发布的表架构中看不到它标有该约束,并且在您的代码中我看到您有bulkInsertIDColumn.Unique = false;
。您确定没有将其设置为true
其他地方。
Random()
的新实例,如下面的尖代码块所示
do
{
bulkInsertID = generateID(); //calling method generateID
} while (isDuplicateBulkInsertID(tableName, bulkInsertID));
在generateID()
中创建Random class
public static string generateID()
{
........
Random _random = new Random(); // creating new instance every time