Ottieni il DataContext dell'oggetto
-
07-07-2019 - |
Domanda
Se ho gli oggetti 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; }
}
}
e da qualche altra parte nell'applicazione:
public static void DoSomethingWithCustomer( Customer customer ) {
// some operations
// now, I want save changes to the database
}
come posso ottenere un'istanza di DataContext che tiene traccia delle modifiche del "cliente" obiettare?
Modifica: perché non voglio passare DataContext nel metodo.
1) Passare sempre 2 oggetti invece di 1 è "brutto" modello per l'intera applicazione.
- I metodi avranno bisogno del prossimo parametro per ogni oggetto business.
- La raccolta dovrà essere modificata da " Elenco " a " Elenca > " ;.
Entrambi i punti saranno più difficili da mantenere - lo sviluppatore deve sempre impostare l'istanza corretta di DataContext (facile da creare un bug), nonostante DataContext sappia che l'oggetto concreto è (o meno) collegato a un altro DataContext.
2) Voglio che (l'attuale versione dell'applicazione lo usi) elabori " qualsiasi " logica aziendale sulla raccolta di oggetti provenienti da diversi "luoghi" (finestre mobili trascinando & amp; rilascia ad esempio).
Currentyl usiamo DataSet personalizzati, quindi le informazioni sulle modifiche si trovano nelle righe di dati (DataRow = oggetto business) e non è stato un problema ottenerlo o creare un clone e quindi salvarlo nel database.
Soluzione
Kevin - Sento il tuo dolore ... quando stai costruendo la logica di business attorno ai tuoi oggetti di business, ci sono momenti in cui hai solo per avere accesso al DataContext a cui appartiene un oggetto, dal momento che non conoscere i menu di DataContext che devono inserire il codice in luoghi che riducono la manutenibilità del codice.
Ho scritto il seguente codice (VB, temo), che presenta una proprietà Context che può essere posizionata su un oggetto dati, e quindi usato per restituire il DataContext (se presente) a cui è collegato l'oggetto.
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
Ecco una versione 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);
}
Prestare attenzione a notare che l'oggetto può individuare DataContext solo se è attualmente collegato al contesto con ChangeTracking attivato. Questa proprietà si basa sul fatto che DataContext ha sottoscritto l'evento OnPropertyChanging dell'oggetto per monitorare le modifiche durante la durata dell'oggetto.
Se questo è stato utile, per favore vota questo post.
Per ulteriori informazioni sull'uso di reflection per trovare i gestori di eventi: http: //weblogs.asp. net / avnerk / archive / 2007/03/29 / riflettendo-over-an-event.aspx http://www.bobpowell.net/eventsubscribers.htm
Altri suggerimenti
Parte del divertimento di POCO è che non puoi essere sicuro che l'oggetto conosce chi lo sta seguendo. Se l'oggetto ha proprietà sensibili ai dati / caricamento lento, allora potresti essere in grado di tracciare il contesto tramite la riflessione, ma in realtà questo sarebbe un casino. Sarebbe molto più semplice trasferire semplicemente il contesto dei dati al codice che ne ha bisogno.
La cosa più semplice da fare è passare DataContext nel tuo metodo.
Tuttavia, puoi anche considerare di cambiare il tuo design in modo da seguire la regola che "un singolo metodo dovrebbe avere un solo scopo", nel qual caso non vorrai "salvare" nello stesso metodo in cui " Modifica " ;.