قراءة البيانات من قاعدة البيانات باستخدام ADO.NET في تطبيقات ASP.NET Core

-

بنتعلم في هذا الدرس عملية القراءة من قواعد البيانات في ADO.NET  وعرضها في View. لتنفيذ ذلك بنحتاج نستخدم object من نوع SqlDataReader  الي هي مشتقة من class باسم DbDataReader .

تمام 

تعتبر SqlDataReader class مفيدة عندما نحتاج إلى الحصول على كميات كبيرة من البيانات بسرعة ولا حاجه إلى الاحتفاظ بنسخه منها في الذاكرة.في مثالنا هون بنرجع بيانات المواد والطلاب من قاعدة البيانات وبعدها بنعرضها على الشاشة. 

ملاحظة: SqlDataReader objects تعمل في بيئة متصلة بمعنى اخر حتي تعمل هذه الطريقة لازم نفتح اتصال مع قاعدة البيانات واكيد هذا الاتصال يبقى مفتوح حتى يتم إغلاقهً.

يمكنك الحصول على SqlDataReader objects من SqlCommand object  عن طريق استخدام الامر ()ExecuteReader.

تمام التمام .لاستخدام هذه الامر سنقوم بقراءة جميع السجلات التي قمنا بحفظها سابقا في جدول الطلاب وعرضها في View  مشان هيك خلونا نبدأ بعمل هذا View 

نبدا بفتح المشروع الخاص بنا StudentsAcademy ثم انتقل الى StudentsController ومن ثم قم بإضافة action باسم AllStudent ، ثم اضف View من خلال هذا ال action 


بعد هيك نضيف الكود التالي في action المسمى AllStudent  : 

public IActionResult AllStudent()
        {
            List<StudentsModel> StudentsModelList = new List<StudentsModel>();
            try
            {
                string connectionString = Configuration["ConnectionStrings:DefaultConnection"];
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    string sql = $"select * from Students";
                    SqlCommand command = new SqlCommand(sql, connection);
                    connection.Open();
                    // Obtain a data reader via ExecuteReader().
                    using (SqlDataReader dataReader = command.ExecuteReader())
                    {
                        // Loop over the results
                        while (dataReader.Read())
                        {
                            StudentsModelList.Add(new StudentsModel
                            {
                                StudentID = Convert.ToInt32(dataReader["StudentId"]),
                                StudentNo = Convert.ToString(dataReader["StudentNo"]),
                                FullName = Convert.ToString(dataReader["FullName"]),
                                Birthday = Convert.ToDateTime(dataReader["Birthday"]),
                                Address = Convert.ToString(dataReader["Address"]),
                                UserName = Convert.ToString(dataReader["Address"]),
                                Email = Convert.ToString(dataReader["Email"]),
                                Mobile = Convert.ToString(dataReader["Mobile"]),
                                CreatedDate = Convert.ToDateTime(dataReader["CreatedDate"]),
                            });
                        }
                    }
                    connection.Close();
                } 
            }
            catch (Exception ex)
            {
            }
            finally
            {
            }
            return View(StudentsModelList);
        }
نفهم الكود بشكل سريع
استخدمنا هنا الامر ()dataReader.Read الذي يعمل على قراءة جميع الحقول واحد تلو الاخر. ولقراءة محتويات كل حقل يجب استخدام الامر
dataReader["Filed Name"]
لاحظ الأوامر التي تم استخدامها: 
StudentID = Convert.ToInt32(dataReader["StudentId"]),
StudentNo = Convert.ToString(dataReader["StudentNo"]),
FullName = Convert.ToString(dataReader["FullName"]),
Birthday = Convert.ToDateTime(dataReader["Birthday"]),
Address = Convert.ToString(dataReader["Address"]),
UserName = Convert.ToString(dataReader["Address"]),
Email = Convert.ToString(dataReader["Email"]),
Mobile = Convert.ToString(dataReader["Mobile"]),
CreatedDate = Convert.ToDateTime(dataReader["CreatedDate"]),

بعد هيك عرفنا List من نوع StudentsModel باستخدام الكود: 

