9

如果我有 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)我想要(当前版本的应用程序使用它)处理来自不同“地方”的对象集合的“任何”业务逻辑(例如,通过拖放操作浮动窗口)。

Currentyl 我们使用自定义类型的数据集,因此有关更改的信息位于数据行(DataRow = 业务对象)中,获取它没有问题,或者创建一个克隆然后将其保存到数据库中。

4

3 回答 3

6

Kevin - 我感到你的痛苦......当你围绕你的业务对象构建业务逻辑时,有时你只需要访问对象所属的 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);
}

请注意,如果对象当前附加到已打开 ChangeTracking 的上下文,则该对象只能找到它的 DataContext。此属性依赖于 DataContext 已订阅对象的 OnPropertyChanging 事件以监视对象生命周期内的更改这一事实。

如果这有帮助,请给这篇文章投票。

有关使用反射查找事件处理程序的更多信息:

于 2009-06-12T11:05:01.230 回答
3

POCO的部分乐趣在于你不能确定对象知道谁在跟踪他。如果对象具有数据感知/延迟加载属性,那么您也许可以通过反射跟踪上下文,但实际上这将是一团糟。将数据上下文简单地传递给需要它的代码会更干净。

于 2008-11-09T21:55:37.523 回答
2

最简单的做法是将 DataContext 传递到您的方法中。

但是,您也可以考虑更改您的设计,以便遵循“单一方法应该只有一个目的”的规则,在这种情况下,您不希望以与“修改”相同的方法“保存”。

于 2008-11-09T21:55:32.500 回答