187 votes

Comment passer des paramètres de valeur de table à une procédure stockée depuis un code .net ?

J'ai une base de données SQL Server 2005. Dans quelques procédures, j'ai des paramètres de table que je transmets à une procédure stockée en tant qu'élément d'information. nvarchar (séparés par des virgules) et les diviser en valeurs individuelles. Je l'ajoute à la liste des paramètres de la commande SQL comme ceci :

cmd.Parameters.Add("@Logins", SqlDbType.NVarchar).Value = "jim18,jenny1975,cosmo";

Je dois faire migrer la base de données vers SQL Server 2008. Je sais qu'il existe des paramètres de valeur de table, et je sais comment les utiliser dans les procédures stockées. Mais je ne sais pas comment en passer un à la liste des paramètres dans une commande SQL.

Est-ce que quelqu'un connaît la syntaxe correcte de la Parameters.Add procédure ? Ou existe-t-il un autre moyen de transmettre ce paramètre ?

0 votes

Consultez cette solution : Procédure stockée avec paramètre évalué par la table dans EF. code.msdn.microsoft.com/Stored-Procedure-with-6c194514

0 votes

Dans un cas comme celui-ci, j'ai l'habitude de concaténer des chaînes de caractères et de les diviser du côté du serveur ou de passer même un xml si j'ai plusieurs colonnes. Sql est très rapide lors du traitement du xml. Vous pouvez essayer toutes les méthodes et vérifier le temps de traitement et après cela choisir la meilleure méthode. Un XML ressemblerait à <Items><Item value="sdadas"/><Item value="sadsad"/>...</Items>. Le processus sur Sql Server est également simple. En utilisant cette méthode, vous pouvez toujours ajouter un nouvel attribut à <item> si vous avez besoin de plus d'informations.

4 votes

@NituAlexandru, "Sql c'est très rapide quand on traite du xml.". Pas du tout.

309voto

Ryan Prechel Points 1050

DataTable , DbDataReader ou IEnumerable<SqlDataRecord> peuvent être utilisés pour remplir un paramètre à valeur de table, conformément à l'article MSDN Paramètres évalués par table dans SQL Server 2008 (ADO.NET) .

L'exemple suivant illustre l'utilisation d'un DataTable ou un IEnumerable<SqlDataRecord> :

Code SQL :

CREATE TABLE dbo.PageView
(
    PageViewID BIGINT NOT NULL CONSTRAINT pkPageView PRIMARY KEY CLUSTERED,
    PageViewCount BIGINT NOT NULL
);
CREATE TYPE dbo.PageViewTableType AS TABLE
(
    PageViewID BIGINT NOT NULL
);
CREATE PROCEDURE dbo.procMergePageView
    @Display dbo.PageViewTableType READONLY
AS
BEGIN
    MERGE INTO dbo.PageView AS T
    USING @Display AS S
    ON T.PageViewID = S.PageViewID
    WHEN MATCHED THEN UPDATE SET T.PageViewCount = T.PageViewCount + 1
    WHEN NOT MATCHED THEN INSERT VALUES(S.PageViewID, 1);
END

Code C# :

private static void ExecuteProcedure(bool useDataTable, 
                                     string connectionString, 
                                     IEnumerable<long> ids) 
{
    using (SqlConnection connection = new SqlConnection(connectionString)) 
    {
        connection.Open();
        using (SqlCommand command = connection.CreateCommand()) 
        {
            command.CommandText = "dbo.procMergePageView";
            command.CommandType = CommandType.StoredProcedure;

            SqlParameter parameter;
            if (useDataTable) {
                parameter = command.Parameters
                              .AddWithValue("@Display", CreateDataTable(ids));
            }
            else 
            {
                parameter = command.Parameters
                              .AddWithValue("@Display", CreateSqlDataRecords(ids));
            }
            parameter.SqlDbType = SqlDbType.Structured;
            parameter.TypeName = "dbo.PageViewTableType";

            command.ExecuteNonQuery();
        }
    }
}

private static DataTable CreateDataTable(IEnumerable<long> ids) 
{
    DataTable table = new DataTable();
    table.Columns.Add("ID", typeof(long));
    foreach (long id in ids) 
    {
        table.Rows.Add(id);
    }
    return table;
}

private static IEnumerable<SqlDataRecord> CreateSqlDataRecords(IEnumerable<long> ids) 
{
    SqlMetaData[] metaData = new SqlMetaData[1];
    metaData[0] = new SqlMetaData("ID", SqlDbType.BigInt);
    SqlDataRecord record = new SqlDataRecord(metaData);
    foreach (long id in ids) 
    {
        record.SetInt64(0, id);
        yield return record;
    }
}

28 votes

+1 Excellent exemple. Les points à retenir sont : envoyer un DataTable comme valeur du paramètre, définissez SqlDbType a Structured y TypeName au nom de l'UDT de la base de données.

10 votes

Si vous comptez réutiliser une instance d'un type de référence dans une boucle (SqlDataRecord dans votre exemple), veuillez ajouter un commentaire expliquant pourquoi il est sûr de le faire dans ce cas particulier.

2 votes

Ce code est erroné : les paramètres vides de la table doivent avoir leur valeur fixée à null . CreateSqlDataRecords ne reviendra jamais null si on lui donne un ids paramètre.

34voto

Scotty.NET Points 4373

En complément de la réponse de Ryan, vous devrez également définir l'option DataColumn 's Ordinal si vous avez affaire à un table-valued parameter con multiple colonnes dont les ordinaux sont no par ordre alphabétique.

Par exemple, si vous avez la valeur de table suivante qui est utilisée comme paramètre dans SQL :

CREATE TYPE NodeFilter AS TABLE (
  ID int not null
  Code nvarchar(10) not null,
);

Vous devrez ordonner vos colonnes de la sorte en C# :

table.Columns["ID"].SetOrdinal(0);
// this also bumps Code to ordinal of 1
// if you have more than 2 cols then you would need to set more ordinals

Si vous ne le faites pas, vous obtiendrez une erreur d'analyse, failed to convert nvarchar to int.

33voto

Paige Cook Points 13415

On dirait que le Conseils MSSQL article - Paramètres de valeur de table dans SQL Server 2008 et .NET (C#) montre comment y parvenir.

Le lien ci-dessus semble être mort, mais il est disponible sur la Wayback Machine .

15voto

Martea Points 127

Générique

   public static DataTable ToTableValuedParameter<T, TProperty>(this IEnumerable<T> list, Func<T, TProperty> selector)
    {
        var tbl = new DataTable();
        tbl.Columns.Add("Id", typeof(T));

        foreach (var item in list)
        {
            tbl.Rows.Add(selector.Invoke(item));

        }

        return tbl;

    }

0 votes

Pouvez-vous me dire ce que je dois passer comme paramètre ? Un sélecteur Func<T, TProperty> ? Ne peut-il pas être simplement tbl.Rows.Add(item) et ne pas avoir besoin de ce paramètre.

0 votes

Le selector.Invoke(item) sélectionne la propriété de l'item ; dans la plupart des cas, il s'agit d'un int, mais il vous permet également de sélectionner une propriété de type chaîne de caractères.

0 votes

Pouvez-vous s'il vous plaît fournir un exemple de la façon dont je mets le sélecteur là-bas ?? J'ai une Liste<Guid> à passer à la procédure stockée...

5voto

Shahzad Qureshi Points 106

La façon la plus propre de travailler avec elle. En supposant que votre table est une liste d'entiers appelée "dbo.tvp_Int" (Personnalisez pour votre propre type de table)

Créer cette méthode d'extension...

public static void AddWithValue_Tvp_Int(this SqlParameterCollection paramCollection, string parameterName, List<int> data)
{
   if(paramCollection != null)
   {
       var p = paramCollection.Add(parameterName, SqlDbType.Structured);
       p.TypeName = "dbo.tvp_Int";
       DataTable _dt = new DataTable() {Columns = {"Value"}};
       data.ForEach(value => _dt.Rows.Add(value));
       p.Value = _dt;
   }
}

Maintenant, vous pouvez ajouter un paramètre de valeur de tableau dans une ligne n'importe où en faisant simplement ceci :

cmd.Parameters.AddWithValueFor_Tvp_Int("@IDValues", listOfIds);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X