私はC#でWindowsフォームプロジェクトに取り組んでいます。配列からSQLServerデータベースに複数のレコードを挿入しようとしています。
最初の行を入力した後、例外が発生します
@UserIDはすでに宣言されています。変数名は、クエリバッチまたはストアドプロシージャ内で一意である必要があります。
UserID
は主キーではないため、データベースの主キーに問題はありません。
これが私がやろうとしていることです。
public static void featuresentry()
{
SqlConnection connection = new SqlConnection(HandVeinPattern.Properties.Settings.Default.HandVeinPatternConnectionString);
SqlCommand command = new SqlCommand();
connection.Open();
try
{
command = connection.CreateCommand();
for (int i = 0; i < Details.modelKeyPoints.Size; i++)
{
command.CommandText = "INSERT INTO FEATURES(UserID, Angle, ClassID, Octave, PointX, PointY, Response, Size) VALUES(@UserID, @Angle, @ClassID, @Octave, @PointX, @PointY, @Response, @Size)";
command.Parameters.AddWithValue("@UserID", Details.ID);
command.Parameters.AddWithValue("@Angle", Convert.ToDouble(Details.modelKeyPoints[i].Angle));
command.Parameters.AddWithValue("@ClassID", Convert.ToDouble(Details.modelKeyPoints[i].ClassId));
command.Parameters.AddWithValue("@Octave", Convert.ToDouble(Details.modelKeyPoints[i].Octave));
command.Parameters.AddWithValue("@PointX", Convert.ToDouble(Details.modelKeyPoints[i].Point.X));
command.Parameters.AddWithValue("@PointY", Convert.ToDouble(Details.modelKeyPoints[i].Point.Y));
command.Parameters.AddWithValue("@Response", Convert.ToDouble(Details.modelKeyPoints[i].Response));
command.Parameters.AddWithValue("@Size", Convert.ToDouble(Details.modelKeyPoints[i].Size));
command.ExecuteNonQuery();
}
}
catch (Exception)
{
throw;
}
finally
{
if (connection.State == ConnectionState.Open)
{
connection.Close();
}
}
}
あなたはこれをするべきです適切に:
using(...) { ... }
ブロックを使用してtry ... catch ... finally
を削除します(using
ブロックは、不要になったときにクラスを適切かつ迅速に破棄します)try...catch
の使用を停止してください-例外を再スローするだけです(意味がありません)このコードを試してください:
public static void featuresentry()
{
string connectionString = HandVeinPattern.Properties.Settings.Default.HandVeinPatternConnectionString;
string insertQuery = "INSERT INTO FEATURES(UserID, Angle, ClassID, Octave, PointX, PointY, Response, Size) VALUES(@UserID, @Angle, @ClassID, @Octave, @PointX, @PointY, @Response, @Size)";
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(insertQuery, connection))
{
// define your parameters ONCE outside the loop, and use EXPLICIT typing
command.Parameters.Add("@UserID", SqlDbType.Int);
command.Parameters.Add("@Angle", SqlDbType.Double);
command.Parameters.Add("@ClassID", SqlDbType.Double);
command.Parameters.Add("@Octave", SqlDbType.Double);
command.Parameters.Add("@PointX", SqlDbType.Double);
command.Parameters.Add("@PointY", SqlDbType.Double);
command.Parameters.Add("@Response", SqlDbType.Double);
command.Parameters.Add("@Size", SqlDbType.Double);
connection.Open();
for (int i = 0; i < Details.modelKeyPoints.Size; i++)
{
// now just SET the values
command.Parameters["@UserID"].Value = Details.ID;
command.Parameters["@Angle"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Angle);
command.Parameters["@ClassID"].Value = Convert.ToDouble(Details.modelKeyPoints[i].ClassId);
command.Parameters["@Octave"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Octave);
command.Parameters["@PointX"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Point.X);
command.Parameters["@PointY"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Point.Y);
command.Parameters["@Response"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Response);
command.Parameters["@Size"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Size);
command.ExecuteNonQuery();
}
}
}
Forループ内にcommand = connection.CreateCommand();
を入れると、機能します。問題は、コマンドパラメータのみをループしているため、既存のコマンドにさらにパラメータを追加しようとしていることですが、それらはすでに存在しています。したがって、代わりにループごとに新しいコマンドを作成する必要があります。
最大のパフォーマンスを得るために、 BulkInsert を検討することができます。これにより、発行されたクエリにはある程度のオーバーヘッドがあるため、挿入が可能な限り高速に実行されます(通常、大きなクエリは多くの小さなクエリよりも高速に実行されます)。次のようになります。
1)AsDataTable拡張メソッドを ここ から定義します:
public static DataTable AsDataTable<T>(this IEnumerable<T> data)
{
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
var table = new DataTable();
foreach (PropertyDescriptor prop in properties)
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
foreach (T item in data)
{
DataRow row = table.NewRow();
foreach (PropertyDescriptor prop in properties)
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
table.Rows.Add(row);
}
return table;
}
2)次のように実際のBulkInsertを実行します(テストされていません):
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlTransaction transaction = connection.BeginTransaction();
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction))
{
bulkCopy.BatchSize = 100;
bulkCopy.DestinationTableName = "dbo.FEATURES";
try
{
// define mappings for columns, as property names / generated data table column names
// is different from destination table column name
bulkCopy.ColumnMappings.Add("ID","UserID");
bulkCopy.ColumnMappings.Add("Angle","Angle");
// the other mappings come here
bulkCopy.WriteToServer(Details.modelKeyPoints.AsDataTable());
}
catch (Exception)
{
transaction.Rollback();
connection.Close();
}
}
transaction.Commit();
}
もちろん、 設定より規約 が使用される場合(オブジェクトプロパティ名は宛先テーブルの列名と正確に一致します)、マッピングは必要ありません。
これを行うには、データをxml文字列として送信し、SQLのストアドプロシージャでテーブルに変換します。例:SQLテーブルに追加/更新するために複数の行を送信するとします。手順は次のとおりです。
次のメソッドを使用して、クラスまたはクラスのリストをxml文字列に変換します。
public static string SerializeObjectToXmlString(object value)
{
var emptyNamepsaces = new XmlSerializerNamespaces(new[] {
XmlQualifiedName.Empty });
var serializer = new XmlSerializer(value.GetType());
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, value, emptyNamepsaces);
return stream.ToString();
}
}
これで、データベースにデータを送信するときに、クラスオブジェクトをxml文字列に変換します(ここでは、コードでエンティティフレームワークを使用していますが、使用せずにこれを行うこともできます)。
bool AddUpdateData(List<MyClass> data)
{
bool returnResult = false;
string datatXml = Helper.SerializeObjectToXmlString(data);
var sqlparam = new List<SqlParameter>()
{
new SqlParameter() { ParameterName = "dataXml", Value = datatXml}
};
var result = this.myEntity.Repository<SQL_StoredProc_ComplexType>().ExecuteStoredProc("SQL_StoredProc", sqlparam);
if (result != null && result.Count() > 0)
{
returnResult = result[0].Status == 1 ? true : false;
}
return returnResult;
}
今あなたのSQLコード:
3.1テーブル変数を宣言します。
DECLARE @tableVariableName TABLE
(
ID INT, Name VARCHAR(20)
)
3.2xml文字列をテーブル変数に挿入します
INSERT INTO @tableVariableName
SELECT
Finaldata.R.value ('(ID/text())[1]', 'INT') AS ID,
Finaldata.R.value ('(Name/text())[1]', 'VARCHAR(20)') AS Name
FROM @MyInputXmlString.nodes ('//ArrayMyClass/MyClass') AS Finaldata (R)
3.3最後に、このテーブル値をSQLテーブルに挿入します
INSERT INTO MyTable (ID, Name)
SELECT ID, Name
FROM @tableVariableName
これにより、forループを使用してデータベースに何度もアクセスする手間が省けます。
それがあなたを助けることを願っています
ループの外側にコマンドパラメータを追加するか、ループの内側でコマンドを宣言する必要があります。
最初のケースでは、次のように各パラメーターの値を更新する必要があります。
oleDbCommand1.Parameters["@UserID"].Value = Details.ID;
そして、新しい値が設定されたら、コマンドを実行します。