Useful Extension Methods
page 3 of 4
by Brian Mains
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 22936/ 49

Extension Method Application

As you can see, extension methods are useful.  One thing I commonly do is add extension methods for business objects, to provide shortcuts when accessing objects.  For instance, suppose you had a Student class.  This student registers for Courses, which are linked to a Semester.  All of the capitalized objects mentioned in the past two sentences are the tables, creating a relationship from Student to Courses and from Semester to Courses, with Courses being the many-to-many table.

Suppose the application wants to get only the courses for the current semester, but doesn't delete the past semesters from the database, for historical tracking purposes.  To keep the previous semesters out of the query results, the Semester table has an IsCurrent flag, denoting the current semester with a bit value of 1 in a SQL Server database.

This query is in a database and in the BAL/DAL, but I often write an extension method as such

Listing 6

public static class StudentExtensions
{
  public static IEnumerable<Course> GetCurrentCourses(this Student student)
    {
      CoursesService service = new CoursesService();
      return service.GetCurrentCourses(student.Key);
    }
}

The CoursesService is a business object that gets the data from the data access layer and converts it to course business objects.  You may ask why not embed this directly in the student object?  Well, your business layer may be auto-generated by an ORM mapping software, like IdeaBlade, Wilson ORMapper, LINQ-to-SQL classes, etc., or it may be an assembly where the Student class is not inheritable and not defined as a partial class.  ORM mappers often regenerate the classes when changes are made or if you have to clear the entire setup and redo all your mappings.  Extension methods extract the logic and save your code from this happening.  Extension methods are best for classes marked as sealed/not-inheritable because you can append your methods to these objects through extension methods.

Extension methods help to shortcut code and encapsulate references.  What I mean by that is in the simple example above, it hid the fact that it used a service class to get the courses, and the fact that courses are retrieved by the student's key.  How about getting all the semesters a student has been involved in, where a student's course grades are worthy of honors?

Listing 7

Public static IEnumerable < Semester > GetCoursesWithHonors(this Student
  student)
{
  CoursesService service = new CoursesService();
  IEnumerable < Course > courses = service.GetAllCourses();
  return (from c in courses Where c.Grade > 3.4 && c.IsCompleted == true select
    c.Semester).Distinct();
}

This way, you can simply invoke the following statement, when binding to an ASP.NET GridView

Listing 8

this.StudentGridView.DataSource = student.GetCoursesWithHonors();
this.StudentGridView.DataBind()

All of the work for processing the record and for determining what grades are included in honors, are encapsulated into one single method for you.  It also works when updating or creating new records.  For instance, suppose you get the student's record, but if the student doesn't exist, a record is created for them.  This would be based on the key value, which in this case is first and last name (in most cases, there is a student ID).  This could be done with the following

Listing 9

public static Student CreateOrGetStudent(string firstName, string lastName)
{
  StudentService service = new StudentService();
  Student student = service.GetByName(firstName, lastName);
  if (student == null)
  {
    student = new Student(firstName, lastName);
    service.Add(student);
  }
  return student;
}

There are other useful things you can do with extension methods, like formatting text

Listing 10

public static string TrimText(this string text, bool ensurePeriodAtEnd)
{
  if (text == null)
    return string.Empty;
 
  text = text.Trim();
  if (string.IsNullOrEmpty(text))
    return string.Empty;
  if (text == "\"\"")
    return string.Empty;
 
  if (string.StartsWith("\""))
    text = text.Substring(1);
  else if (string.EndsWith("\""))
    text = text.Substring(0, text.Length - 1);
  text = text.Trim();
 
  if (ensurePeriodAtEnd && !text.EndsWith("."))
    text += ".";
 
  return text;
}

I often work with data like Excel data, where if the comma exists inside the data, it's wrapped with double-quotes.  This method helps strip off those outside double-quotes, as well as ensure it's a proper sentence with a period at the end.  It also ensures that if null or empty, it returns an empty string safely.

How about the list control in ASP.NET?  Extension methods can work with any object, which also counts for ASP.NET or windows server controls.  How about the need to select all the items in the list (I saw a ClearSelection() method, but did not see a select all method)?  The following static extension method attaches to any class that derives from ListControl, and allows you to invoke the SelectAll method, selecting all items in the list.

Listing 11

public static void SelectAll(this ListControl control)
{
  foreach (ListItem item in control.Items)
  item.Selected = true;
}

How about the use of reflection?  Although easy to use, it's helpful to have a method that shortcuts the accessing of properties, making property statement determinations (such as whether the property can read or write), and casting the value.  The following extension methods may be helpful in extracting values from an underlying business object and returning the property name.  Note that it extends the object type, which means it can work for any object in the .NET Framework.

Listing 12

public static object GetPropertyValue(this object value, string propertyName)
{
  return GetPropertyValue(value, propertyName, null);
}
public static object GetPropertyValue(this object value, string propertyName,
  object[]indexes)
{
  if (value == null)
    throw new ArgumentNullException("value");
  if (string.IsNullOrEmpty(propertyName))
    throw new ArgumentNullException("propertyName");
 
  PropertyInfo property = value.GetType().GetProperty(propertyName,
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
  if (property == null)
    throw new Exception("The property does not exist for the target object.");
  if (!property.CanRead)
    throw new Exception("The property cannot be read.");
 
  return property.GetValue(value, indexes);
}
public static T GetPropertyValue < T > (this object value, string propertyName,
  object[]indexes)
{
  return (T)GetPropertyValue(value, propertyName, indexes);
}

The last method defined is a convenience method; it defines a generic type that can be used to strongly-type the return value.  However, ensure this matches the return value, because you may get an InvalidCastException result from it.


View Entire Article

User Comments

Title: RE: Please specify the framework version   
Name: Brendan Enrick
Date: 2008-05-13 9:13:21 AM
Comment:
Yes, extension methods were added in C# 3.0, so this is a new feature.
Title: Please specify the framework version   
Name: Vijil Jones
Date: 2008-05-13 7:38:16 AM
Comment:
Is this new feature in .net 3 ?






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-23 2:21:12 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search