Parametreleri DbContext.Database.ExecuteSqlCommand yöntemine nasıl iletilir?


222

Varlık Çerçevesinde doğrudan sql komutu yürütmek için geçerli bir ihtiyacım olduğunu varsayalım. Benim sql deyiminde parametreleri nasıl kullanılacağını bulmakta sorun yaşıyorum. Aşağıdaki örnek (benim gerçek örneğim değil) çalışmıyor.

var firstName = "John";
var id = 12;
var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";
ctx.Database.ExecuteSqlCommand(sql, firstName, id);

ExecuteSqlCommand yöntemi, ADO.Net'teki gibi adlandırılmış parametreleri iletmenize izin vermez ve bu yöntemin belgeleri, parametreli bir sorgunun nasıl yürütüleceğine dair herhangi bir örnek vermez.

Parametreleri nasıl doğru bir şekilde belirlerim?

Yanıtlar:


295

Bunu dene:

var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";

ctx.Database.ExecuteSqlCommand(
    sql,
    new SqlParameter("@FirstName", firstname),
    new SqlParameter("@Id", id));

2
Bu gerçekten doğru işaretli cevap olmalı, yukarıdaki cevap saldırılara eğilimli ve en iyi uygulamalar değil.
Min

8
@Min, kabul edilen cevap, bu cevaptan daha fazla saldırıya eğilimli değildir. Belki de string.Format kullandığını düşünüyorsun - değil.
Simon MᶜKenzie

1
Bu cevap olarak işaretlenmelidir! DateTime ile çalıştığı için bu tek ve doğru cevaptır.
Sven

219

Bunun işe yaradığı ortaya çıktı.

var firstName = "John";
var id = 12;
var sql = "Update [User] SET FirstName = {0} WHERE Id = {1}";
ctx.Database.ExecuteSqlCommand(sql, firstName, id);

13
Bu işe yarayacaktır, ancak bu mekanizma SQL enjeksiyonuna izin verir ve ayrıca ifade tekrar görüntülendiğinde ancak farklı değerlerle veritabanının bir yürütme planını yeniden kullanmasını önler.
Greg Biles

95
@GregB Burada doğru olduğunu sanmıyorum. Örneğin, bir dize hazır bilgisini erken sonlandırmama izin vermeyeceğini test ettim. Ayrıca, kaynak kodu baktı ve herhangi bir ham değerleri parametrelere sarmak için DbCommand.CreateParameter kullandığını bulundu. Yani SQL enjeksiyonu ve güzel bir özlü yöntem çağırma.
Josh Gallagher

7
@JoshGallagher Evet, haklısın. Bir dizeyi düşünüyordum.
Greg Biles

6
SQL Server 2008 R2'den beri çalışmaz. Geçtiğiniz parametreler yerine @ p0, @ p2, ..., @pN olur. SqlParameter("@paramName", value)Bunun yerine kullanın .
Arnthor

Veritabanı tablo sütunları ANSI varchar olarak belirtilirse, hiç kimsenin bu sorgunun performans etkisinden bahsetmediğine inanamıyorum. Net dizeleri unicode, paramaterlerin sunucuya nvarchar olarak aktarılacağı anlamına gelir. Bu, veri katmanının veri çevirisi gerçekleştirmesi gerektiğinden önemli bir performans sorununa neden olur. SqlParameter yaklaşımına uymalı ve veri türlerini belirtmelisiniz.
akd

68

Şunlardan birini yapabilirsiniz:

1) Ham argümanları iletin ve {0} sözdizimini kullanın. Örneğin:

DbContext.Database.SqlQuery("StoredProcedureName {0}", paramName);

2) DbParameter alt sınıf bağımsız değişkenlerini iletin ve @ParamName sözdizimini kullanın.