List<StudentsModel> StudentsModelList = new List<StudentsModel>();
بعد ذلك، بنعمل تنفيذ استعلام بسيط query لقراءة هذه السجلات واستخدمنا الكود:
string sql = $"select * from Students";
وبعدها بنستخدم طريقة ()SqlCommand’s ExecuteReader . في هاذه الطريقة SqlDataReader بتم عمل تكرار عبر جميع السجلات في البيئة المتصلة بحيث يتم في نفس الوقت إضافة قيمة كل عمود إلى StudentsModelList Object :

باستخدام الكود: 

using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    string sql = $"select * from Students";
                    SqlCommand command = new SqlCommand(sql, connection);
                    connection.Open();
                    // Obtain a data reader via ExecuteReader().
                    using (SqlDataReader dataReader = command.ExecuteReader())
                    {         
                        // Loop over the results
                        while (dataReader.Read())
                        {
                            StudentsModelList.Add(new StudentsModel
                            {
                                StudentID = Convert.ToInt32(dataReader["StudentId"]),
                                StudentNo = Convert.ToString(dataReader["StudentNo"]),
                                FullName = Convert.ToString(dataReader["FullName"]),
                                Birthday = Convert.ToDateTime(dataReader["Birthday"]),
                                Address = Convert.ToString(dataReader["Address"]),
                                UserName = Convert.ToString(dataReader["Address"]),
                                Email = Convert.ToString(dataReader["Email"]),
                                Mobile = Convert.ToString(dataReader["Mobile"]),
                                CreatedDate = Convert.ToDateTime(dataReader["CreatedDate"]),
                            });
                        }
}
وبهذه الطريقة يتم ملء جميع البيانات الموجودة في student table في List object. ويتم الملئ باستخدام الكود 
(قمنا باستخدام أسماء الأعمدة بشكل ثابت من أجل الحصول على قيمها)، مثل -
dataReader["StudentId"]
dataReader["StudentNo"]
dataReader["FullName"] 
واخر اشي بنعمل استدعاء View باسم AllStudent وبنرسلها object List من نوع StudentsModel .

يمكن تجنب تثبيت اسم الأعمدة باستخدام الخاصية FieldCount مع الطرق ()GetName() and GetValue  من SqlDataReader class. كما هو موضح أدناه:
• FieldCount - إرجاع العدد الإجمالي للأعمدة في السجلات الحالية SqlDataReader object.
• ()GetName - يحصل على اسم العمود المحدد.
• ()GetValue - الحصول على قيمة العمود المحدد.
المثال التالي يوضح ذلك:
using (SqlDataReader dataReader = command.ExecuteReader())
{
    while (dataReader.Read())
    {
        for (int i = 0; i < dataReader.FieldCount; i++)
        {
            string currentColName = dataReader.GetName(i);
            string currentColValue = Convert.ToString(dataReader.GetValue(i));
        }
    }
}   
يمكن الحصول على نتائج من جدولين عن طريق تشغيل 2 SQL queries مثل -
string sql = "Select * From student; Select * from Report";
هذا الاستعلام يسمى Multiple Result Sets. ولقراءة البيانات في مثل هذه الحالة، يمكن تكرار كل نتيجة من هذه النتائج باستخدام طريقة NextResult ل SqlDataReader class.
في الكود أدناه ، تم استخدام NextResult لقراءة جميع الحقول من الاستعلام:
do
{
    while (dataReader.Read())
    {
        for (int i = 0; i < dataReader.FieldCount; i++)
        {
            string currentColName = dataReader.GetName(i);
            string currentColValue = Convert.ToString(dataReader.GetValue(i));
        }
    }
} while (dataReader.NextResult());

عرض البيانات داخل View 
انتقل الى View  الذي اضفناه باسم AllStudent  ثم أضف الكود التالي: 

@model List<StudentsModel>
<link href="~/lib/bootstrap/dist/css/bootstrap.css.map" rel="stylesheet" />
<table class="table table-bordered table-sm table-striped">
    <thead>
        <tr>
            <th scope="col">Student No</th>
            <th scope="col">FullName</th>
            <th scope="col">Birthday</th>
            <th scope="col">Address</th>
            <th scope="col">UserName</th>
            <th scope="col">Email</th>
            <th scope="col">Mobile</th>
            <th scope="col">CreatedDate</th>
            <th scope="col">Del</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var student in Model)
        {
        <tr>
            <td>
                @student.StudentNo
            </td>
            <td>
                @student.FullName
            </td>
            <td>
              @Convert.ToDateTime(@student.Birthday).ToString("dd/MM/yyyy")
            </td>
            <td>
                @student.Address
            </td>
            <td>
                @student.UserName
            </td>
            <td>
                @student.Email
            </td>
            <td>
                @student.Mobile
            </td>
            <td>
               @string.Format("{0:dd MMMM yyyy}",@student.CreatedDate)
            </td>
            <td>
                <a onclick="DeleteStudent(@student.StudentID);" class="btn btn-danger small">Delete</a>
            </td>
        </tr>
        }
    </tbody>
