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)1つではなく常に2つのオブジェクトを渡すことは、アプリケーション全体にとって「醜い」パターンです。

  • メソッドには、ビジネス オブジェクトごとに次のパラメータが必要です。
  • コレクションは「List」から「List>」に変更する必要があります。

どちらの点も維持するのがより難しくなります - 開発者は、具体的なオブジェクトが別の DataContext に接続されている (または接続されていない) ことを DataContext が認識しているにもかかわらず、DataContext の正しいインスタンスを毎回設定する必要があります (バグを作成しやすい)。

2)私は(アプリケーションの現在のバージョンがそれを使用して)異なる「場所」から来たオブジェクトのコレクションで「任意の」ビジネスロジックを処理したい(たとえば、ドラッグアンドドロップによるフローティングウィンドウ)。

現在、カスタムの型指定された DataSet を使用しているため、変更に関する情報はデータ行 (DataRow = ビジネス オブジェクト) にあり、それを取得したり、クローンを作成してデータベースに保存したりすることは問題ありませんでした。

4

3 に答える 3

6

ケビン - 私はあなたの痛みを感じます... ビジネスオブジェクトの周りにビジネスロジックを構築しているとき、コードを配置する必要がある 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の楽しみの 1つは、オブジェクト自分を追跡している人物を確実に認識できないことです。オブジェクトにデータ認識/遅延読み込みのプロパティがある場合、リフレクションを介してコンテキストを追跡できる可能性がありますが、実際には混乱します。データコンテキストをそれを必要とするコードに単純に渡す方がはるかにクリーンです。

于 2008-11-09T21:55:37.523 に答える
2

最も簡単な方法は、DataContext をメソッドに渡すことです。

ただし、「1 つのメソッドには 1 つの目的しか持たない」という規則に従うように設計を変更することを検討することもできます。その場合、「変更」する同じメソッドで「保存」したくないでしょう。

于 2008-11-09T21:55:32.500 に答える