Получить DataContext объекта - PullRequest
       6

Получить DataContext объекта

9 голосов
/ 10 ноября 2008

Если у меня есть объекты 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, который отслеживает изменения объекта "customer"?

Редактировать: Почему я не хочу передавать DataContext в метод.

1) Передача всегда 2 объектов вместо 1 - это "уродливый" шаблон для всего приложения.

  • Для методов потребуется следующий параметр для каждого бизнес-объекта.
  • Собрание будет изменено с «Список» на «Список>».

Обе точки будут труднее поддерживать - разработчик должен каждый раз устанавливать правильный экземпляр DataContext (легко создать ошибку), несмотря на то, что DataContext знает, что конкретный объект (или нет) присоединен к другому DataContext.

2) Я хочу (текущая версия приложения использует это) обрабатывать «любую» бизнес-логику на коллекции объектов, которые пришли из разных «мест» (например, плавающие окна с помощью перетаскивания).

В настоящее время мы используем пользовательские типизированные наборы данных, поэтому информация об изменениях находится в строках данных (DataRow = бизнес-объект), и их было не сложно получить или создать клон, а затем сохранить его в базе данных.

Ответы [ 3 ]

6 голосов
/ 12 июня 2009

Кевин - я чувствую вашу боль ... когда вы строите бизнес-логику вокруг своих бизнес-объектов, бывают случаи, когда у вас просто есть для доступа к 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

Вот версия 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

3 голосов
/ 10 ноября 2008

Часть забавы POCO заключается в том, что вы не можете быть уверены, что объект знает , кто его отслеживает. Если объект обладает свойствами данных / отложенной загрузки, то вы могли бы иметь возможность отслеживать контекст с помощью отражения, но на самом деле это будет беспорядок. Было бы намного чище просто передать контекст данных в код, который нуждается в этом.

2 голосов
/ 10 ноября 2008

Самое простое, что нужно сделать, это передать DataContext в ваш метод.

Тем не менее, вы можете также рассмотреть возможность изменения дизайна, чтобы вы следовали правилу, согласно которому «один метод должен иметь только одну цель», и в этом случае вы не захотите «Сохранить» в том же методе, который вы «изменили». ».

...