</table>
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
تم استقبال StudentsModel على شكل List في هذا ال View ومن ثم تم عرضه على شكل جدول 
تم إضافة أسماء الحقول في البداية في Head باستخدام الكود 
    <thead>
        <tr>
            <th scope="col">Student No</th>
            <th scope="col">FullName</th>
            <th scope="col">Birthday</th>
            <th scope="col">Address</th>
            <th scope="col">UserName</th>
            <th scope="col">Email</th>
            <th scope="col">Mobile</th>
            <th scope="col">CreatedDate</th>
            <th scope="col">Del</th>
        </tr>
    </thead>
ثم استخدمنا الكود foreach لعرض جميع القيم في List التي تم حفظها في AllStudent action 
الكود الذي يعمل على ذلك: 

@foreach (var student in Model)
        {
        <tr>
            <td>
                @student.StudentNo
            </td>
            <td>
                @student.FullName
            </td>
            <td>
              @Convert.ToDateTime(@student.Birthday).ToString("dd/MM/yyyy")
            </td>
            <td>
                @student.Address
            </td>
            <td>
                @student.UserName
            </td>
            <td>
                @student.Email
            </td>
            <td>
                @student.Mobile
            </td>
            <td>
@string.Format("{0:dd MMMM yyyy}",@student.CreatedDate)
            </td>
            <td>
                <a onclick="DeleteStudent(@student.StudentID);" class="btn btn-danger small">Delete</a>
            </td>
        </tr>
        }

تم تغيير طريقة عرض التواريخ بحيث يكون على شكل dd/MM/yyyy  
الكود الذي يعمل على ذلك :
@Convert.ToDateTime(@student.CreatedDate).ToString("dd/MM/yyyy")
يمكن استخدام طريقة ثانية لتغير طريقة عرض التاريخ باستخدام الكود 
  @string.Format("{0:dd MMMM yyyy}",@student.CreatedDate)
حيث سيتم عرض تاريخ الانشاء بصيغة dd MMMM yyyy
أخر عمود هو button لغرض الحذف حيث تم استدعاء JavaScript function باسم DeleteStudent  وتم ارسال رقم الطالب كمتغير 
سنتقوم لاحقا بتطبيق كود الحذف
نتيجة التنفيذ
قم بتشغيل التطبيق بالانتقال الى الرابط 
https://localhost:44382/students/allstudent
ستكون نتيجة التنفيذ كما في الشكل في الأسفل: 


لا تنسى التأكد من إضافة ملف bootstrap.css.map  لتحسين شكل الجدول 
الكود:
<link href="~/lib/bootstrap/dist/css/bootstrap.css.map" rel="stylesheet" />


استخدام Stored Procedure مع SqlDataReader
يمكن أيضًا استخدام Stored Procedure  مع SqlDataReader لقراءة جميع السجلات من جدول الطلاب في قاعدة البيانات.
 للقيام بذلك، قم أولاً بإضافة stored procedure (الوارد أدناه) إلى قاعدة بيانات SQL Server الخاصة بك:

CREATE PROCEDURE [dbo].[ReadStudent]
AS     
BEGIN     
  Select * From Students
End
انتقل الان الى StudentsController  ومن ثم قم بتغيير بسيط في الكود داخلaction  المسمى  AllStudent: 
فقط قم بإعطاء اسم SP ل 1st parameter  ل SqlCommand object وقم بتعيين 
CommandType to StoredProcedure. الكود موضح في الاسفل:

using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    string sql = "ReadStudent";
    SqlCommand command = new SqlCommand(sql, connection);
    command.CommandType = CommandType.StoredProcedure;
  
    using (SqlDataReader dataReader = command.ExecuteReader())
    {
        while (dataReader.Read())
        {
            //…
        }
    }
    connection.Close();
}
بقية الكود لا تغيير عليه. 

