Implementing Custom Data Class Interfaces (Entity Framework)

The recommended way to use custom data classes with an Entity Data Model (EDM) is to inherit from EntityObject and ComplexObject. For cases when you cannot inherit from EntityObject and ComplexObject or when you need a higher degree of independence from the framework, the Entity Framework provides a set of custom data class interfaces. If you do not inherit from EntityObject, you must implement these interfaces to use custom data classes with an EDM. The specific interfaces that you implement depend on the requirements of your custom data classes and your application.

  • IEntityWithKey
    Optional. Exposes the entity key to Object Services for improved performance.

    IEntityWithKey defines the EntityKey property. Object Services uses the EntityKey property to manage objects in the object context.

    If you choose not to implement IEntityWithKey, you will see decreased performance and increased memory usage when loading related objects, attaching objects to an object context, or any operation that requires a key.

  • IEntityWithRelationships
    Required for entities with associations. Enables Object Services to manage relationships between objects.

    IEntityWithRelationships defines the RelationshipManager property. Object Services uses the RelationshipManager property to access the RelationshipManager used to manage relationships to other objects.

For more information, see How to: Implement Custom Data Class Interfaces (Entity Framework).

Like custom data classes that inherit from EntityObject, classes that implement these interfaces must meet the following requirements:

  • There must be an object for each entity type that is defined in the conceptual schema definition language (CSDL) file.

  • The namespace, classes, and data properties must have the appropriate EDM attributes applied.

  • The names of the namespace, classes, and data properties that have EDM attributes applied must match the names in the corresponding CSDL file.

For more information, see Customizing Objects (Entity Framework).

The following example shows the code that is required to implement these interfaces for an Order object, where Order is based on the SalesOrderHeader table.

Dim _changeTracker As IEntityChangeTracker = Nothing

' Specify the IEntityChangeTracker to use for tracking changes.
Private Sub SetChangeTracker(ByVal changeTracker As IEntityChangeTracker) _
    Implements IEntityWithChangeTracker.SetChangeTracker
    _changeTracker = changeTracker

    ' Every time the change tracker is set, we must also set all the 
    ' complex type change trackers.
    If Not _extendedInfo Is Nothing Then
        _extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker)
    End If
End Sub

Dim _entityKey As EntityKey = Nothing

' Define the EntityKey property for the class.
Property EntityKey() As EntityKey Implements IEntityWithKey.EntityKey
    Get
        Return _entityKey
    End Get
    Set(ByVal value As EntityKey)
        ' Set the EntityKey property.
        ' Report the change if the change tracker exists.
        If Not _changeTracker Is Nothing Then
            _changeTracker.EntityMemberChanging(StructuralObject.EntityKeyPropertyName)
            _entityKey = value
            _changeTracker.EntityMemberChanged(StructuralObject.EntityKeyPropertyName)
        Else
            _entityKey = value
        End If
    End Set
End Property

Dim _relationships As RelationshipManager = Nothing

' Define a relationship manager for the class.
ReadOnly Property RelationshipManager() As RelationshipManager _
Implements IEntityWithRelationships.RelationshipManager
    Get
        If _relationships Is Nothing Then
            _relationships = RelationshipManager.Create(Me)
        End If
        Return _relationships
    End Get
End Property
IEntityChangeTracker _changeTracker = null;

// Specify the IEntityChangeTracker to use for tracking changes.
void IEntityWithChangeTracker.SetChangeTracker(IEntityChangeTracker changeTracker)
{
    _changeTracker = changeTracker;

    // Every time the change tracker is set, we must also set all the 
    // complex type change trackers.
    if (_extendedInfo != null)
    {
        _extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker);
    }
}

EntityKey _entityKey = null;

// Define the EntityKey property for the class.
EntityKey IEntityWithKey.EntityKey
{
    get 
    { 
        return _entityKey; 
    }
    set
    {
        // Set the EntityKey property.
        // Report the change if the change tracker exists.
        if (_changeTracker != null)
        {
            _changeTracker.EntityMemberChanging(StructuralObject.EntityKeyPropertyName);
            _entityKey = value;
            _changeTracker.EntityMemberChanged(StructuralObject.EntityKeyPropertyName);
        }
        else
        {
            _entityKey = value;
        }
    }
}

RelationshipManager _relationships = null;

// Define a relationship manager for the class.
RelationshipManager IEntityWithRelationships.RelationshipManager
{
    get
    {
        if (null == _relationships)
            _relationships = RelationshipManager.Create(this);
        return _relationships;
    }
}

Complex Types

Complex types are non-scalar properties of entity types that enable scalar properties to be organized within entities. For more information, see Complex Type (EDM). Tracking changes in complex type objects requires you to write custom change tracking code. Therefore, we recommend inheriting from EntityObject and ComplexObject when possible. There are no custom data class interfaces to implement for complex type objects. However, you can use the following procedure to implement change tracking with complex type objects that do not inherit from ComplexObject.

Note

