0Pricing
C# Academy · درس

SQL خام والإجراءات المخزّنة والاستيفاء

نفّذوا SQL خامًا بأمان باستخدام FromSqlRaw وFromSqlInterpolated والإجراءات المخزّنة، مع تجنّب حقن SQL.

SQL خام والإجراءات المخزّنة والاستيفاء درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 4 دروس في المجموع.

متى تستخدم SQL الخام في EF Core

تعمل معظم الاستعلامات جيدًا باستخدام LINQ، لكن بعض السيناريوهات تتطلب SQL خامًا، مثل التحليلات المعقدة، أو الدوال الخاصة بمورّد معين، أو الإجراءات المخزنة، أو الاستعلامات الحرجة للأداء التي لا تترجمها LINQ جيدًا.

يوفر EF Core طرقًا آمنة لاستخدام SQL الخام دون التضحية بالأمان.

FromSqlRaw: استعلامات آمنة ذات معلمات

ينفّذ FromSqlRaw SQL خامًا ويطابق النتائج مع الكيانات. استخدم دائمًا العناصر النائبة {0} — ولا تستخدم أبدًا دمج السلاسل النصية — لمنع حقن SQL.

// SAFE: parameterized
var products = await _db.Products
    .FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 100m)
    .ToListAsync();

// DANGEROUS: never do this
// FromSqlRaw("SELECT * FROM Products WHERE Price > " + price)

FromSqlInterpolated: أمان الاستيفاء

يقبل FromSqlInterpolated سلسلة نصية استيفائية في C#. ويحوّل EF Core القيم المستوفاة تلقائيًا إلى معلمات SQL، ما يجمع بين الأمان وسهولة القراءة.

decimal minPrice = 100m;
string category = "Electronics";

var products = await _db.Products
    .FromSqlInterpolated(
        $"SELECT * FROM Products WHERE Price > {minPrice} AND Category = {category}")
    .OrderBy(p => p.Name)
    .ToListAsync();
// Values become @p0, @p1 parameters automatically

تركيب LINQ بعد FromSql

يمكنك تسلسل عوامل LINQ بعد FromSql. يغلّف EF Core SQL الخام في استعلام فرعي، ثم يطبّق LINQ عليه، وهو ما يفيد في إضافة التصفية أو تقسيم النتائج إلى صفحات.

decimal min = 50m;
var page = await _db.Products
    .FromSqlInterpolated($"SELECT * FROM Products WHERE Price > {min}")
    .Where(p => p.IsActive)        // additional LINQ filter
    .OrderBy(p => p.Price)
    .Skip(0).Take(20)              // pagination
    .ToListAsync();
// Generates: SELECT ... FROM (raw SQL) AS p WHERE ...

تنفيذ الإجراءات المخزنة

استدعِ الإجراءات المخزنة التي تُرجع صفوف كيانات باستخدام FromSqlRaw. أما الإجراءات التي لا تُرجع صفوفًا، مثل INSERT وUPDATE، فاستخدم معها ExecuteSqlRawAsync.

// Stored proc returning Products
var products = await _db.Products
    .FromSqlRaw("EXEC GetProductsByCategory {0}", "Books")
    .ToListAsync();

// Stored proc with no return rows
await _db.Database.ExecuteSqlRawAsync(
    "EXEC ArchiveOldOrders {0}",
    DateTime.UtcNow.AddYears(-1));

ExecuteSqlInterpolated لعبارات DML

ينفّذ ExecuteSqlInterpolated عبارات DML مثل INSERT وUPDATE وDELETE بأمان السلاسل الاستيفائية. ويُرجع عدد الصفوف المتأثرة.

int days = 30;
int affected = await _db.Database.ExecuteSqlInterpolatedAsync(
    $"UPDATE Orders SET Status = 'Expired' WHERE PlacedAt < {DateTime.UtcNow.AddDays(-days)}");

Console.WriteLine($"{affected} orders expired");

الاستعلام عن الأنواع غير الكيانية

استخدم SqlQuery<T> (في .NET 7+) للاستعلام عن أنواع عددية أو DTO مخصصة لا تمثل كيانات في EF Core. ولا يتطلب ذلك وجود DbSet.

// Query a DTO directly — no entity needed
var stats = await _db.Database
    .SqlQuery<ProductStats>(
        $"SELECT Category, COUNT(*) AS Count, AVG(Price) AS AvgPrice FROM Products GROUP BY Category")
    .ToListAsync();