DbContext.Database.SqlQuery("StoredProcedureName @ParamName", 
                                   new SqlParameter("@ParamName", paramValue);

İlk sözdizimini kullanırsanız, EF aslında argümanlarınızı DbParamater sınıflarıyla sarar, adlar atar ve {0} yerine oluşturulan parametre adıyla değiştirir.

İlk sözdizimi tercih edilirse, bir fabrika kullanmanız veya ne tür bir DbParamater oluşturmanız gerektiğini bilmemeniz gerekir (SqlParameter, OracleParamter, vb.).


6
{0} sözdiziminin veritabanı agnostik olduğu belirtildi. "... bir fabrika kullanmanız veya ne tür bir DbParamaters [sic] oluşturmak için
bilmeniz gerekmiyor

Senaryo 1, enterpolasyonlu bir sürüm lehine kullanımdan kaldırılmıştır. Eşdeğer şimdi: DbContext.Database.ExecuteSqlInterpolated ($ "StoredProcedureName {paramName}");
ScottB

20

Oracle kullanırken diğer yanıtlar işe yaramaz. Bunun :yerine kullanmanız gerekir @.

var sql = "Update [User] SET FirstName = :FirstName WHERE Id = :Id";

context.Database.ExecuteSqlCommand(
   sql,
   new OracleParameter(":FirstName", firstName), 
   new OracleParameter(":Id", id));

Şükürler olsun ki kimse Oracle kullanmıyor. Gönüllü olarak değil! EDIT: Geç şaka için özür dileriz! EDIT: Kötü şaka için özür dileriz!
Chris Bordeman

18

Şunu deneyin (düzenlenmiş):

ctx.Database.ExecuteSqlCommand(sql, new SqlParameter("FirstName", firstName), 
                                    new SqlParameter("Id", id));

Önceki fikir yanlıştı.


Bunu yaptığımda aşağıdaki hatayı alıyorum: "System.Data.Objects.ObjectParameter nesne türünden bilinen bir yönetilen sağlayıcı yerel tür eşleme yok."
Ocak 11:27

Üzgünüm, benim hatam. DbParameter kullanın.
Ladislav Mrnka

7
DbParameter soyuttur. Bir DbParameter oluşturmak için SqlParameter veya bir DbFactory kullanmanız gerekir.
jrummell

12

Varlık Framework Core 2.0 veya üstü için bunu yapmanın doğru yolu:

var firstName = "John";
var id = 12;
ctx.Database.ExecuteSqlCommand($"Update [User] SET FirstName = {firstName} WHERE Id = {id}";

Entity Framework'ün sizin için iki parametre üreteceğini unutmayın, böylece Sql Injection'dan korunursunuz.

Ayrıca NOT:

var firstName = "John";
var id = 12;
var sql = $"Update [User] SET FirstName = {firstName} WHERE Id = {id}";
ctx.Database.ExecuteSqlCommand(sql);

çünkü bu sizi Sql Injection'dan koruyamaz ve hiçbir parametre üretilmez.

Bkz bu daha fazlası.


3
.NET Core 2.0'ı çok sevdiğim için bana neşe veriyor: ')
Joshua Kemmerer

.NET Core 2.0 benim için şimdiye kadar daha yumuşak bir yolculuk olduğu için bazı insanların coşkularını görebiliyorum.
Paul Carlton

İlki SQL enjeksiyonuna karşı savunmasız değil mi? Her ikisi de C # string interpolation kullanır. Birincisi, bildiğim kadarıyla dize genişletmesini önleyemezdi. Bunu kastettiğinizden şüpheleniyorumctx.Database.ExecuteSqlCommand("Update [User] SET FirstName = {firstName} WHERE Id = {id}", firstName, id);
CodeNaked

@CodeNaked, Hayır, bunu demek istemedim. EF sorunun farkındadır ve sizi korumak için iki gerçek parametre oluşturur. Yani bu sadece geçirilen bir dize değildir. Ayrıntılar için yukarıdaki bağlantıya bakın. VS'de denerseniz, sürümüm Sql Injection hakkında bir uyarı vermeyecek ve diğeri olacak.
Greg Gum

1
@GregGum - TIL hakkında FormattableString. Haklısın ve bu oldukça havalı!
CodeNaked

4

Oracle için basitleştirilmiş sürüm. OracleParameter oluşturmak istemiyorsanız

var sql = "Update [User] SET FirstName = :p0 WHERE Id = :p1";
context.Database.ExecuteSqlCommand(sql, firstName, id);

2
SQL Server'da: p0 yerine @ p0 kullanıyorum.
Marek Malczewski

3
var firstName = "John";
var id = 12;

ctx.Database.ExecuteSqlCommand(@"Update [User] SET FirstName = {0} WHERE Id = {1}"
, new object[]{ firstName, id });

Bu çok basit !!!

Parametre referansını bilmek için resim

resim açıklamasını buraya girin


2

Eşzamansız Yöntem ("ExecuteSqlCommandAsync") için şu şekilde kullanabilirsiniz:

var sql = @"Update [User] SET FirstName = @FirstName WHERE Id = @Id";

await ctx.Database.ExecuteSqlCommandAsync(
    sql,
    parameters: new[]{
        new SqlParameter("@FirstName", firstname),
        new SqlParameter("@Id", id)
    });

Bu db-agnostic değil, sadece MS-SQL Server için çalışacaktır. Oracle veya PG için başarısız olacaktır.
ANeves

1

Temel veritabanı veri türleriniz varchar ise, aşağıdaki yaklaşıma bağlı kalmalısınız. Aksi takdirde, sorgunun büyük bir performans etkisi olur.

var firstName = new SqlParameter("@firstName", System.Data.SqlDbType.VarChar, 20)
                            {
                                Value = "whatever"
                            };

var id = new SqlParameter("@id", System.Data.SqlDbType.Int)
                            {
                                Value = 1
                            };
ctx.Database.ExecuteSqlCommand(@"Update [User] SET FirstName = @firstName WHERE Id = @id"
                               , firstName, id);

Farkı görmek için Sql profil oluşturucuyu kontrol edebilirsiniz.


0
public static class DbEx {
    public static IEnumerable<T> SqlQueryPrm<T>(this System.Data.Entity.Database database, string sql, object parameters) {
        using (var tmp_cmd = database.Connection.CreateCommand()) {
            var dict = ToDictionary(parameters);
            int i = 0;
            var arr = new object[dict.Count];
            foreach (var one_kvp in dict) {
                var param = tmp_cmd.CreateParameter();
                param.ParameterName = one_kvp.Key;
                if (one_kvp.Value == null) {
                    param.Value = DBNull.Value;
                } else {
                    param.Value = one_kvp.Value;
                }
                arr[i] = param;
                i++;
            }
            return database.SqlQuery<T>(sql, arr);
        }
    }
    private static IDictionary<string, object> ToDictionary(object data) {
        var attr = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance;
        var dict = new Dictionary<string, object>();
        foreach (var property in data.GetType().GetProperties(attr)) {
            if (property.CanRead) {
                dict.Add(property.Name, property.GetValue(data, null));
            }
        }
        return dict;
    }
}

Kullanımı:

var names = db.Database.SqlQueryPrm<string>("select name from position_category where id_key=@id_key", new { id_key = "mgr" }).ToList();

7
Bu kodu açıklayabilme şansınız ve bunun neden sorulan soruya bir cevap olduğu, böylece daha sonra gelip bulanların anlayabilmesi için?
Andrew Barber

1
Okunabilirliği kaybettiğiniz {0} sözdizimi ile ilgili sorun - kişisel olarak gerçekten hoşlanmıyorum. DbParameter (SqlParameter, OracleParameter vb.) Somut uygulamasını belirtmeniz gereken SqlParameters iletme sorunu. Sağlanan örnek, bu sorunlardan kaçınmanıza olanak tanır.
Neco

3
Sanırım bu geçerli bir görüş, ama aslında burada sorulan soruya cevap vermediniz ; Parametrelerin nasıl ExecuteSqlCommand()aktarılacağı Cevapları gönderirken sorulan soruyu cevapladığınızdan emin olmalısınız.
Andrew Barber

3
Gereksiz yere karmaşık olduğu için indirildi (örn. Yansımayı kullanır, tekerleği yeniden icat eder, farklı veritabanı sağlayıcılarını hesaba katmaz)
bir phu

Olası çözümlerden birini gösterdiği için oy verildi. ExecuteSqlCommand parametreleri SqlQuery gibi aynı şekilde kabul eminim.Ayrıca parametreleri geçen Dapper.net tarzı gibi.
Zar Shardan

0

Saklı yordamda birden çok parametre içeren birden çok parametre:

Dim result= db.Database.ExecuteSqlCommand("StoredProcedureName @a,@b,@c,@d,@e", a, b, c, d, e)

0

Saklı yordamlar aşağıdaki gibi yürütülebilir

 string cmd = Constants.StoredProcs.usp_AddRoles.ToString() + " @userId, @roleIdList";
                        int result = db.Database
                                       .ExecuteSqlCommand
                                       (
                                          cmd,
                                           new SqlParameter("@userId", userId),
                                           new SqlParameter("@roleIdList", roleId)
                                       );

System.Data.SqlClient
FlyingV

0

.NET Core 2.2 FormattableStringiçin dinamik SQL için kullanabilirsiniz .

//Assuming this is your dynamic value and this not coming from user input
var tableName = "LogTable"; 
// let's say target date is coming from user input
var targetDate = DateTime.Now.Date.AddDays(-30);
var param = new SqlParameter("@targetDate", targetDate);  
var sql = string.Format("Delete From {0} Where CreatedDate < @targetDate", tableName);
var froamttedSql = FormattableStringFactory.Create(sql, param);
_db.Database.ExecuteSqlCommand(froamttedSql);

System.Data.SqlClient
FlyingV
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.