If you choose to implement custom data class interfaces for objects but also to inherit from ComplexObject, you must still implement custom change tracking as described in the following procedure.

To implement change tracking for complex type objects

  1. Implement custom data interfaces for entity types. For more information, see How to: Implement Custom Data Class Interfaces (Entity Framework).

  2. Ensure that complex types are correctly defined in the conceptual and mapping sections of the EDM. For more information, see Complex Type (EDM).

  3. Define an abstract base class called 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);
            }
        }
    }
    
  4. Define the complex type class, which inherits from ComplexTypeChangeTracker, and apply the EdmComplexTypeAttribute.

    <EdmComplexTypeAttribute(NamespaceName:="Microsoft.Samples.Entity", Name:="OrderInfo")> _
    Partial Public Class OrderInfo
        Inherits ComplexTypeChangeTracker
    
    [EdmComplexTypeAttribute(NamespaceName = "Microsoft.Samples.Entity", Name = "OrderInfo")]
    public partial class OrderInfo : ComplexTypeChangeTracker
    {
    
  5. In the complex type class, override the SetComplexChangeTracker method.

    Public Overrides Sub SetComplexChangeTracker(ByVal rootComplexPropertyName As String, _
        ByVal changeTracker As IEntityChangeTracker)
    
        ' Call SetChangeTracker on the base class to set the change tracker 
        ' and the name of the root complex type property on the entity.
        MyBase.SetComplexChangeTracker(rootComplexPropertyName, changeTracker)
    End Sub
    
    override public void SetComplexChangeTracker(string rootComplexPropertyName, IEntityChangeTracker changeTracker)
    {
        // Call SetChangeTracker on the base class to set the change tracker 
        // and the name of the root complex type property on the entity.
        base.SetComplexChangeTracker(rootComplexPropertyName, changeTracker);
    }
    
  6. Implement standard change tracking on the scalar properties of the complex type. For more information, see Reporting Changes in Custom Data Classes (Entity Framework).

  7. Apply the EdmComplexPropertyAttribute to the complex property in the entity type, and add a call to SetComplexChangeTracker to reset the change tracker when the complex property changes.

    <EdmComplexPropertyAttribute()> _
            Public Property ExtendedInfo() As OrderInfo
        Get
            Return _extendedInfo
        End Get
        Set(ByVal value As OrderInfo)
    
            ' For a complex type any changes in the complex type 
            ' properties all get tracked together.
            ' The change tracker may be Nothing during object materialization.
            If Not _changeTracker Is Nothing Then
    
                ' Since this is a complex property, we need to reset the change 
                ' tracker on the complex type. 
                If Not _extendedInfo Is Nothing Then
                    ' Reset the change tracker.
                    _extendedInfo.SetComplexChangeTracker("ExtendedInfo", Nothing)
                End If
    
                ' Report the change.
                _changeTracker.EntityMemberChanging("ExtendedInfo")
                _extendedInfo = value
                _changeTracker.EntityMemberChanging("ExtendedInfo")
    
            Else
                _extendedInfo = value
            End If
    
            ' Rest the change tracker. Complex type property cannot be Nothing.
            If Not _extendedInfo Is Nothing Then
                _extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker)
            End If
        End Set
    End Property
    
    [EdmComplexPropertyAttribute()]
    public OrderInfo ExtendedInfo
    {
        get
        {
            return _extendedInfo;
        }
        set
        {
            // For a complex type any changes in the complex type 
            // properties all get tracked together.
            // The change tracker may be null during object materialization.
            if (_changeTracker != null)
            {
                // Since this is a complex property, we need to reset the change 
                // tracker on the complex type. 
                if (_extendedInfo != null)
                {
                    // Reset the change tracker.
                    _extendedInfo.SetComplexChangeTracker("ExtendedInfo", null);
                }
    
                // Report the change.
                _changeTracker.EntityMemberChanging("ExtendedInfo");
                _extendedInfo = value;
                _changeTracker.EntityMemberChanged("ExtendedInfo");
            }
            else
            {
                _extendedInfo = value;
            }
    
            // Reset the change tracker. Complex type property cannot be null.
            if (_extendedInfo != null)
            {
                _extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker);
            }
        }
    }
    
  8. Repeat steps 4-7 for each complex property.

  9. In the System.Data.Objects.DataClasses.IEntityWithChangeTracker.SetChangeTracker(System.Data.Objects.DataClasses.IEntityChangeTracker) implementation for the entity type, insert a call to SetComplexChangeTracker to set the change tracker. Do this once for each complex property in the type.

    ' Every time the change tracker is set, we must also set all the 
    ' complex type change trackers.
    If Not _extendedInfo Is Nothing Then
        _extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker)
    End If
    
    // Every time the change tracker is set, we must also set all the 
    // complex type change trackers.
    if (_extendedInfo != null)
    {
        _extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker);
    }
    

See Also

Reference

EDM Generator (EdmGen.exe)

Concepts

Object Services Overview (Entity Framework)