public record ProductStats(string Category, int Count, decimal AvgPrice);

أنواع الكيانات بلا مفتاح لواجهات العرض

اربط واجهة عرض في قاعدة البيانات أو نتيجة استعلام خامًا بـنوع كيان بلا مفتاح باستخدام HasNoKey() وToView(). ويمكنك الاستعلام عنه مثل DbSet من دون الحاجة إلى مفتاح أساسي.

public class ProductSalesSummary
{
    public string Category { get; set; } = "";
    public int TotalSold { get; set; }
    public decimal Revenue { get; set; }
}

// In OnModelCreating:
modelBuilder.Entity<ProductSalesSummary>()
    .HasNoKey()
    .ToView("vw_ProductSalesSummary");

// Query:
var summary = await _db.Set<ProductSalesSummary>().ToListAsync();

تجنب مخاطر حقن SQL

القاعدة الذهبية: لا تدمج أبدًا إدخال المستخدم داخل سلاسل SQL. استخدم دائمًا الاستعلامات ذات المعلمات عبر العناصر النائبة في FromSqlRaw، أو FromSqlInterpolated، أو LINQ في EF Core.

// SAFE
var name = userInput;
var result = await _db.Products
    .FromSqlInterpolated($"SELECT * FROM Products WHERE Name = {name}")
    .ToListAsync();

// INJECTION RISK (never do this)
// FromSqlRaw("SELECT * FROM Products WHERE Name = '" + name + "'");

معلمات الإخراج مع الرجوع إلى ADO.NET

لا يدعم EF Core معلمات إخراج الإجراءات المخزنة دعمًا أصليًا. في هذه الحالات، انتقل إلى ADO.NET باستخدام _db.Database.GetDbConnection().

await _db.Database.OpenConnectionAsync();
var conn = _db.Database.GetDbConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "EXEC GetOrderCount @CustomerId, @Count OUTPUT";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@CustomerId", 5));
var outParam = new SqlParameter("@Count", SqlDbType.Int) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(outParam);
await cmd.ExecuteNonQueryAsync();
int count = (int)outParam.Value;

عمليًا: الرجوع إلى البحث في النص الكامل

لا تستطيع LINQ التعبير عن شروط البحث في النص الكامل الخاصة بقاعدة بيانات معينة. استخدم FromSqlInterpolated للاستفادة من إمكانات FREETEXT أو tsvector الأصلية.

string query = "keyboard mechanical";
var results = await _db.Products
    .FromSqlInterpolated(
        $"SELECT * FROM Products WHERE FREETEXT(Name, {query})")
    .AsNoTracking()
    .Take(20)
    .ToListAsync();

تحقق سريع

لماذا يُعد FromSqlInterpolated أكثر أمانًا من دمج السلاسل النصية في الاستعلامات ذات المعلمات؟

مراجعة: SQL الخام في EF Core

أهم النقاط:

  • FromSqlRaw: استخدم العناصر النائبة {0}؛ ولا تدمج إدخال المستخدم أبدًا
  • FromSqlInterpolated: سلاسل استيفائية آمنة ← معلمات ADO.NET
  • سلسّل عوامل LINQ بعد FromSql لإضافة التصفية والترتيب وتقسيم النتائج إلى صفحات
  • ExecuteSqlInterpolatedAsync: تنفيذ آمن لعبارات DML
  • SqlQuery<T>: الاستعلام عن DTO غير الكيانات (في .NET 7+)
  • استخدم ADO.NET مباشرةً لمعلمات الإخراج أو الإجراءات المخزنة المعقدة

الأسئلة الشائعة

هل درس «SQL خام والإجراءات المخزّنة والاستيفاء» مجاني؟

نعم — نص درس «SQL خام والإجراءات المخزّنة والاستيفاء» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 4 دروس في المجموع.

ماذا ستتعلم في «SQL خام والإجراءات المخزّنة والاستيفاء»؟

نفّذوا SQL خامًا بأمان باستخدام FromSqlRaw وFromSqlInterpolated والإجراءات المخزّنة، مع تجنّب حقن SQL. تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟

لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.

كم من الوقت يستغرق درس «SQL خام والإجراءات المخزّنة والاستيفاء»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟

نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. التحميل المبكر والكسول والصريح
  2. SQL خام والإجراءات المخزّنة والاستيفاء
  3. الاستعلامات المجمّعة والأداء
  4. عوامل تصفية الاستعلام العامة والكيانات المملوكة
← العودة إلى C# Academy