Je travaille sur un projet de formulaire Windows en C #. J'essaie d'insérer plusieurs enregistrements dans la base de données SQL Server à partir d'un tableau.
Après avoir entré la première ligne, je reçois une exception
@UserID a déjà été déclaré. Les noms de variables doivent être uniques dans un lot de requêtes ou une procédure stockée.
Il n'y a pas de problème avec la clé primaire dans la base de données, car UserID
n'est pas la clé primaire.
C'est ce que j'essaie de faire.
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();
}
}
}
Vous devriez faire ceci correctement :
using(...) { ... }
pour vous débarrasser du try ... catch ... finally
(le bloc using
garantira une élimination appropriée et rapide de vos cours, lorsqu'ils ne sont plus nécessaires)try...catch
si vous n'êtes pas réellement manipulant les exceptions - les renvoyer simplement (cela n'a aucun sens)Essayez ce code:
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();
}
}
}
Si vous mettez command = connection.CreateCommand();
dans votre boucle for, cela fonctionnera. Le problème est que vous passez en boucle sur les paramètres de commande uniquement. Vous essayez donc d'ajouter d'autres paramètres à votre commande existante, mais ils sont déjà présents. Vous devez donc créer une nouvelle commande à chaque boucle.
Afin d'obtenir les performances maximales, vous pouvez envisager un BulkInsert . Cela garantit que votre insertion est effectuée aussi rapidement que possible, car toute requête émise a un temps système (une requête volumineuse s'exécutera généralement plus rapidement que beaucoup de petites requêtes). Cela devrait ressembler à ceci:
1) définir la méthode d’extension AsDataTable à partir de ici :
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) exécutez le BulkInsert réel comme ceci ( non testé ):
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();
}
Bien sûr, si la convention sur la configuration était utilisée (les noms des propriétés de l'objet correspondraient exactement aux noms des colonnes de la table de destination), aucun mappage ne serait requis.
Vous pouvez le faire en envoyant vos données sous forme de chaîne XML et les convertir en table dans une procédure stockée dans SQL. Par exemple: Supposons que j'envoie plusieurs lignes à ajouter/mettre à jour dans une table SQL, voici les étapes à suivre:
Convertissez votre classe ou liste de classe en une chaîne xml en utilisant la méthode suivante:
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();
}
}
Maintenant, lors de l'envoi de données à la base de données, convertissez votre objet de classe en chaîne xml (Ici, j'utilise un cadre d'entité dans mon code, vous pouvez le faire sans l'utiliser également):
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;
}
Maintenant votre code SQL:
3.1 Déclarer une variable de table:
DECLARE @tableVariableName TABLE
(
ID INT, Name VARCHAR(20)
)
3.2 Insérer votre chaîne XML dans la variable Table
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 Enfin, insérez cette valeur dans votre table SQL
INSERT INTO MyTable (ID, Name)
SELECT ID, Name
FROM @tableVariableName
Cela vous évitera de frapper la base de données encore et encore en utilisant une boucle for.
J'espère que cela vous aidera
Vous devez ajouter des paramètres de commande en dehors de la boucle ou déclarer une commande à l'intérieur de la boucle.
Dans le premier cas, vous devrez mettre à jour la valeur de chaque paramètre comme suit:
oleDbCommand1.Parameters["@UserID"].Value = Details.ID;
Et exécutez la commande une fois les nouvelles valeurs définies.