Отчеты об изменениях в пользовательских классах данных (платформа Entity Framework)
Службы объектов предоставляют интерфейс IEntityChangeTracker, который используется классами данных для отчета об изменениях свойств данных. EntityObject реализует метод SetChangeTracker для IEntityWithChangeTracker. Этот метод вызывается службами объектов для указания экземпляра IEntityChangeTracker, который объект использует для отчета об изменениях. Модель отчета об изменениях, поддерживаемая IEntityChangeTracker, предполагает отчет об отложенном изменении свойства, задание свойства и последующий отчет о завершении изменения.
Для отчета об изменениях в пользовательских классах данных, не наследуемых от EntityObject, эти классы должны реализовать интерфейс IEntityWithChangeTracker. Дополнительные сведения см. в разделе Реализация интерфейсов пользовательских классов данных (платформа Entity Framework).
При отчете об изменениях следует принимать во внимание следующие соображения.
Отчет об изменении свойства следует предоставлять как перед, так и после задания значения этого свойства.
Необходимо создавать отчет об изменении свойства EntityKey. После задания значения свойства EntityKey код приложения, создающий отчет об изменении этого свойства, вызывает исключение InvalidOperationException. Но в некоторых случаях службы объектов должны иметь возможность изменять свойство EntityKey после его установки. Создавая отчет об изменениях этого свойства, службы объектов могут определить, когда следует задавать это свойство.
Можно создавать отчет об изменении свойства без последующего создания отчета об изменении его значения. Однако в этом случае изменение не будет отслеживаться.
Исключение InvalidOperationException вызывается, если создается отчет о завершении изменения свойства до того, как предоставляется отчет об изменении этого свойства или передается недопустимое имя свойства. Это происходит, если создается отчет об изменении нескольких свойств без последующего создания отчета о завершении изменения. Это объясняется тем, что распознается изменение только последнего свойства, если измененное свойство сравнивается со свойством, отчет об изменении которого был предоставлен первоначально.
Отчет об изменениях свойств при наследовании от базовых классов EntityObject и ComplexObject
Если пользовательский класс данных наследуется от EntityObject или ComplexObject, для отчета об изменении свойств необходимо вызвать методы ReportPropertyChanging и ReportPropertyChanged.
Отчет об изменении свойств при наследовании от класса EntityObject
Вызовите метод System.Data.Objects.DataClasses.EntityObject.ReportPropertyChanging(System.String) для EntityObject, передав имя изменяемого свойства.
В результате текущее значение свойства, которое используется в качестве исходного, будет помещено в кэш.
Установите нужное значение свойства.
Вызовите метод System.Data.Objects.DataClasses.EntityObject.ReportPropertyChanged(System.String) для EntityObject, передав имя измененного свойства.
В результате службы объектов получают уведомление о том, что выполнено отложенное изменение свойства. Затем службы объектов помечают свойство как измененное.
Отчет об изменении свойств при наследовании от класса ComplexObject
Вызовите метод System.Data.Objects.DataClasses.ComplexObject.ReportPropertyChanging(System.String) для ComplexObject, передав имя изменяемого свойства. В результате текущее значение свойства, которое используется в качестве исходного, будет помещено в кэш.
Установите нужное значение свойства.
Вызовите метод System.Data.Objects.DataClasses.ComplexObject.ReportPropertyChanged(System.String) для ComplexObject, передав имя измененного свойства. В результате службы объектов получат уведомление о том, что выполнено отложенное изменение свойства. Затем службы объектов помечают свойство как измененное.
В следующем примере показано, как передавать уведомления об изменениях, если задается скалярное свойство Status для объекта Order:
<EdmScalarPropertyAttribute(IsNullable:=False)> _
Public Property Status() As Byte
Get
Return _status
End Get
Set(ByVal value As Byte)
If _status <> value Then
ReportPropertyChanging("Status")
_status = value
ReportPropertyChanged("Status")
End If
End Set
End Property
[EdmScalarPropertyAttribute(IsNullable = false)]
public byte Status
{
get
{
return _status;
}
set
{
if (_status != value)
{
ReportPropertyChanging("Status");
_status = value;
ReportPropertyChanged("Status");
}
}
}
Отчет об изменении свойств при реализации интерфейса IEntityWithChangeTracker
Если пользовательский класс данных реализует интерфейс IEntityWithChangeTracker, для правильного создания отчета об изменении в методе IEntityChangeTracker необходимо вызвать методы отчета об изменениях до и после изменения свойства.
Отчет об изменении свойств при реализации интерфейса IEntityWithChangeTracker
Вызовите метод EntityMemberChanging, передав имя изменяемого свойства. В результате текущее значение свойства, которое используется в качестве исходного, будет помещено в кэш.
Установите нужное значение свойства.
Вызовите метод EntityMemberChanged, передав имя измененного свойства.
В результате службы объектов получат уведомление о том, что выполнено отложенное изменение свойства. Затем службы объектов помечают свойство как измененное.
Отчет об изменении свойств при реализации интерфейса IEntityWithChangeTracker для сложного типа
Вызовите метод EntityComplexMemberChanging, передав имя измененной сущности верхнего уровня, экземпляр сложного объекта, содержащий измененное свойство, и имя свойства, изменившегося в сложном типе. В результате текущее значение свойства, которое используется в качестве исходного, будет помещено в кэш.
Установите нужное значение свойства.
Вызовите метод EntityComplexMemberChanged, передав имя измененной сущности верхнего уровня, экземпляр сложного объекта, содержащий измененное свойство, и имя свойства, изменившегося в сложном типе. В результате службы объектов получат уведомление о том, что выполнено отложенное изменение свойства. Затем службы объектов помечают свойство как измененное.
В некоторых ситуациях экземпляр IEntityChangeTracker может оказаться недоступным. Это происходит, если объект отсоединен от своего контекста или запрос выполняется с параметром NoTracking. Перед вызовом методов отчета об изменениях необходимо проверить наличие экземпляра IEntityChangeTracker.
В следующем примере показан абстрактный класс ComplexTypeChangeTracker, который является базовым для всех производных сложных типов. В этом классе реализовано отслеживание изменений для сложных типов.
' Base class for complex types that implements change tracking.
Public MustInherit Class ComplexTypeChangeTracker
Protected _complexChangeTracker As IEntityChangeTracker = Nothing
Private _rootComplexPropertyName As String
' Gets an IEntityChangeTracker to call for properties change.
' You must do this in order to track changes.
Public Overridable Sub SetComplexChangeTracker( _
ByVal rootComplexPropertyName As String, _
ByVal complexChangeTracker As IEntityChangeTracker)
_rootComplexPropertyName = rootComplexPropertyName
_complexChangeTracker = complexChangeTracker
End Sub
' Protected method that is called before the change for change tracking
' each of the scalar properties in the complex type.
Protected Sub ReportMemberChanging(ByVal scalarPropertyName As String)
If Not _complexChangeTracker Is Nothing Then
_complexChangeTracker.EntityComplexMemberChanging( _
_rootComplexPropertyName, Me, scalarPropertyName)
End If
End Sub
' Protected method that is called after the change for change tracking
' each of the scalar properties in the complex type.
Protected Sub ReportMemberChanged(ByVal scalarPropertyName As String)
If Not _complexChangeTracker Is Nothing Then
_complexChangeTracker.EntityComplexMemberChanged( _
_rootComplexPropertyName, Me, scalarPropertyName)
End If
End Sub
End Class
// Base class for complex types that implements change tracking.
public abstract class ComplexTypeChangeTracker
{
protected IEntityChangeTracker _complexChangeTracker = null;
private string _rootComplexPropertyName;
// Gets an IEntityChangeTracker to call for properties change.
// You must do this in order to track changes.
virtual public void SetComplexChangeTracker(string rootComplexPropertyName, IEntityChangeTracker complexChangeTracker)
{
_rootComplexPropertyName = rootComplexPropertyName;
_complexChangeTracker = complexChangeTracker;
}
// Protected method that is called before the change for change tracking
// each of the scalar properties in the complex type.
protected void ReportMemberChanging(string scalarPropertyName)
{
if (null != _complexChangeTracker)
{
_complexChangeTracker.EntityComplexMemberChanging(_rootComplexPropertyName,
this, scalarPropertyName);
}
}
// Protected method that is called after the change for change tracking
// each of the scalar properties in the complex type.
protected void ReportMemberChanged(string scalarPropertyName)
{
if (null != _complexChangeTracker)
{
_complexChangeTracker.EntityComplexMemberChanged(_rootComplexPropertyName,
this, scalarPropertyName);
}
}
}
В следующем примере показано, как использовать методы из предыдущего примера для уведомления об изменениях при установке скалярного свойства Status объекта Order.
<EdmScalarPropertyAttribute()> _
Public Property Comment() As String
Get
Return _comment
End Get
Set(ByVal value As String)
' Validate the value before setting it.
If (value <> Nothing) AndAlso value.Length > 128 Then
Throw New ApplicationException(String.Format( _
My.Resources.propertyNotValidString, _
"Comment", "128"))
End If
If _comment <> value Then
' Report the change if the change tracker exists.
If Not _complexChangeTracker Is Nothing Then
ReportMemberChanging("Comment")
_comment = value
ReportMemberChanged("Comment")
Else
_comment = value
End If
End If
End Set
End Property
[EdmScalarPropertyAttribute()]
public string Comment
{
get
{
return _comment;
}
set
{
// Validate the value before setting it.
if ((value != null) && value.Length > 128)
{
throw new ApplicationException(string.Format(
Properties.Resources.propertyNotValidString,
new string[3] { value, "Comment", "128" }));
}
if (_comment != value)
{
// Report the change if the change tracker exists.
if (_complexChangeTracker != null)
{
ReportMemberChanging("Comment");
_comment = value;
ReportMemberChanged("Comment");
}
else
{
_comment = value;
}
}
}
}