- Home
- LLBLGen Pro
- Architecture
Opinions on Manager Base Class
Joined: 29-Sep-2004
I'm in the middle of developing a base class for the adapter scenario, where all controller (manager) classes inherit from. So far, everthing seems to be working well. It's by no means complete, but it has the majority of the guts implemented. Any feedback and suggestions would be great!
Imports SD.LLBLGen.Pro.ORMSupportClasses
Namespace Sheakley.ORM
''' -----------------------------------------------------------------------------
''' Project : Sheakley.ORM
''' Class : Sheakley.ORM.ControllerBase
'''
''' -----------------------------------------------------------------------------
''' <summary>
''' Abstract class encapsulating common functionality for all controller manager classes
''' utilizing the LLBL Adapter Scenario.
''' </summary>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Public MustInherit Class ControllerBase
' private attributes
Private _dataAccessAdapter As IDataAccessAdapter
' factory method to force derived classes to create the appropriate data access adapter.
Protected MustOverride Function CreateDataAccessAdapter() As IDataAccessAdapter
' factory method to force derived classes to create the appropriate entity collection.
Protected MustOverride Function CreateEntityCollection() As IEntityCollection2
' information used to perform run-time type checking and entity generation.
Protected MustOverride ReadOnly Property ManagedEntityInfo() As EntityInfo
' default sort expression used in generic fetch method calls.
Protected MustOverride ReadOnly Property DefaultSortExpression() As ISortExpression
''' -----------------------------------------------------------------------------
''' <summary>
''' Ctor.
''' </summary>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Sub New()
_dataAccessAdapter = CreateDataAccessAdapter()
End Sub
''' -----------------------------------------------------------------------------
''' <summary>
''' Ctor.
''' </summary>
''' <param name="DataAccessAdapter">Existing data access adapter.</param>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Sub New(ByRef DataAccessAdapter As IDataAccessAdapter)
_dataAccessAdapter = DataAccessAdapter
End Sub
''' -----------------------------------------------------------------------------
''' <summary>
''' Returns interface reference for the current DataAccessAdapter.
''' </summary>
''' <value></value>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected ReadOnly Property DataAdapter() As IDataAccessAdapter
Get
Return _dataAccessAdapter
End Get
End Property
''' -----------------------------------------------------------------------------
''' <summary>
''' Checks whether or not the specified entity matches the derived class's expected type.
''' </summary>
''' <param name="entity">Entity to validate</param>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Private Sub CheckType(ByVal entity As IEntity2)
If Not CType(entity, Object).GetType Is ManagedEntityInfo.EntityType Then
Throw New ArgumentException(String.Format("Invalid entity. Entity must be of type {0}.", ManagedEntityInfo.EntityType.ToString()))
End If
End Sub
''' -----------------------------------------------------------------------------
''' <summary>
''' Checks whether or not the specified entity matches the derived class's expected type.
''' </summary>
''' <param name="collection">Collection holding the entity to validate</param>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Private Sub CheckType(ByVal collection As IEntityCollection2)
CheckType(collection.EntityFactoryToUse.Create())
End Sub
#Region " Persistence Methods"
''' -----------------------------------------------------------------------------
''' <summary>
''' Persists the entity to the persistent medium.
''' </summary>
''' <param name="entityToSave">Entity to persist</param>
''' <returns>True if save was successful, false otherwise</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Overloads Function SaveEntity(ByVal entityToSave As IEntity2) As Boolean
Return SaveEntity(entityToSave, False)
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Persists the entity to the persistent medium.
''' </summary>
''' <param name="entityToSave">Entity to persist</param>
''' <param name="refetchAfterSave">Refetch the data after save</param>
''' <returns>True if save was successful, false otherwise</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Overloads Function SaveEntity(ByVal entityToSave As IEntity2, ByVal refetchAfterSave As Boolean) As Boolean
Return SaveEntity(entityToSave, refetchAfterSave, Nothing)
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Persists the entity to the persistent medium.
''' </summary>
''' <param name="entityToSave">Entity to persist</param>
''' <param name="refetchAfterSave">Refetch the data after save</param>
''' <param name="updateRestriction">IPredicateExpression for filtering</param>
''' <returns>True if save was successful, false otherwise</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overloads Function SaveEntity(ByVal entityToSave As IEntity2, ByVal refetchAfterSave As Boolean, ByVal updateRestriction As IPredicateExpression) As Boolean
Return SaveEntity(entityToSave, refetchAfterSave, updateRestriction, False)
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Persists the entity to the persistent medium.
''' </summary>
''' <param name="entityToSave">Entity to persist</param>
''' <param name="refetchAfterSave">Refetch the data after save</param>
''' <param name="updateRestriction">IPredicateExpression for filtering</param>
''' <param name="recurse">Recursively save the object graph</param>
''' <returns>True if save was successful, false otherwise</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overridable Overloads Function SaveEntity(ByVal entityToSave As IEntity2, ByVal refetchAfterSave As Boolean, ByVal updateRestriction As IPredicateExpression, ByVal recurse As Boolean) As Boolean
' perform safe type checking.
CheckType(entityToSave)
' persist the entity to the data source.
Return _dataAccessAdapter.SaveEntity(entityToSave, refetchAfterSave, updateRestriction, recurse)
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Persists the entity collection to the persistent medium.
''' </summary>
''' <param name="collectionToSave">Entity collection to persist</param>
''' <returns>Number of records affected</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overloads Function SaveEntityCollection(ByVal collectionToSave As IEntityCollection2) As Integer
Return SaveEntityCollection(collectionToSave, False, False)
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Persists the entity collection to the persistent medium.
''' </summary>
''' <param name="collectionToSave">Entity collection to persist</param>
''' <param name="refetchSavedEntitesAfterSave">Refetch the data after save</param>
''' <returns>Number of records affected</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overloads Function SaveEntityCollection(ByVal collectionToSave As IEntityCollection2, ByVal refetchSavedEntitesAfterSave As Boolean) As Integer
Return SaveEntityCollection(collectionToSave, refetchSavedEntitesAfterSave, False)
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Persists the entity collection to the persistent medium.
''' </summary>
''' <param name="collectionToSave">Entity collection to persist</param>
''' <param name="refetchSavedEntitesAfterSave">Refetch the data after save</param>
''' <param name="recurse">Recursively save the object graph</param>
''' <returns>Number of records affected</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overridable Overloads Function SaveEntityCollection(ByVal collectionToSave As IEntityCollection2, ByVal refetchSavedEntitesAfterSave As Boolean, ByVal recurse As Boolean) As Integer
' perform safe type checking.
CheckType(collectionToSave)
' persist the entity collection to the data source.
Return _dataAccessAdapter.SaveEntityCollection(collectionToSave, refetchSavedEntitesAfterSave, recurse)
End Function
#End Region
#Region " Delete Methods "
''' -----------------------------------------------------------------------------
''' <summary>
''' Deletes the entity to the persistent medium.
''' </summary>
''' <param name="entityToDelete">Entity to delete</param>
''' <returns>True if delete was successful, false otherwise</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Overloads Function DeleteEntity(ByVal entityToDelete As IEntity2) As Boolean
Return DeleteEntity(entityToDelete, Nothing)
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Deletes the entity from the persistent medium.
''' </summary>
''' <param name="entityToDelete">Entity to delete</param>
''' <param name="deleteRestriction">IPredicateExpression filter</param>
''' <returns>True if delete was successful, false otherwise</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overridable Overloads Function DeleteEntity(ByVal entityToDelete As IEntity2, ByVal deleteRestriction As IPredicateExpression) As Boolean
' perform safe type checking.
CheckType(entityToDelete)
' delete the entity from the data source.
Return _dataAccessAdapter.DeleteEntity(entityToDelete, deleteRestriction)
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Deletes the entities directly from the persistent medium based on the specified filter.
''' </summary>
''' <param name="filterBucket">IRelationPredicateBucket filter</param>
''' <returns>Number of records affected</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overridable Overloads Function DeleteEntitiesDirectly(ByVal filterBucket As IRelationPredicateBucket) As Integer
Return _dataAccessAdapter.DeleteEntitiesDirectly(ManagedEntityInfo.EntityFactoryToUse.Create().LLBLGenProEntityName, filterBucket)
End Function
#End Region
#Region " Fetch Methods "
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetches the entity from the persistent medium.
''' </summary>
''' <param name="entityToFetch">Entity to fetch</param>
''' <returns>True if fetch was successful, false otherwise</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 12/1/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Overloads Function FetchEntity(ByVal entityToFetch As IEntity2) As Boolean
Return FetchEntity(entityToFetch, Nothing)
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetches an entity from the persistent storage.
''' </summary>
''' <param name="entityToFetch">Entity to fetch</param>
''' <param name="prefetchPath">Any foreign keys which need to be prefetched</param>
''' <returns>True if fetch was successful, false otherwise</returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 12/1/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overridable Overloads Function FetchEntity(ByVal entityToFetch As IEntity2, ByVal prefetchPath As IPrefetchPath2) As Boolean
' perform safe type checking.
CheckType(entityToFetch)
' fetch the entity from the data source.
Return _dataAccessAdapter.FetchEntity(entityToFetch, prefetchPath)
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetch Record count for the entire entity collection.
''' </summary>
''' <returns></returns>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 12/10/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Overridable Overloads Function FetchRecordCount() As Integer
' first we need to create our entity based on the factory.
Dim entity As IEntity2 = ManagedEntityInfo.EntityFactoryToUse.Create()
' execute our scalar function and return the record count.
Return Convert.ToInt32(_dataAccessAdapter.GetScalar(CType(entity.Fields.PrimaryKeyFields(0), IEntityField2), AggregateFunction.CountRow))
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetches one or more entities without any filtering or sorting.
''' </summary>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 12/2/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Overloads Function FetchEntityCollection() As IEntityCollection2
' create our entity collection.
Dim collection As IEntityCollection2 = CreateEntityCollection()
' fetch our entity collection with the default sort expression.
FetchEntityCollection(collection, Nothing, 0, DefaultSortExpression)
' return the collection.
Return collection
End Function
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetches one or more entities into the EntityCollection passed in.
''' </summary>
''' <param name="collectionToFill">EntityCollection object containing an entity factory which has to be filled</param>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 12/1/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overloads Sub FetchEntityCollection(ByVal collectionToFill As IEntityCollection2)
FetchEntityCollection(collectionToFill, Nothing)
End Sub
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetches one or more entities into the EntityCollection passed in.
''' </summary>
''' <param name="collectionToFill">EntityCollection object containing an entity factory which has to be filled</param>
''' <param name="filterBucket">filter information for retrieving the entities. If null, all entities are returned of the type created by the factory in the passed in EntityCollection instance</param>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 12/1/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overloads Sub FetchEntityCollection(ByVal collectionToFill As IEntityCollection2, ByVal filterBucket As IRelationPredicateBucket)
FetchEntityCollection(collectionToFill, filterBucket, 0)
End Sub
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetches one or more entities into the EntityCollection passed in.
''' </summary>
''' <param name="collectionToFill">EntityCollection object containing an entity factory which has to be filled</param>
''' <param name="filterBucket">filter information for retrieving the entities. If null, all entities are returned of the type created by the factory in the passed in EntityCollection instance</param>
''' <param name="maxNumberOfItemsToReturn">The maximum amount of entities to return. If 0, all entities matching the filter are returned</param>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 12/1/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overloads Sub FetchEntityCollection(ByVal collectionToFill As IEntityCollection2, ByVal filterBucket As IRelationPredicateBucket, ByVal maxNumberOfItemsToReturn As Integer)
FetchEntityCollection(collectionToFill, filterBucket, maxNumberOfItemsToReturn, Nothing)
End Sub
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetches one or more entities into the EntityCollection passed in.
''' </summary>
''' <param name="collectionToFill">EntityCollection object containing an entity factory which has to be filled</param>
''' <param name="filterBucket">filter information for retrieving the entities. If null, all entities are returned of the type created by the factory in the passed in EntityCollection instance</param>
''' <param name="maxNumberOfItemsToReturn">The maximum amount of entities to return. If 0, all entities matching the filter are returned</param>
''' <param name="sortClauses">SortClause expression which is applied to the query executed, sorting the fetch result</param>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 12/1/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overloads Sub FetchEntityCollection(ByVal collectionToFill As IEntityCollection2, ByVal filterBucket As IRelationPredicateBucket, ByVal maxNumberOfItemsToReturn As Integer, ByVal sortClauses As ISortExpression)
FetchEntityCollection(collectionToFill, filterBucket, maxNumberOfItemsToReturn, sortClauses, Nothing)
End Sub
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetches one or more entities into the EntityCollection passed in.
''' </summary>
''' <param name="collectionToFill">EntityCollection object containing an entity factory which has to be filled</param>
''' <param name="filterBucket">filter information for retrieving the entities. If null, all entities are returned of the type created by the factory in the passed in EntityCollection instance</param>
''' <param name="prefetchPath">The prefetch path to use for this fetch, which will fetch all related entities defined by the path as well</param>
''' <remarks></remarks>
''' <history>
''' mpaul 12/1/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overloads Sub FetchEntityCollection(ByVal collectionToFill As IEntityCollection2, ByVal filterBucket As IRelationPredicateBucket, ByVal prefetchPath As IPrefetchPath2)
FetchEntityCollection(collectionToFill, filterBucket, 0, Nothing, prefetchPath)
End Sub
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetches one or more entities into the EntityCollection passed in.
''' </summary>
''' <param name="collectionToFill">EntityCollection object containing an entity factory which has to be filled</param>
''' <param name="filterBucket">filter information for retrieving the entities. If null, all entities are returned of the type created by the factory in the passed in EntityCollection instance</param>
''' <param name="maxNumberOfItemsToReturn">The maximum amount of entities to return. If 0, all entities matching the filter are returned</param>
''' <param name="sortClauses">SortClause expression which is applied to the query executed, sorting the fetch result</param>
''' <param name="pageNumber">The page number to retrieve. First page is 1. When set to 0, no paging logic is applied</param>
''' <param name="pageSize">The size of the page. When set to a value smaller than 2, no paging logic is applied</param>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 12/1/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overridable Overloads Sub FetchEntityCollection(ByVal collectionToFill As IEntityCollection2, ByVal filterBucket As IRelationPredicateBucket, ByVal maxNumberOfItemsToReturn As Integer, ByVal sortClauses As ISortExpression, ByVal pageNumber As Integer, ByVal pageSize As Integer)
' perform our safe type checking.
CheckType(collectionToFill)
' fetch our entity collection from the data source.
_dataAccessAdapter.FetchEntityCollection(collectionToFill, filterBucket, maxNumberOfItemsToReturn, sortClauses, pageNumber, pageSize)
End Sub
''' -----------------------------------------------------------------------------
''' <summary>
''' Fetches one or more entities into the EntityCollection passed in.
''' </summary>
''' <param name="collectionToFill">EntityCollection object containing an entity factory which has to be filled</param>
''' <param name="filterBucket">filter information for retrieving the entities. If null, all entities are returned of the type created by the factory in the passed in EntityCollection instance</param>
''' <param name="maxNumberOfItemsToReturn">The maximum amount of entities to return. If 0, all entities matching the filter are returned</param>
''' <param name="sortClauses">SortClause expression which is applied to the query executed, sorting the fetch result</param>
''' <param name="prefetchPath">The prefetch path to use for this fetch, which will fetch all related entities defined by the path as well</param>
''' <remarks></remarks>
''' <history>
''' mpaul 12/1/2004 Created
''' </history>
''' -----------------------------------------------------------------------------
Protected Overridable Overloads Sub FetchEntityCollection(ByVal collectionToFill As IEntityCollection2, ByVal filterBucket As IRelationPredicateBucket, ByVal maxNumberOfItemsToReturn As Integer, ByVal sortClauses As ISortExpression, ByVal prefetchPath As IPrefetchPath2)
' perform our safe type checking.
CheckType(collectionToFill)
' fetch our entity collection from the data source.
_dataAccessAdapter.FetchEntityCollection(collectionToFill, filterBucket, maxNumberOfItemsToReturn, sortClauses, prefetchPath)
End Sub
#End Region
End Class
End Namespace
Imports SD.LLBLGen.Pro.ORMSupportClasses
Namespace Sheakley.ORM
''' -----------------------------------------------------------------------------
''' Project : Sheakley.ORM
''' Struct : Sheakley.ORM.EntityInfo
'''
''' -----------------------------------------------------------------------------
''' <summary>
''' Lightweight wrapper for encapsulating identifying entity attributes.
''' </summary>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Structure EntityInfo
' private attributes.
Private _entityType As Type
Private _entityFactoryToUse As IEntityFactory2
''' -----------------------------------------------------------------------------
''' <summary>
''' Ctor.
''' </summary>
''' <param name="entityType">Entity type</param>
''' <param name="entityFactoryToUse">Entity factory used to create entity</param>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Sub New(ByVal entityType As Type, ByVal entityFactoryToUse As IEntityFactory2)
_entityType = entityType
_entityFactoryToUse = entityFactoryToUse
End Sub
''' -----------------------------------------------------------------------------
''' <summary>
''' Gets and sets the entity type.
''' </summary>
''' <value></value>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Property EntityType() As Type
Get
Return _entityType
End Get
Set(ByVal Value As Type)
_entityType = Value
End Set
End Property
''' -----------------------------------------------------------------------------
''' <summary>
''' Gets and sets the entity factory to use.
''' </summary>
''' <value></value>
''' <remarks>
''' </remarks>
''' <history>
''' mpaul 2/8/2005 Created
''' </history>
''' -----------------------------------------------------------------------------
Public Property EntityFactoryToUse() As IEntityFactory2
Get
Return _entityFactoryToUse
End Get
Set(ByVal Value As IEntityFactory2)
_entityFactoryToUse = Value
End Set
End Property
End Structure
End Namespace
Joined: 08-May-2004
Hey MarcoP,
I looked over your manager class. I think what you wrote should work, but I also wonder how much extra functionality your class offers beyond what the existing DataAccessAdapter offers. I do understand that you want to hide the DataAccessAdapter from the UI layer, which your class does do. What follows is my own manager class, though it's not called a manager class. Any thoughts?
using System;
using SD.LLBLGen.Pro.ORMSupportClasses;
using EZPharmSoft.Dms.Data;
using EZPharmSoft.Common.Misc;
namespace EZPharmSoft.Dms.BusinessServices
{
// Summary:
// Provides basic entity creation, updating, retrieval, deletion, and
// validation features.
// Remarks:
// Tables with no foreign keys should always have a corresponding
// EntityServicesBase child class.
//
// If a table does have foreign key(s), create a corresponding
// EntityServicesBase child class for every table in the relationship
// which you want to access directly. For example, the Rx table has a
// foreign key reference to the Patient table. Since we want the user to
// be able to search for and update individual prescriptions and patients,
// RxServices and PatientServices classes should be created for both
// tables.
//
// However, the PatientDetails table references the Patient table. In this
// case, we want the user to find and update a specific PatientDetails
// record by finding a patient and then editing the desired detail record.
// Therefore, creating a PatientDetailsServices class is unnecessary.
// Instead, load a patient's details when loading the patient using the
// FetchRelatedEntities method.
public abstract class EntityServicesBase : PersistenceServicesBase
{
// Summary:
// The system type of the entity this class serves.
// Remarks:
// Methods such as Save and Delete use this field to determine whether
// to save an entity. An EntityServices instance should only serve the
// type of entity for which it was created because other entity types
// won't be handled correctly.
protected Type EntitySystemType
{
get { return entitySystemType; }
}
private Type entitySystemType;
// Summary:
// The constructor needs the entity type to create the EntityValidator,
// though IdEntityServices also uses it to generate a surrogate key.
public abstract DmsEntities EntityType { get; }
// Summary: Gets the entity validator.
public EntityValidatorBase EntityValidator
{
get { return entityValidator; }
}
private EntityValidatorBase entityValidator;
// Summary:
// Initializes a new EntityServicesBase instance.
// Parameters:
// dataAccessAdapter - The IDataAccessAdapter instance used to access
// the database. If this value is null, a new
// instance will be created using the
// DbConnectString.
// entitySystemType - The type of entity a child class is created to
// serve.
public EntityServicesBase(IDataAccessAdapter dataAccessAdapter,
Type entitySystemType) : base(dataAccessAdapter)
{
// Since validation will take place on this class' thread, it makes
// sense for this class and its validator to share IDataAccessAdapter
// instances.
entityValidator = CreateValidator(EntityType.ToString(),
dataAccessAdapter);
this.entitySystemType = entitySystemType;
}
//
// Methods which child classes must implement
//
// Summary:
// Creates a new IEntity2 instance.
// Remarks:
// When implementing, initialize the new entity's values so that, aside
// from setting required field values, the entity is in a consistent
// state which will pass validation. This is helpful for example, in the
// Dms.Forms DataEntryControls, where it's important for the newly
// created entity's fields to be set to reasonable default values.
// Note:
// When setting default values for fields which are validated, you can
// use the field's validator object to obtain the correct default value.
// This prevents the definition of the default value in more than
// one place. Look at the DoctorServices implementation of this method
// for an example.
public abstract IEntity2 CreateNew();
// Summary:
// Creates the class' EntityValidatorBase instance.
// Parameters:
// entityName - The name of the entity to which this class corresponds.
// adapter - The IDataAccessAdapter instance which the validator will
// use if it needs to query the database.
// Returns:
// An EntityValidatorBase instance.
protected abstract EntityValidatorBase CreateValidator
(string entityName, IDataAccessAdapter adapter);
//
// Database operations
//
// Summary:
// Opens the database connection.
// Remarks:
// This method is handy when you need to perform several database
// operations and don't want the overhead of opening and closing the
// connection repeatedly. If the connection isn't opened explicitly
// using this method, it is opened and closed for each database
// operation.
public void OpenConnection()
{
DataAccessAdapter.OpenConnection();
DataAccessAdapter.KeepConnectionOpen = true;
}
// Summary: Closes the database connection.
public void CloseConnection()
{
DataAccessAdapter.CloseConnection();
DataAccessAdapter.KeepConnectionOpen = false;
}
// Summary:
// Saves an entity to the database.
// Remarks:
// Before the entity is saved, the EntityValidator's ValidationErrors
// collection will be cleared and the entity will be refetched.
// Parameters:
// entity - The entity to save.
// Returns:
// True if the entity was saved, otherwise false.
public bool Save(IEntity2 entity)
{
return Save(entity, true, true);
}
// Summary:
// Saves an entity to the database.
// Remarks:
// If you plan to use the entity after you save it, you must refetch it
// or the IDataAccessAdapter instance will throw an exception. If you
// don't plan to use the enitity after saving, refetching is wasteful.
// Parameters:
// entity - The entity to save.
// clearValidationErrors - Indicates whether to clear the
// EntityValidator's ValidationErrors collection
// before the entity is saved.
// refetch - Indicates whether to refetch the entity after it has been
// saved.
// Returns:
// True if the entity was saved, otherwise false.
public bool Save(IEntity2 entity, bool clearValidationErrors,
bool refetch)
{
if (clearValidationErrors == true)
{
EntityValidator.ValidationErrors.Clear();
}
return Save(entity, refetch);
}
// Summary:
// Saves an entity to the database.
// Remarks:
// If you plan to use the entity after you save it, you must refetch it
// or the DataAccessAdapter will throw an exception. If you don't plan
// to use the enitity after saving, refetching is wasteful.
// Parameters:
// entity - The entity to save.
// refetch - Indicates whether to refetch the entity after it has been
// saved.
// Returns:
// True if the entity was saved, otherwise false.
protected virtual bool Save(IEntity2 entity, bool refetch)
{
entity.EntityValidatorToUse = entityValidator;
if (entity.GetType() == EntitySystemType)
return DataAccessAdapter.SaveEntity(entity, refetch);
else
return false;
}
// Summary:
// Fills an entity with current values from the database.
// Parameters:
// entity - The entity to refresh.
// Returns:
// True if the entity was refreshed, otherwise false.
public bool Refresh(IEntity2 entity)
{
if (entity.GetType() == EntitySystemType)
{
return DataAccessAdapter.FetchEntity(entity) &&
FetchRelatedEntities(entity);
}
else
return false;
}
// Summary:
// Deletes an entity.
// Remarks:
// This method only deletes the entity specified, not any other related
// entities. If you also need to delete related entities, override this
// method.
// Example:
// <code>
// public override bool Delete(IEntity2 entity)
// {
// CustomerEntity customer = entity as CustomerEntity;
//
// if (customer != null)
// DataAccessAdapter.DeleteEntityCollection(customer.Orders);
//
// return base.Delete(entity);
// }
// </code>
// Parameters:
// entity - The entity to delete.
// Returns:
// True if the entity was deleted, otherwise false.
public virtual bool Delete(IEntity2 entity)
{
entity.EntityValidatorToUse = null;
if (entity.GetType() == EntitySystemType)
return DataAccessAdapter.DeleteEntity(entity);
else
return false;
}
// Summary:
// Fetches an entity based on the specified primary key.
// Remarks:
// If the entity that's to be returned has a related entity (for
// example, an Employer entity has a related Company entity), load it by
// overriding the FetchRelatedEntities method.
// Note:
// - If the entity has a compound primary key, make sure to pass the key
// values in the order in which they're defined in the entity's table.
// - Some related entities don't have to exist. For example, a Customer
// might not have any related Orders.
// - Other related entities must exist. For example, an Employer must
// have a related Company.
// Parameters:
// primaryKey - The target entity's primary key.
// Returns:
// The entity, if it and all required related entities were found,
// otherwise null.
public IEntity2 FindByPrimaryKey(params object[] primaryKey)
{
IEntity2 entity = CreateNew();
SetPrimaryKey(entity, primaryKey);
// If the connection is already open, it should be left open after
// the entity is loaded.
bool connectionWasAlreadyOpen = DataAccessAdapter.KeepConnectionOpen;
try
{
DataAccessAdapter.OpenConnection();
DataAccessAdapter.KeepConnectionOpen = true;
if (DataAccessAdapter.FetchEntity(entity) == true)
{
if (FetchRelatedEntities(entity) == true)
return entity;
}
}
finally
{
if (connectionWasAlreadyOpen == false)
{
DataAccessAdapter.CloseConnection();
DataAccessAdapter.KeepConnectionOpen = false;
};
}
return null;
}
// Summary:
// Fetches an entity using a unique constraint.
// Remarks:
// If an entity has a unique constraint, it will have a method for
// creating a filter which specifies the unique columns by which to
// search. This method's name begins with 'ConstructFilterForUC'.
// Parameters:
// entity - A new entity with the unique value(s) set that are needed to
// load the desired entity.
// uniqueConstraintFilter - The filter that specifies the unique
// column(s) to use when searching.
// Returns:
// The entity, if it and all required related entities were found,
// otherwise null.
protected IEntity2 FindByUniqueValue(IEntity2 entity,
IPredicateExpression uniqueConstraintFilter)
{
// If the connection is already open, it should be left open after
// the entity is loaded.
bool connectionWasAlreadyOpen = DataAccessAdapter.KeepConnectionOpen;
try
{
DataAccessAdapter.OpenConnection();
DataAccessAdapter.KeepConnectionOpen = true;
bool fetchedEntity = DataAccessAdapter.FetchEntityUsingUniqueConstraint
(entity, uniqueConstraintFilter);
if (fetchedEntity)
{
if (FetchRelatedEntities(entity) == true)
return entity;
}
}
finally
{
if (connectionWasAlreadyOpen == false)
{
DataAccessAdapter.CloseConnection();
DataAccessAdapter.KeepConnectionOpen = false;
};
}
return null;
}
// Summary:
// Determines whether a duplicate exists for the specified entity.
// Remarks:
// Usually the primary key is used to determine duplicate existance.
// Override this method if your class should check for duplicates. See
// the DoctorServices class for an example.
// Arguments:
// entity - The entity that is being checked for duplicates.
// Returns:
// true if a duplicate exists, false otherwise.
public virtual bool DuplicateExists(IEntity2 entity)
{
return false;
}
// Summary:
// Fetches the entities which are related to the specified entity.
// Remarks:
// Override this method if the entity you're trying to load has any
// related entities which you also want to load.
//
// The example shows how to fetch a one-to-one related entity. Fetching
// one-to-many related entities is very similar; just call
// FetchEntityCollection instead of FetchEntity.
// Example:
// <code>
// protected override bool FetchRelatedEntities(IEntity2 rootEntity)
// {
// EmployerEntity employer = rootEntity as EmployerEntity;
//
// employer.Company = (EmployerEntity)FetchEntity
// (new EmployerEntityFactory(),
// employer.GetRelationInfoCompany());
//
// if (entity.Company.IsNew == false)
// return base.FetchRelatedEntities(rootEntity);
// else
// return false;
// }
// </code>
// Parameters:
// entity - The entity which has related entities.
// Returns:
// True if the related entities were fetched, otherwise false.
protected virtual bool FetchRelatedEntities(IEntity2 entity)
{
return true;
}
// Summary:
// Fetches an entity based on the specified IEntityFactory2 and
// IRelationPredicateBucket.
// Remarks:
// If you're overriding FetchRelatedEntities, you may want to use this
// method to fetch single related entities. See the FetchRelatedEntities
// method for an example.
// Parameters:
// entityFactory - The entity factory which.
// relationInfo - The predicate bucket containing criteria to locate an
// entity.
// Returns:
// The entity that was fetched.
protected IEntity2 FetchEntity(IEntityFactory2 entityFactory,
IRelationPredicateBucket relationInfo)
{
return DataAccessAdapter.FetchNewEntity(entityFactory, relationInfo);
}
// Summary:
// Fills an entity collection based on the specified
// IRelationPredicateBucket.
// Remarks:
// If you're overriding FetchRelatedEntities, you may want to use this
// method to fetch related entities. See the FetchRelatedEntities method
// for an example.
// Parameters:
// entityCollection - The entity collection to fill.
// relationInfo - The predicate bucket containing criteria to locate an
// entity.
protected void FetchEntities(EntityCollectionBase2 entityCollection,
IRelationPredicateBucket relationInfo, ISortExpression sortExpression)
{
DataAccessAdapter.FetchEntityCollection(entityCollection, relationInfo,
Int32.MaxValue, sortExpression);
}
// Summary:
// Sets an entity's primary key.
// Parameters:
// entity - The entity who's primary key should be set.
// primaryKey - The primary key with which the entity should be set.
public static void SetPrimaryKey(IEntity2 entity, object[] primaryKey)
{
// Prevents the user of this method from passing in the wrong number of
// arguments.
if (primaryKey.Length == entity.PrimaryKeyFields.Count)
{
for (int i = 0; i < primaryKey.Length; i++)
{
((IEntityField2)entity.Fields.PrimaryKeyFields[i]).CurrentValue =
primaryKey[i];
}
}
else
throw new ArgumentException(Resources.GetString
("PrimaryKeyArgumentError"), "primaryKey");
}
}
}
Joined: 29-Sep-2004
Jeremy wrote:
Hey MarcoP,
I looked over your manager class. I think what you wrote should work, but I also wonder how much extra functionality your class offers beyond what the existing DataAccessAdapter offers. I do understand that you want to hide the DataAccessAdapter from the UI layer, which your class does do. What follows is my own manager class, though it's not called a manager class. Any thoughts?
Hey Jeremy,
The purpose of the ControllerBase is to simplify the derived manager classes and be able to pull functionality right out of the box. By just inheriting directly from the base, without any additional code in the controller (besides the abstract method overrides), the manager provides persistence, fetching entity/collection, basic scalar functionality, and allows transactions to span multiple manager/controllers. In order for this class to be completly reused throughout our organization, I had to cut down on some of the functionality. Like I said, this is still in process, however, it is being used in multiple production applications. When I get home from work tonight, I'll look at what you came up with.