Implementing Custom Data Class Interfaces (Entity Framework)

.NET Framework 3.5

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.

IEntityWithChangeTracker

Required for change tracking. Enables Object Services to track changes to the object.

Object Services provides objects with the IEntityChangeTracker interface to enable objects to report changes. IEntityWithChangeTracker defines the SetChangeTracker method. This method specifies the IEntityChangeTracker used to report changes. For more information, see Reporting Changes in Custom Data Classes (Entity Framework).

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.

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.

NoteNote

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 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")]
    public partial class OrderInfo : ComplexTypeChangeTracker
    {
    
    
  5. In the complex type class, override the SetComplexChangeTracker method.

    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 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 (_extendedInfo != null)
    {
        _extendedInfo.SetComplexChangeTracker("ExtendedInfo", _changeTracker);
    }
    
    

See Also

Community Additions

Show: