Tablo değeri parametreleri .net kodundan saklı yordama nasıl geçirilir


171

Bir SQL Server 2005 veritabanım var. Birkaç prosedürde nvarchar(virgüllerle ayrılmış) depolanmış bir proc'a ilettiğim ve dahili olarak tek değerlere böldüğüm tablo parametreleri var . Ben böyle SQL komut parametreleri listesine ekleyin:

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

Ben veritabanını SQL Server 2008'e geçirmek zorundayım. Tablo değeri parametreleri olduğunu biliyorum ve bunları saklı yordamlarda nasıl kullanılacağını biliyorum. Ama nasıl bir SQL komutunda parametre listesine geçmek bilmiyorum.

Parameters.AddProsedürün doğru sözdizimini bilen var mı ? Yoksa bu parametreyi geçirmenin başka bir yolu var mı?


Bu çözümü inceleyin: EF'de Tablo Değerli Parametre ile Saklı Yordam. code.msdn.microsoft.com/Stored-Procedure-with-6c194514
Carl Prothman

Bunun gibi bir durumda, genellikle dizeleri birleştirir ve bunları sunucu tarafında bölerim veya birden fazla sütunum varsa bir xml bile iletirim. Sql xml işlerken çok hızlı. Tüm yöntemleri deneyebilir ve işlem süresini kontrol edebilir ve bundan sonra en iyi yöntemi seçebilirsiniz. XML <Items> <Item value = "sdadas" /> <Item value = "sadsad" /> ... </Items> gibi görünür. Sql Server üzerindeki işlem de basittir. Bu yöntemi kullanarak, daha fazla bilgiye ihtiyacınız varsa <item> öğesine her zaman yeni bir özellik ekleyebilirsiniz.
Nițu Alexandru

4
@ NițuAlexandru, "Sql xml işlenirken çok hızlı." Yakınında bile değil.
nothrow

Yanıtlar:


279

DataTable, DbDataReaderveya IEnumerable<SqlDataRecord>nesneleri SQL Server 2008'deki Tablo Değerli Parametreler (ADO.NET) adlı MSDN makalesine göre tablo değerli bir parametreyi doldurmak için kullanılabilir .

Aşağıdaki örnek, a DataTableveya an kullanımını gösterir IEnumerable<SqlDataRecord>:

SQL Kodu :

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

C # Kodu :

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;
    }
}

24
+1 Mükemmel örnek. Çıkarımlar: a göndermek DataTableset parametre değeri olarak SqlDbTypehiç Structuredve TypeNameveritabanı udt adının.
Ic.

10
Bir döngüde referans türünün bir örneğini yeniden kullanacaksanız (örneğin SqlDataRecord), lütfen bu örnekte bunu yapmanın neden güvenli olduğuna dair bir yorum ekleyin.
Søren Boisen

2
Bu kod yanlış: boş tablo değerli parametrelerin değerleri olarak ayarlanmış olmalıdır null. boş bir parametre verildiğinde CreateSqlDataRecordsasla geri dönmeyecektir . nullids
ta.speot.

4
@Crono: DataTable(veya DataSet) yalnızca Visual-Studio'daki sürükle ve bırak yeteneklerini desteklemek zorunda oldukları için uygulayın, böylece IComponenthangi uygulamaları uygularlar IDisposable. Tasarımcıyı kullanmaz ancak manuel olarak oluşturursanız, tasfiye etmek (veya using-statement kullanmak) için bir neden yoktur . Altın kuralın “uygulayan her şeyi bertaraf etme” istisnalarından biri budur IDisposable.
Tim Schmelter

2
@TimSchmelter Temel bir kural olarak Dispose, Kod Analizi beni uyarmazsa bile her zaman yöntemleri çağırırım . Ancak, üs DataSetve DataTableörneklerin kullanıldığı bu senaryoda , çağrının Disposehiçbir şey yapmayacağını kabul ediyorum.
Crono

31

Ayrıca Ryan'ın cevabı da ayarlamanız gerekir DataColumn'nin Ordinalbir ilgileniyor lütfen tesise table-valued parametersahip birden kimin sıra sayıları olan sütunlar değil alfabetik sıraya göre.

Örnek olarak, SQL'de parametre olarak kullanılan aşağıdaki tablo değeriniz varsa:

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

Sütunlarınızı C # 'da olduğu gibi sipariş etmeniz gerekir:

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

Bunu yapamazsanız, bir ayrıştırma hatası alırsınız, nvarchar'ı int'e dönüştüremedi.


15

genel

   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;

    }

Lütfen parametre olarak neyi geçmem gerektiğini bana bildirir misiniz? İşlev <T, TProperty> seçici? Sadece tbl.Rows.Add (item) olamaz ve bu parametreye gerek yoktur.
GDroid

selector.Invoke (item)
öğedeki

Seçiciyi buraya nasıl koyacağım konusunda bir örnek verebilir misiniz? Depolanan işleme geçmek için bir Liste <
Kılavuz>

guidList.ToTabledValuedParameter (x => x), x sizin durumunuzda rehber olduğu için, dönüş bir
rehber

5

Onunla çalışmanın en temiz yolu. Tablonuzun "dbo.tvp_Int" adlı bir tamsayı listesi olduğunu varsayarsak (kendi tablo türünüz için özelleştirin)

Bu uzantı yöntemini oluştur ...

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;
   }
}

Artık bunu yaparak, her yerde bir satıra tablo değerli bir parametre ekleyebilirsiniz:

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

1
paramCollection NULL olursa ne olur? Boş tip nasıl geçirilir?
Muflix

2
@Muflix Belirsiz olarak, genişletme yöntemleri aslında null örneklere karşı çalışır. Bu nedenle if(paramCollection != null), yöntemin en üstüne basit bir kontrol eklemek iyi olacaktır
Rhumborl

1
İlk -if- çek ile cevap güncellendi
Shahzad Qureshi

2
Belki biraz bilgiçim, ama imza IEnumerableyerine kullanmak istiyorum List, bu şekilde IEnumerablesadece listeler değil, herhangi bir şey iletebilirsiniz , özel bir işlev kullanmadığınızdan List, gerçekten Bizim içinIEnumerable
Francis Rab

Listeyi kullanmak, steno verileri (ForEach ()) kullanmanıza izin verir, aksi takdirde gerçekten bir foreach döngüsü yazmanız gerekir. Bu da işe yarayabilir, ama mümkün olduğunca kısa şeyler yazmayı seviyorum.
Shahzad Qureshi

0

Türünüzden uygun bir parametre oluşturmak için bu kodu kullanın:

private SqlParameter GenerateTypedParameter(string name, object typedParameter)
{
    DataTable dt = new DataTable();

    var properties = typedParameter.GetType().GetProperties().ToList();
    properties.ForEach(p =>
    {
        dt.Columns.Add(p.Name, Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);
    });
    var row = dt.NewRow();
    properties.ForEach(p => { row[p.Name] = (p.GetValue(typedParameter) ?? DBNull.Value); });
    dt.Rows.Add(row);

    return new SqlParameter
    {
        Direction = ParameterDirection.Input,
        ParameterName = name,
        Value = dt,
        SqlDbType = SqlDbType.Structured
    };
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.