This topic describes the interfaces DevForce AOP injects into your entity classes when it rewrites them.
When you build your model project, DevForce rewrites your AOP Entity and “Complex Type” classes. It replaces your property implementations and injects infrastructure code that readies your classes for participation in common .NET client technologies such as Windows Forms, WPF, Silverlight and Windows Phone.
All of the technologies mentioned encourage data binding of UI controls to model data sources and these controls respond best when the data-bound objects implement the pertinent .NET interfaces.
DevForce AOP injects implementations of most of these interfaces as well as a few of its own. This topic describes these interfaces and how to use them.
Entity AOP classes are identified by the DevForce ProvideEntityAspect attribute adorning either the class or one of its base classes.
.NET interfaces:
System.ComponentModel.INotifyPropertyChanged - Notifies clients that a property value has changed.
System.ComponentModel.IEditableObject - Functionality to commit or rollback changes to an object that is used as a data source.
System.ComponentModel.IDataErrorInfo - Implement to provide custom error information that a user interface can bind to.
System.ComponentModel.INotifyDataErrorInfo - Implement to provide custom synchronous and asynchronous validation support.
DevForce interfaces:
IdeaBlade.EntityModel.IEntity - Get the entity's EntityAspect.
IdeaBlade.EntityModel.IEntitySvcs - Set the entity's EntityAspect property [DevForce internal use]
IdeaBlade.Validate.INotifyVerifierResults - Receive notification of DevForce validation (aka "verification") results.
“Complex Type” AOP classes are identified by the DevForce ProvideComplexObject attribute adorning either the class or one of its base classes.
.NET interfaces:
System.ComponentModel.IDataErrorInfo - Implement to provide custom error information that a user interface can bind to.
System.ComponentModel.INotifyDataErrorInfo - Implement to provide custom synchronous and asynchronous validation support.
DevForce interfaces
IdeaBlade.EntityModel.IComplexObject - Get the object's ComplexAspect, the complex type's equivalent to EntityAspect.
IdeaBlade.EntityModel.IComplexObjectSvcs - Set the ComplexAspect property [DevForce internal use]
IdeaBlade.Validate.INotifyVerifierResults - Receive notification of DevForce validation (aka "verification") results.
You may have noticed that “Complex Type” classes implement fewer .NET interfaces than do the entity classes. INotifyPropertyChanged is conspicuously absent.
Instances of “Complex Types” are almost always attached to a parent entity (accessible via the ParentEntity property). They typically delegate functionality to their parent entities. For example, to listen for a change to the Street property of an Address complex type, you would attach a handler to the parent Person entity’s EntityAspect.EntityPropertyChanged event.
The balance of this topic concentrates on entity interfaces, leaving you to infer the corresponding usage for “Complex Types”.
Consider this bare-bones AOP Entity
C# | [ProvideEntityAspect] public class Category { public int CategoryId { get; set; } public string CategoryName { get; set; } } |
You can’t tell by reading this class that, at runtime, it will support INotifyPropertyChanged, IEditableObject, IDataErrorInfo and INotifyDataErrorInfo. That’s usually unimportant because you generally don’t program with these interfaces. What matters is that UI controls detect them and respond appropriately.
But you can access these interface members by casting the entity appropriately.
For example, the following line returns the all-important EntityAspect.
C# | ((IEntity) myEntity).EntityAspect; |
You can discover whether an entity has validation errors for a particular property with this line:
C# | ((INotifyDataErrorInfo) myEntity).GetErrors(somePropertyName); |
You can attach a handler to the INotifyPropertyChanged.PropertyChanged event as follows:
C# | ((INotifyPropertyChanged) myEntity).PropertyChanged += myHandler; |
You could raise PropertyChanged in one of your custom, non-persisted properties by writing the following method and calling it within the property’s setter:
C# | protected void RaisePropertyChanged(string propertyName) { var eventArgs = new PropertyChangedEventArgs(propertyName); ((IEntity) this).EntityAspect.ForcePropertyChanged(eventArgs); } |
RaisePropertyChanged might be a welcome addition to your custom base entity class.
The DevForce implementations of these interfaces are sufficient for most applications. But you can inject your own interface logic and completely replace the DevForce behaviors if you wish by implementing certain DevForce injection interfaces.
All of injection interfaces are in the IdeaBlade.EntityModel namespace.
Implement the IPropertyChangedInterceptor to participate in change notifications
For example, you could filter change notifications in the Category class as follows:
C# | [ProvideEntityAspect] public class Category : IPropertyChangedInterceptor { // ... void IPropertyChangedInterceptor.OnPropertyChanged(PropertyChangedEventArgs e) { // Don't raise PropertyChanged if the property name starts with 'A' if (e.PropertyName.StartsWith("A")) return; // Invoke the base implementation by delegation to EntityAspect ((IEntity) this).EntityAspect.OnEntityAspectPropertyChanged(e.PropertyName); } } |
The following interceptors follow the same pattern. Remember to call the appropriate method of the EntityAspect – as shown above – unless you intend to by-pass the DevForce implementation.
Implement IPropertyValidationInterceptor to participate in DevForce property validation.
C# | bool ValidatePropertyBeforeSet( IdeaBlade.EntityModel.EntityProperty property, object newValue) // Delegate to the DevForce default implementation, EntityAspect.ValidatePropertyBeforeSet bool ValidatePropertyAfterSet( IdeaBlade.EntityModel.EntityProperty property, object newValue) //Delegate to the DevForce default implementation, EntityAspect.ValidatePropertyAfterSet |
Implement IEditableObjectInterceptor to participate in or override the way DevForce handles the members of the IEditableObject interface.
C# | void BeginEditCore(); // Delegate to the DevForce default implementation, EntityAspect.BeginEditCore() void EndEditCore(); // Delegate to the DevForce default implementation, EntityAspect.EndEditCore() void CancelEditCore(); // Delegate to the DevForce default implementation, EntityAspect.CancelEditCore() |
DevForce defines a "null entity" (aka "nullo") for each entity type. You most frequently encounter the "nullo" when a reference navigation property (e.g., Product.Category) can't find the parent entity; it's safer and more useful to return the "nullo" than return null and have to deal with a NullReferenceException.
The "nullo" has all of the members of its entity type. It's properties return the default values for their types: int properties return 0, boolean properties return false, object properties return null, and reference navigation properties return other "nullos".
You can define alternatives to these defaults by implementing this IUpdateNullEntity interface.
C# | void UpdateNullEntity(); // There is no DevForce default implementation for the optional method that specifies the NullEntity. |