يعتبر المجال System.Data.Dataset تمثيلا للبيانات في الذاكرة وهو مفيد في الوصول إلى نسخة منفصلة من البيانات التي تأتي من مصدر بيانات خارجي. وبغض النظر عن ذلك المصدر فالتمثيل الداخلي للـ DataSet يكون بالصورة العلائقية متضمنا الجداول والشروط والعلاقات بين الجداول. وبكلمات أخرى يمكنك أن تعتبر الـ DataSet قاعدة بيانات في الذاكرة فهي تعتبر هدف جيد لأدوات Linq.
استخدام Linq لتحميل مجموعة البيانات
يمكن تحميل الـ DataSet عن طريق الاستعلام من قاعدة بيانات علائقية وأحد الطرق لعمل ذلك هو عن طريق DataAdapter كما هو ظاهر في الكود
DataSet ds = new DataSet("CustomerOrders");
SqlDataAdapter da = new SqlDataAdapter( QueryOrders, ConnectionString );
da.SelectCommand.Parameters.AddWithValue( "@CustomerID", "QUICK" );
da.TableMappings.Add( "Table", "Orders" );
da.TableMappings.Add( "Table1", "OrderDetails" );
da.Fill( ds );
const string ConnectionString = "Database=Northwind;Trusted_Connection=yes";
const string QueryOrders = @"
SET @CustomerID = 'QUICK'
SELECT OrderID, OrderDate, Freight, ShipName,
ShipAddress, ShipCity, ShipCountry
FROM Orders
WHERE CustomerID = @CustomerID
SELECT od.OrderID, od.UnitPrice, od.Quantity, od.Discount,
p.[ProductName]
FROM [Order Details] od
INNER JOIN Orders o
ON o.[OrderID] = od.[OrderID]
LEFT JOIN Products p
ON p.[ProductID] = od.[ProductID]
WHERE o.CustomerIDحيث يقوم الكود السابق بجمع جدولي بيانات في مجموعة بيانات واحدة توافق الطلبات المقدمة من قبل زبون معين
استخدام Linq للاستعلام من مجموعة البيانات
يمكن الاستعلام عن جدول البيانات DataTable باستخدام Linq تماما كأي قائمة قابلة للتعداد Enumerable<T> مع أنه لا يحققها وهنا عليك استدعاء الدالة الموسعة AsEnumerable التي توسع جدول البيانات للحصول على غلاف يحقق تلك الواجهة.
ويتم تكوين القائمة من مجموعة من أغراض DataRow وهكذا يجب عليك استخدام خصائص سطر البيانات DataRow للحصول على قيم الحقول. مما يمكنك من استدعاء عنصر عائد لسطر البيانات DataRow بدلا من استخدام تعبير الاستعلام عبر جدول البيانات DataTable حيث يمكنك استخدام طريقة الوصول Field<T> بدلا من استخدام التحويل المباشر على النتيجة عند استخدام طريقة الوصول القياسية. ويرينا الاستعلام التالي الطلبات الوقعة في عام 1998 أو ما بعد
DataSet ds = LoadDataSetUsingDataAdapter(); DataTable orders = ds.Tables["Orders"]; DataTable orderDetails = ds.Tables["OrderDetails"]; var query = from o in orders.AsEnumerable() where o.Field<DateTime>( "OrderDate" ).Year >= 1998 orderby o.Field<DateTime>( "OrderDate" ) descending select o;
والطريقتان AsEnumerable و Field<T> هما طريقتان موسعتان لجدول البيانات DataTable و سطر البيانات DataRow وهما معرفتان في مجالي الأسماء System.Data.DataTableExtensions و System.Data.DataRowExtensions.
فعندما يكون لديك عدة أغراض DataTable في DataSet قد تريد استخدام نوعا من الدمج والاستعلام التالي يقوم بجمع مجموع كل طلب من العام 1998 إلى الوقت الحاضر
DataSet ds = LoadDataSetUsingDataAdapter();
DataTable orders = ds.Tables["Orders"];
DataTable orderDetails = ds.Tables["OrderDetails"];
var query =
from o in orders.AsEnumerable()
join od in orderDetails.AsEnumerable()
on o.Field<int>( "OrderID" ) equals od.Field<int>( "OrderID" )
into orderLines
where o.Field<DateTime>( "OrderDate" ).Year >= 1998
orderby o.Field<DateTime>( "OrderDate" ) descending
select new { OrderID = o.Field<int>( "OrderID" ),
OrderDate = o.Field<DateTime>( "OrderDate" ),
Amount = orderLines.Sum(
od => od.Field<decimal>( "UnitPrice" )
* od.Field<short>( "Quantity" ) ) };في المثال السابق قمنا بتحديد علاقة بين الجدولين Orders و OrdersDetails عبر صيغة دمج Join Syntax وإن كانت الـ DataSet تحتوي على علاقات بينهما فـ Linq يستفيد منها وفي الاستعلام التالي نستخدم GetChildRows للحصول على أسطر تفاصيل الطلب بدلا عن دمج الجدولين
DataSet ds = LoadDataSetUsingDataAdapter();
DataTable orders = ds.Tables["Orders"];
DataTable orderDetails = ds.Tables["OrderDetails"];
ds.Relations.Add( "OrderDetails",
orders.Columns["OrderID"],
orderDetails.Columns["OrderID"]);
var query =
from o in orders.AsEnumerable()
where o.Field<DateTime>( "OrderDate" ).Year >= 1998
orderby o.Field<DateTime>( "OrderDate" ) descending
select new { OrderID = o.Field<int>( "OrderID" ),
OrderDate = o.Field<DateTime>( "OrderDate" ),
Amount = o.GetChildRows( "OrderDetails" ).Sum(
od => od.Field<decimal>( "UnitPrice" )
* od.Field<short>( "Quantity" ) ) };استخدام Linq للاستعلام من Typed Dataset
يمكن الاستعلام من Typed DataSet بطريقة أبسط لأنه يصبح من غير الضروري استخدام الطريقتين Field<T> و AsEnumerable. فإن قمت بإنشاء Typed DataSet باستخدام فيجول ستوديو فستكون فئات Typed DataTable الخاصة بك مشتقة من الفئة TypedTableBase<T> والتي تحقق الواجهة IEnumerable<T> ولهذا السبب أنت غير مضطر لاستدعاء الدالة AsEnumerable.
والاستعلام السابق الذي يستخدم لرفع العلاقات الموجودة في الـ DataSet يمكن أن يعاد كتابته باستخدام Typed DataSet بالشكل
var query =
from o in ds.Orders
where o.OrderDate.Year >= 1998
orderby o.OrderDate descending
select new { o.OrderID, o.OrderDate,
Amount = o.GetOrder_DetailsRows().Sum(
od => od.UnitPrice * od.Quantity ) };وكما ترى فصيغة الاستعلام تكون أبسط ومشابهة للاستعلام السابق ومع ذلك يمكنك استخدام مخطط محدد سلفا Typed DataSet للاستعلام من الـ DataSet بطريقة مشابهة مما يمنع استخدام هذه الصيغة مع DataSet تحتوي على مخطط مرن في زمن التنفيذ وهذا لا يعني انه عليك استخدام UnTyped DataSet ولكنه يؤكد على أن UnTyped DataSet يمكن الاستعلام منها فقط باستخدام محدد الوصول Field<T>
الوصول إلى بيانات UnTyped DataSet
الوصول إلى البيانات في UnTyped DataSet يتطلب استخدام محددات الوصول Field<T> و SetField<T> للوصول إلى قيم الحقول وتغييرها ويعتبر محددي الوصول هذين هامين بسبب أن القيمة Null في قاعدة البيانات تمثل بالطريقة IsNull تعيد القيمة True وعليك التحقق من هذا الشرط في كل مرة تحاول فيها الوصول إلى عمود من أجل تجنب أخطاء التحويل واستخدام محددات الوصول هذه مسموح في أي DataRow أو DataTable حتى خارج تعبير الاستعلام كما هو ظاهر من الكود
foreach( DataRow r in orderDetails.Rows ) {
if (r.Field<decimal>( "UnitPrice" ) < 10 ){
r.SetField<decimal>( "UnitPrice", 10 );
}
}مترجم بتصرف