Generic Business Layer

Programming 3 October 2007 | 0 Comments

This is part 3 of a 4 part series where I describe an attempt to implement some generic principles I thought up at the cottage this past summer

In part 1 I discussed XML serialization and its uses.

In part 2 I discussed the creation of a generic data layer that utilizes the XML serialization techniques

The next step was to tackle the slightly more complex generic business layer. What are the goals here? These were my thoughts:

  1. Create a fully reusable piece of code for all classes
  2. Create easy retrieval mechanisms for individual entities and lists
  3. Create easy persistence mechanisms for entities and lists
  4. Allow for easy and generic searching and filtering
  5. Allow for easy sorting

Using a combination of Reflection and Generics I think I have achieved this first set of goals. To be honest this is not the first implementation, nor will it be the last ofcourse, but the time I spend enhancing these classes now feel like very valuable work.

There are 2 classes that have been created so far, the main class which I just call GenericBusiness<T> and a helper class I recently created called GenericComparer. Here is the source: Source of GenericBusiness<T>:

 using System; using System.Collections.Generic; using System.Text; using System.Reflection;
namespace TwentySeven.Blogger.Services { /// <summary> ///
A generic XML business class that allows for retrieving and persisting also some basic
search functions. /// Works really well if the class implements the IIdentifer interface.
/// </summary> /// <typeparam name="T"></typeparam> public class GenericBusiness<T>
{ #region Constructor /// <summary> /// Initializes a new instance of the
<see cref="T:GenericBusiness<T>"/> class. /// </summary> public GenericBusiness() { }
 #endregion #region Declarations List<T> items = null; PropertyInfo[] properties = null;
#endregion #region Methods /// <summary> /// Persists this instance. ///
</summary> public void Persist() { GenericData<T> genericXML = new GenericData<T>();
genericXML.Save(Items); items = null; } /// <summary> /// Gets the item by field. ///
</summary> /// <param name="comparer">The comparer.</param> ///
<returns></returns> public T GetItemByField(GenericComparer comparer)
{ List<GenericComparer> comparers = new List<GenericComparer>(); comparers.Add(comparer);
List<T> records = GetListByFields(comparers); if(records.Count > 0) return records[0];
else return default(T); } /// <summary> /// Gets the item by field. /// </summary> ///
<param name="filterPredicate">The filter predicate.</param> ///
<returns></returns> public T GetItemByField(Predicate<T> filterPredicate)
{ //As an example: //Predicate<ContentEntry> p = delegate(ContentEntry contentEntry)
{ return contentEntry.Id == int.Parse(id); }; return Items.Find(filterPredicate); }
/// <summary> /// Deletes the items by field. /// </summary> ///
<param name="comparer">The comparer.
</param> public void DeleteItemsByField(GenericComparer comparer)
{ List<T> items = GetListByField(comparer); foreach (T item in items) { Items.Remove(item); } }
 /// <summary> /// Gets the list by field. /// </summary> ///
<param name="comparer">The comparer.</param> /// <returns></returns> public List<T>
GetListByField(GenericComparer comparer)
{ List<GenericComparer> comparers = new List<GenericComparer>(); comparers.Add(comparer);
return GetListByFields(comparers); } /// <summary> /// Gets the list by fields. ///
</summary> /// <param name="comparisons">The comparisons.</param> ///
<returns></returns> public List<T> GetListByFields(List<GenericComparer> comparisons)
{ List<T> filteredItems = new List<T>(); int fieldNamesIndex; foreach (T item in Items)
{ foreach (PropertyInfo property in Properties) { fieldNamesIndex = 0;
foreach (GenericComparer genericComparer in comparisons)
{ if (property.Name.ToUpper() == genericComparer.FieldName.ToUpper())
{ switch (genericComparer.ComparisonType)
{ case ComparisonTypes.Equals: if (genericComparer.Value.Equals(property.GetValue(item, null)))
 filteredItems.Add(item); break;
case ComparisonTypes.Contains: if (property.GetValue(item, null).ToString().Contains(genericComparer.Value.ToString()))
filteredItems.Add(item); break; case ComparisonTypes.Startwith:
if (property.GetValue(item, null).ToString().StartsWith(genericComparer.Value.ToString()))
 filteredItems.Add(item); break; case ComparisonTypes.Endswith:
if (property.GetValue(item, null).ToString().EndsWith(genericComparer.Value.ToString()))
 filteredItems.Add(item); break; case ComparisonTypes.NotEqual:
if (!genericComparer.Value.Equals(property.GetValue(item, null))) filteredItems.Add(item);
break; } } fieldNamesIndex++; } } } return filteredItems; } /// <summary> ///
Gets the list by fields. /// </summary> /// <param name="filterPredicate">The filter predicate.
</param> /// <returns></returns> public List<T> GetListByFields(Predicate<T> filterPredicate)
{ return Items.FindAll(filterPredicate); } #endregion #region Properties /// <summary> ///
Gets the items. /// </summary> /// <value>The items.</value> public List<T>
Items { get { if (items == null) { //Refresh the user list GenericData<T>
genericXML = new GenericData<T>(); items = genericXML.Get(); } return items; } } /// <summary> ///
 Gets the next id. /// </summary> /// <value>The next id.</value> public int NextId { get
{ int maxId = 0; System.Reflection.PropertyInfo[] properties = typeof(T).GetProperties();
foreach (T item in Items) { foreach (PropertyInfo property in properties)
{ if (property.Name.ToUpper() == "ID" && (int)property.GetValue(item, null) > maxId)
maxId = (int)property.GetValue(item, null); } } return ++maxId; } } /// <summary> ///
Gets the properties. /// </summary> /// <value>The properties.</value> private PropertyInfo[]
Properties { get { //Populate the reflected property list if it has not been done already if
(properties == null) properties = typeof(T).GetProperties();
//Return the properties return properties; } } #endregion } }

Source of GenericComparer:

 using System; using System.Collections.Generic; using System.Text;
namespace TwentySeven.Blogger.Services { #region Enumerations /// <summary> ///
Type of string comparisons /// </summary> public enum ComparisonTypes
{ /// <summary>Exactly</summary> Equals, /// <summary>Contains</summary> Contains, ///
 <summary>Startwith</summary> Startwith, /// <summary>Endswith</summary> Endswith, ///
 <summary>NotEqual</summary> NotEqual } #endregion /// <summary> /// A generic comparison ///
 </summary> public class GenericComparer { #region Constructor /// <summary> ///
Initializes a new instance of the <see cref="T:GenericComparer"/> class. ///
</summary> public GenericComparer() { } /// <summary> ///
Initializes a new instance of the <see cref="T:GenericComparer"/> class. /// </summary> ///
<param name="fieldName">Name of the field.</param> ///
<param name="value">The value.</param> public GenericComparer(string fieldName, object value)
 { FieldName = fieldName; Value = value; } /// <summary> ///
Initializes a new instance of the <see cref="T:GenericComparer"/> class. /// </summary> ///
 <param name="fieldName">Name of the field.</param> /// <param name="value">The value.</param>
/// <param name="comparisonType">Type of the comparison.</param> public
GenericComparer(string fieldName, object value, ComparisonTypes comparisonType)
 { FieldName = fieldName; Value = value; ComparisonType = comparisonType; }
#endregion #region Declarations private string fieldName = string.Empty;
private object value = null; private ComparisonTypes comparisonType = ComparisonTypes.Equals;
 #endregion #region Properties /// <summary> /// Gets or sets the name of the field. ///
</summary> /// <value>The name of the field.</value> public string FieldName { get {
return fieldName; } set { fieldName = value; } } /// <summary> /// Gets or sets the value. ///
</summary> /// <value>The value.</value> public object Value { get { return value; } set
{ this.value = value; } } /// <summary> /// Gets or sets the type of the comparison. ///
</summary> /// <value>The type of the comparison.</value> public ComparisonTypes ComparisonType
{ get { return comparisonType; } set { comparisonType = value; } } #endregion } }

In the end, we have a VERY generic business layer that gives the following methods and properties:

  • Property public List<T> Items: A strong typed list of entites
  • Property public int NextId: Retrieves the next sequence value if you have a property called Id in your class – recall we are using XML serialization, so the business layer supports incremented values.
  • Method public void Persist(): Saves all modified, or new values in the entire list
  • Method public T GetItemByField(GenericComparer comparer): Returns the first record found matching the generic comparer record.
  • Method public T GetItemByField(Predicate filterPredicate): Returns the first record found matching the predicate.
  • Method public void DeleteItemsByField(GenericComparer comparer): Deletes a collection of items or item based on the generic comparer.
  • Method public List GetListByField(GenericComparer comparer): Returns a list of entities meeing your comparer
  • Method public List GetListByFields(List comparisons): Returns a list of entities meeting all of your generic comparers.
  • Method public List GetListByFields(Predicate filterPredicate): Returns a list if entities based on your filter predicate.

This may seem like a relatively short list, but I encourage you to attempt to use this class and see for yourself – I have not yet found an instance where I can not use this generic business layer with no additional code.

Part 4 will describe the implementation of this business layer as used in a real application so you can see the total power, simplicity and speed of these techniques when used in conjunction.

Leave a Reply