Получить 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) Всегда передавать 2 объекта вместо 1 — это «уродливый» шаблон для всего приложения.
- Методам потребуется следующий параметр для каждого бизнес-объекта.
- Коллекция должна быть изменена со «Списка» на «Список>».
Оба пункта будет сложнее поддерживать - разработчик должен каждый раз устанавливать правильный экземпляр DataContext (легко создать ошибку), несмотря на то, что DataContext знает, что конкретный объект прикреплен (или нет) к другому DataContext.
2) Я хочу (текущая версия приложения использует ее) обрабатывать «любую» бизнес-логику для сбора объектов, пришедших из разных «мест» (например, плавающие окна путем перетаскивания).
В настоящее время мы используем наборы данных с пользовательским типом, поэтому информация об изменениях находится в строках данных (DataRow = бизнес-объект), и не было проблем с ее получением или созданием клона, а затем сохранением его в базе данных.
Решение
Кевин – Я чувствую твою боль...когда вы строите бизнес-логику вокруг своих бизнес-объектов, бывают случаи, когда вы просто иметь иметь доступ к DataContext, которому принадлежит объект, поскольку незнание меню DataContext означает необходимость размещения вашего кода в местах, которые снижают удобство сопровождения вашего кода.
Я написал следующий код (боюсь, VB), который представляет свойство Context, которое можно поместить в объект данных, а затем использовать для возврата 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
Вот версия С#:
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 в ваш метод.
Однако вы также можете рассмотреть возможность изменения своего дизайна, чтобы следовать правилу, согласно которому «один метод должен иметь только одну цель», и в этом случае вы не захотите «Сохранить» в том же методе, который вы «Изменяете».