الحصول على DataContext للكائن
-
07-07-2019 - |
سؤال
إذا كان لدي كائنات LINQ:
public class SampleDataContext : DataContext {
public Table<Customer> Customers { get { return this.GetTable<Customer>(); } }
public SampleDataContext( string connectionString ) : base( connectionString ) { }
}
[Table( Name="dbo.tblCustomers" )]
public class Customer {
private Guid? customerID;
[Column( Storage="customerID", DbType="uniqueidentifier NOT NULL", IsPrimaryKey=true )]
public Guid? CustomerID {
get { return this.customerID; }
set { this.customerID = value; }
}
private string customerName;
[Column( Storage = "customerName", DbType = "nvarchar(255) NOT NULL" )]
public string CustomerName {
get { return this.customerName; }
set { this.customerName = value; }
}
}
وفي مكان آخر في التطبيق:
public static void DoSomethingWithCustomer( Customer customer ) {
// some operations
// now, I want save changes to the database
}
كيف يمكنني الحصول على مثيل DataContext الذي يتتبع تغييرات كائن "العميل"؟
يحرر:لماذا لا أريد تمرير DataContext إلى الطريقة.
1) يعد تمرير كائنين دائمًا بدلاً من كائن واحد نمطًا "قبيحًا" للتطبيق بأكمله.
- ستحتاج الأساليب إلى المعلمة التالية لكل كائن عمل.
- سوف تحتاج المجموعة إلى التغيير من "قائمة" إلى "قائمة>".
ستكون صيانة كلتا النقطتين أكثر صعوبة - يجب على المطور في كل مرة تعيين المثيل الصحيح لـ DataContext (من السهل إنشاء خطأ)، على الرغم من أن DataContext يعرف أن الكائن الملموس (أو لا) مرتبط بـ DataContext آخر.
2) أريد (الإصدار الحالي من التطبيق استخدامه) معالجة "أي" منطق عمل على مجموعة من الكائنات التي تأتي من "أماكن" مختلفة (النوافذ العائمة عن طريق السحب والإفلات على سبيل المثال).
نستخدم Currentyl مجموعات بيانات مكتوبة مخصصة، لذلك توجد معلومات حول التغييرات في صفوف البيانات (DataRow = كائن أعمال) ولم تكن هناك مشكلة في الحصول عليها، أو إنشاء نسخة منها ثم حفظها في قاعدة البيانات.
المحلول
كيفن: أشعر بألمك..عندما تقوم ببناء منطق أعمال حول كائنات عملك، هناك أوقات عندما تقوم ببساطة يملك للوصول إلى DataContext الذي ينتمي إليه الكائن، نظرًا لعدم معرفة قواعد DataContext، يتعين علينا وضع التعليمات البرمجية الخاصة بك في أماكن تقلل من إمكانية صيانة التعليمات البرمجية الخاصة بك.
لقد كتبت الكود التالي (VB، أخشى)، والذي يقدم خاصية سياق يمكن وضعها على كائن بيانات، ثم استخدامها لإرجاع DataContext (إن وجد) الذي يرتبط به الكائن.
Private Const StandardChangeTrackerName As String = "System.Data.Linq.ChangeTracker+StandardChangeTracker"
Private _context As DataClasses1DataContext
Public Property Context() As DataClasses1DataContext
Get
Dim hasContext As Boolean = False
Dim myType As Type = Me.GetType()
Dim propertyChangingField As FieldInfo = myType.GetField("PropertyChangingEvent", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim propertyChangingDelegate As PropertyChangingEventHandler = propertyChangingField.GetValue(Me)
Dim delegateType As Type = Nothing
For Each thisDelegate In propertyChangingDelegate.GetInvocationList()
delegateType = thisDelegate.Target.GetType()
If delegateType.FullName.Equals(StandardChangeTrackerName) Then
propertyChangingDelegate = thisDelegate
hasContext = True
Exit For
End If
Next
If hasContext Then
Dim targetField = propertyChangingDelegate.Target
Dim servicesField As FieldInfo = targetField.GetType().GetField("services", BindingFlags.NonPublic Or BindingFlags.Instance)
If servicesField IsNot Nothing Then
Dim servicesObject = servicesField.GetValue(targetField)
Dim contextField As FieldInfo = servicesObject.GetType.GetField("context", BindingFlags.NonPublic Or BindingFlags.Instance)
_context = contextField.GetValue(servicesObject)
End If
End If
Return _context
End Get
Set(ByVal value As DataClasses1DataContext)
_context = value
End Set
End Property
إليك نسخة C#:
public DataContext GetMyDataContext()
{
// Find the StandardChangeTracker listening to property changes on this object.
// If no StandardChangeTracker is listening, then this object is probably not
// attached to a data context.
var eventField = this.GetType().GetField("PropertyChangingEvent", BindingFlags.NonPublic | BindingFlags.Instance);
var eventDelegate = eventField.GetValue(this) as Delegate;
if (eventDelegate == null)
return null;
eventDelegate = eventDelegate.GetInvocationList().FirstOrDefault(
del => del.Target.GetType().FullName == "System.Data.Linq.ChangeTracker+StandardChangeTracker");
if (eventDelegate == null)
return null;
// Dig through the objects to get the underlying DataContext.
// If the following fails, then there was most likely an internal change
// to the LINQ-to-SQL framework classes.
var targetField = eventDelegate.Target;
var servicesField = targetField.GetType().GetField("services", BindingFlags.NonPublic | BindingFlags.Instance);
var servicesObject = servicesField.GetValue(targetField);
var contextField = servicesObject.GetType().GetField("context", BindingFlags.NonPublic | BindingFlags.Instance);
return (DataContext)contextField.GetValue(servicesObject);
}
احرص على ملاحظة أن الكائن يمكنه تحديد موقع DataContext الخاص به فقط إذا كان متصلاً حاليًا بالسياق مع تشغيل ChangeTracking.تعتمد هذه الخاصية على حقيقة أن DataContext قد اشترك في حدث OnPropertyChanging الخاص بالكائن لمراقبة التغييرات على مدى عمر الكائن.
إذا كان هذا مفيدًا، فيرجى التصويت لصالح هذه المشاركة.
لمزيد من المعلومات حول استخدام الانعكاس للعثور على معالجات الأحداث:http://weblogs.asp.net/avnerk/archive/2007/03/29/reflecting-over-an-event.aspx http://www.bobpowell.net/eventsubscribers.htm
نصائح أخرى
جزء من متعة بوكو هو أنه لا يمكنك التأكد من أن الكائن يعرف الذي يتتبعه.إذا كان الكائن يحتوي على خصائص علم البيانات/تحميل كسول، فأنت قد نكون قادرين على تتبع السياق من خلال التفكير، ولكن في الواقع سيكون هذا فوضى.سيكون من الأنظف بكثير تمرير سياق البيانات إلى الكود الذي يحتاج إليه.
إن أبسط ما يمكنك فعله هو تمرير DataContext إلى طريقتك.
ومع ذلك، قد تفكر أيضًا في تغيير تصميمك بحيث تتبع القاعدة التي تنص على أن "الطريقة الواحدة يجب أن يكون لها غرض واحد فقط"، وفي هذه الحالة لن ترغب في "الحفظ" بنفس الطريقة التي "تعديلها".