Obter DataContext do objeto
-
07-07-2019 - |
Pergunta
Se eu tiver os objetos 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 em outro lugar no aplicativo:
public static void DoSomethingWithCustomer( Customer customer ) {
// some operations
// now, I want save changes to the database
}
Como posso obter instância de DataContext que controla as alterações do objeto "cliente"?
Editar: Por que eu não quero passar o DataContext no método
.1) Passando sempre 2 objetos em vez de 1 é o padrão de "feio" para a aplicação inteira.
- Métodos precisará próximo parâmetro para cada objeto de negócios.
- Coleção irá necessidades alterada de "Lista" para "Lista>".
Ambos os pontos serão mais difíceis de manter - desenvolvedor deve cada vez define a instância correta do DataContext (fácil de criar um bug), apesar das DataContext saber que o objeto concreto é (ou não) anexado a outro DataContext
2) Eu quero (versão atual do uso do aplicativo lo) processo de "qualquer" lógica de negócios na coleção de objetos que vieram de diferentes "lugares" (janelas flutuantes por drag & drop, por exemplo).
Currentyl usamos costume digitado DataSets, para que informações sobre alterações estão nas linhas de dados (DataRow = objeto de negócios) e não foi problema para obtê-lo, ou criar um clone e depois salvá-lo no banco de dados.
Solução
Kevin - eu sinto sua dor ... quando você está construindo a lógica de negócios em torno de seus objetos de negócios, há momentos em que você simplesmente Have para ter acesso ao DataContext ao qual pertence um objeto, uma vez não conhecendo as menas DataContext ter de colocar seu código em lugares que reduzem a capacidade de manutenção do seu código.
Eu escrevi o seguinte código (VB, eu tenho medo), que apresenta uma propriedade de contexto que pode ser colocado em um objeto de dados e, em seguida, usado para retornar o DataContext (se houver) que o objeto está anexado.
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
Aqui está uma versão 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);
}
Tome o cuidado de observar que o objeto só pode localizá-lo de DataContext se ele estiver ligado ao contexto com ChangeTracking ligado. Esta propriedade se baseia no fato de que o DataContext subscreveu evento OnPropertyChanging do objeto para a monitorizar as alterações ao longo do tempo de vida do objeto.
Se isso foi útil, por favor, up-voto este post.
Para obter mais informações sobre como usar a reflexão para encontrar manipuladores de eventos: http: //weblogs.asp. net / avnerk / Arquivo / 2007/03/29 / refletindo-over-an-event.aspx http://www.bobpowell.net/eventsubscribers.htm
Outras dicas
Parte da diversão de POCO é que você não pode ter certeza que o objeto sabe , que é o acompanhamento dele. Se o objeto tem propriedades / lazy-carregamento de dados de reconhecimento, então você pode ser capaz de traçar o contexto através da reflexão, mas, na realidade, isso seria uma bagunça. Seria muito mais limpo para simplesmente passar o contexto de dados para o código que precisa.
A coisa mais simples a fazer é passar o DataContext em seu método.
No entanto, você também pode considerar mudar seu projeto de modo que você siga a regra de que "um único método deve ter apenas um propósito", caso em que você não gostaria de "Save" no mesmo método que você "Modificar ".