فهم SqlDataAdapter Object 

يمكن قراءة البيانات من الجداول في قواعد البيانات باستخدام SqlDataAdapter class. ولكن الامر مختلف قليلا هنا عن SqlDataReader ، يعمل SqlDataAdapter object  في بيئة غير متصلة disconnected environment  بحيث يتم الاتصال تلقائيًا عند البدء بملء البيانات من قاعدة البيانات ، كما أنه يغلق الاتصال بمجرد جلب البيانات بالكامل. 

هذا يعني أنك لست مضطرًا إلى فتح الاتصال وإغلاقه بشكل صريح.
لتنفيذ هاذ الاشي اول خطوه هي تحديد كائن SqlDataAdapter ،ولعمل هذا التحديد يجب  constructor ب  SqlCommand object ، مثال ذلك:

SqlDataAdapter dataAdapter = new SqlDataAdapter(sqlcommand);
يحتوي SqlDataAdapter على الطريقة fill() method. والي بتعمل على تعبئة البيانات الراجعه من قاعدة البيانات في object من نوع DataTable  .
تمام شو يعني هذا الكلام
هاذ يعني ان الان صار عندنا بيانات داخل  DataTable وبنقدر نتعامل مع هاي البيانات بدون الاتصال بقاعدة البيانات.
يُظهر code أدناه كيفية استخدام SqlDataAdapter من اجل ملء سجلات في DataTable object.


public IActionResult AllStudent()
        {
            List<StudentsModel> StudentsModelList = new List<StudentsModel>();
            try
            {
                string connectionString = Configuration["ConnectionStrings:DefaultConnection"];
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    DataTable dataTable = new DataTable();
                    string sql = "Select * From Students";
                    SqlCommand command = new SqlCommand(sql, connection);
                    SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
                    // filling records to DataTable
                    dataAdapter.Fill(dataTable);
                    foreach (DataRow item in dataTable.Rows)
                    {
                        StudentsModelList.Add(new StudentsModel
                        {
                            StudentID = Convert.ToInt32(item["StudentId"]),
                            StudentNo = Convert.ToString(item["StudentNo"]),
                            FullName = Convert.ToString(item["FullName"]),
                            Birthday = Convert.ToDateTime(item["Birthday"]),
                            Address = Convert.ToString(item["Address"]),
                            UserName = Convert.ToString(item["Address"]),
                            Email = Convert.ToString(item["Email"]),
                            Mobile = Convert.ToString(item["Mobile"]),
                            CreatedDate = Convert.ToDateTime(item["CreatedDate"]),
                        });
                    }
                }
            }
            catch (Exception ex)
            {
            }
            finally
            {
            }
            return View(StudentsModelList);
        }
إذا كنت ترغب في قراءة السجلات باستخدام Stored Procedure  ، فسيصبح الكود أعلاه بالشكل التالي:

public IActionResult AllStudent()
        {
            List<StudentsModel> StudentsModelList = new List<StudentsModel>();
            try
            {
                string connectionString = Configuration["ConnectionStrings:DefaultConnection"];
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    DataTable dataTable = new DataTable();
                    string sql = "ReadStudent";
                    SqlCommand command = new SqlCommand(sql, connection);
                    command.CommandType = CommandType.StoredProcedure;
                    SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
                    // filling records to DataTable
                    dataAdapter.Fill(dataTable);
                    foreach (DataRow item in dataTable.Rows)
                    {
                        StudentsModelList.Add(new StudentsModel
                        {
                            StudentID = Convert.ToInt32(item["StudentId"]),
                            StudentNo = Convert.ToString(item["StudentNo"]),
                            FullName = Convert.ToString(item["FullName"]),
                            Birthday = Convert.ToDateTime(item["Birthday"]),
                            Address = Convert.ToString(item["Address"]),
                            UserName = Convert.ToString(item["Address"]),
                            Email = Convert.ToString(item["Email"]),
                            Mobile = Convert.ToString(item["Mobile"]),
                            CreatedDate = Convert.ToDateTime(item["CreatedDate"]),
                        });
                    }
                }
            }
            catch (Exception ex)
            {
            }
            finally
            {
            }
            return View(StudentsModelList);
        }
وبهيك بنكون خلصنا الدرس