فيه أكتر من طريقة تقدر تستخدمها للتعامل مع نتائج الـ Stored Procedure في .NET، حسب احتياجاتك وحسب الـ Library اللي بتستخدمها.

1. استخدام DataTable

بنستخدم فيها SqlDataAdapter و DataTable عشان نخزن البيانات في جدول داخل الذاكرة. الطريقة دي مناسبة لو عايز تخزن النتائج في صيغة جدولية تقدر تتعامل معها بسهولة، وخصوصًا لو عندك أكتر من صف في النتيجة.


2. استخدام DataReader مع ADO.NET

بدل ما تستخدم DataTable، تقدر تستخدم SqlDataReader لو عايز تجيب البيانات بشكل أسرع، وده بيفيد لو بتتعامل مع كمية كبيرة من البيانات أو محتاج أداء أعلى. DataReader بيقرأ البيانات صف بصف (Row by Row) من قاعدة البيانات من غير ما يخزنها كلها في الذاكرة، فبيكون أسرع وأقل في استهلاك الموارد.

مثال:

using System.Data;
using System.Data.SqlClient;
 
public void GetUserById(int userId)
{
    using (SqlConnection connection = new SqlConnection(_connectionString))
    {
        using (SqlCommand cmd = new SqlCommand("GetUserById", connection))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Add(new SqlParameter("@UserId", userId));
 
            connection.Open();
 
            using (SqlDataReader reader = cmd.ExecuteReader())
            {
                while (reader.Read())
                {
                    Console.WriteLine("User ID: " + reader["UserId"]);
                    Console.WriteLine("Name: " + reader["Name"]);
                    Console.WriteLine("Email: " + reader["Email"]);
                }
            }
        }
    }
}

في المثال ده، بنستخدم SqlDataReader لقراءة البيانات صف بصف من غير ما نخزنها في DataTable.

Empty?

تقدر تستخدم HasRows للتأكد إن الـ SqlDataReader فيه بيانات قبل استخدام Read().

الكود:

using (SqlDataReader reader = cmd.ExecuteReader())
{
    if (reader.HasRows)
    {
        if (reader.Read())
        {
            return new User
            {
                UserId = (int)reader["UserId"],
                Name = reader["Name"].ToString(),
                Email = reader["Email"].ToString()
            };
        }
    }
    else
    {
        Console.WriteLine("No data found.");
    }
}

3. استخدام الـ Entity Framework وإرجاع البيانات كـ Objects

لو بتستخدم Entity Framework، مش محتاج تخزن النتايج في DataTable، تقدر ترجع النتايج على طول كـ List من الكائنات (Objects)، زي ما شفنا في المثال اللي فات باستخدام FromSqlRaw. وده بيسهل التعامل مع البيانات بشكل كائنات بدل الجداول.

مثال باستخدام Entity Framework:

public async Task<List<User>> GetUserByIdAsync(int userId)
{
    return await _context.Users
        .FromSqlRaw("EXEC GetUserById @UserId = {0}", userId)
        .ToListAsync();
}

الطريقة دي بترجع List<User> على طول، من غير ما نحتاج نخزن النتايج في DataTable.


4. تخزين النتيجة في كائن (Object) مخصص

لو عندك نتيجة بسيطة، ممكن تنشئ كائن مخصص (Custom Object) وتحط فيه النتايج اللي راجعة من Stored Procedure بدل DataTable. الطريقة دي مفيدة لو النتيجة عبارة عن صف واحد أو عدد قليل من الأعمدة.

مثال:

public class User
{
    public int UserId { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}
 
public User GetUserById(int userId)
{
    using (SqlConnection connection = new SqlConnection(_connectionString))
    {
        using (SqlCommand cmd = new SqlCommand("GetUserById", connection))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Add(new SqlParameter("@UserId", userId));
 
            connection.Open();
 
            using (SqlDataReader reader = cmd.ExecuteReader())
            {
                if (reader.Read())
                {
                    return new User
                    {
                        UserId = (int)reader["UserId"],
                        Name = reader["Name"].ToString(),
                        Email = reader["Email"].ToString()
                    };
                }
            }
        }
    }
 
    return null; // في حالة عدم وجود بيانات
}

هنا بنرجع كائن User مباشرة بدل DataTable، وده بيكون مناسب لو بتتعامل مع نتيجة مكونة من صف واحد.


5. استخدام ExecuteScalar لو النتيجة قيمة واحدة

لو الاستعلام أو الـ Stored Procedure بيرجع قيمة واحدة بس (مثلاً عدد المستخدمين أو قيمة معينة)، ممكن تستخدم ExecuteScalar بدل ما تستخدم DataTable أو DataReader.

مثال:

public int GetUserCount()
{
    using (SqlConnection connection = new SqlConnection(_connectionString))
    {
        SqlCommand cmd = new SqlCommand("SELECT COUNT(*) FROM Users", connection);
        
        connection.Open();
        int userCount = (int)cmd.ExecuteScalar();
        
        return userCount;
    }
}

ملخص

  • الـDataTable: مناسب لو عايز تخزن البيانات بشكل جدول وتتعامل مع أكتر من صف في الذاكرة. (أكتر من صف)
  • الـDataReader: أسرع وأخف على الذاكرة، وبيفيد لما يكون عندك كمية بيانات كبيرة. (صف واحد - كائن)
  • الـEntity Framework: بيرجع النتايج كـ List من الكائنات مباشرة، وبيسهل التعامل مع البيانات في شكل Object-Oriented.
  • الـ ExecuteScalar: استخدم ExecuteScalar لو النتيجة عبارة عن قيمة واحدة (زي عدد أو قيمة). (قيمة واحدة)
  • كائن مخصص (Custom Object): ممكن ترجع كائن مباشرة لو النتيجة صف واحد أو بيانات بسيطة، بدل ما تستخدم DataTable.

على حسب احتياجاتك، تقدر تختار الطريقة المناسبة للتعامل مع نتائج الـ Stored Procedure.