Cache Management Helper Class

As a follow up to my post on creating a Web Session Management and Centralization Class, I thought I would also touch on one method of presentation level caching. Caching, as well all know, stores a local version of some data closer to the actual requestor making the call, whether it is in the BLL, the presentation, or even page/control specific. Caching can be a significant boost to performance and is fairly easy to implement. Additionally, there are several types of caching, such as image, page, custom, etc. In this brief example, we’re going to explore a custom implementation of caching to minimize trips to the database.

When Would I Use This?

In my opinion the ideal candidates for presentation level caching are things like dropdown lists which are dynamically pulled from the database. I’ve been working on a project which utilizes several dropdowns throughout the application, and each one is to be administered dynamically. This means for each page, I may have several calls to simply setup the page and populate the dropdowns.

In this application, I’m calling a business logic layer which calls down to the data access layer and finally to the database. We can minimize the amount of calls to the database by instead of calling directly to the business layer, call our WebCacheManager class, which is going to first check if the data exists in local cache, or if it needs to make a call to retrieve the data. If the call is made down to the business layer, the results are then stored in the local cache until either the cache expires based on an absolute date/time or a dependent file (acting as a trigger) is modified.

The external file, located in _cacheDependencyFilePath in the following code is a textfile, which can be manually or programmatically modified to expire the cache without having to reset IIS or wait out the expiration time period. The following code sets up a helper class which could sit on the presentation (web application), and be called to retrieve cached data back very quickly.

WebCacheManager

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Common.Entities.Complaints;  //This is just a reference to my business entities project
using System.Web.Caching;
using System.IO;

namespace UI.ComplaintsWeb.Code
{
    public class WebCacheManager
    {
        /// 
        /// A reference to the current context cache.
        /// 
        private Cache _cache;

        /// 
        /// A filepath to the cachedependency file which triggers expiration of cache.
        /// 
        private string _cacheDependencyFilePath;

        /// 
        /// The absolute date/time of cache expiration.
        /// 
        private DateTime _expirationDate;

        public WebCacheManager()
        {
            _cache = HttpContext.Current.Cache;
            _cacheDependencyFilePath = Path.GetFullPath("../CacheDependency.txt");
            _expirationDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 59, 59);
        }

        public InspectionType[] GetInspectionTypes()
        {
            InspectionType[] data;
            string cacheKey = "InspectionTypes";

            if (_cache[cacheKey] != null)
            {
                //Get data from cache
                data = _cache[cacheKey] as InspectionType[];
            }
            else
            {
                //Get data from database                
                data = WebHelper.ComplaintsLogic.GetInspectionTypes();

                //Assign to cache
                _cache.Add(cacheKey, data, new CacheDependency(_cacheDependencyFilePath), _expirationDate, System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
            }

            return data;
        }        
    }
}

Fancy, How Do I Call It?

No sweat, let’s imagine we’re binding to a DropDownList called “InspectionTypeList”, instead of your typically business call (or direct DB call if you’re into that sort of thing, and you probably shouldn’t be, but that’s another topic); simple use the following similar syntax:

var cacheManager = new WebCacheManager();
//Inspection Types
InspectionTypeList.DataSource = cacheManager.GetInspectionTypes();
InspectionTypeList.DataBind();

Not too much to it. If you have any questions on how the WebCacheManager is being used, or suggestions on improvements, please leave a comment below. Don’t be shy, you know you want to comment.

A Generic Data Mapper for Simple Data

Recently, a buddy and I were working on a project and we came across an almost abnormal amount of simple data entities which were used primarily for populating dropdowns. You know the type of data, there is essentially an ID, Name, and not much else.

We needed to do several things with these simple entities, namely populate dropdown, but also associate the entities with other more complex entities. In our architecture, we’re using a layered approach and manual mappers of data retrieval to business entities. There were several ways that we could have gone about propagating this data through the DAL, BLL, and to the presentation, but we chose to create individual entities to represent each of these lists of data. Our reasoning for this was to optimize the ability to separate the code and provide for future classes that could take on additional responsibility if these entities became more complex.

For these simple entities, we didn’t want to write an individual mapper for each one (essentially a whole lot of cut and paste work); so we opted to create a generic mapper class which is constrained by a base class which all of the simple entities inherit from (SimpleData).

The SimpleData class is just that, simple, and looks like this:

The SimpleData Entity

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace Common.Entities.General
{
    [DataContract()]
    public abstract class SimpleData
    {
        #region Constructors

        public SimpleData()
        {
        }

        public SimpleData(int id)
            : this(id, string.Empty, string.Empty)
        {
        }

        public SimpleData(string text, string value) : this(0, text, value)
        {            
        }

        public SimpleData(int id, string text, string value)
        {
            this.ID = id;
            this.DataText = text;
            this.DataValue = value;
        }

        #endregion

        #region Properties

        public DateTime Created { get; set; }
        public string DataText { get; set; }
        public string DataValue { get; set; }
        public int ID { get; set; }
        public DateTime? LastUpdated { get; set; }

        #endregion

        #region Methods

        public abstract SimpleData Create(int id, string text, string value);

        #endregion
    }
}

Now that we had a base class to inherit from, we needed to apply this to our simple entity to be mapped; here is one example of our entity inheriting SimpleData:

The Entity to be Mapped

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Common.Entities.General
{
    /// 
    /// An arbitrary class implementing SimpleData.
    /// 
    public class CallType : SimpleData
    {
        #region Constructors

        public CallType()
        {
        }

        public CallType(int id)
            : this(id, string.Empty, string.Empty)
        {
        }

        public CallType(string text, string value) : base(text, value)
        {            
        }

        public CallType(int id, string text, string value)
            : base(id, text, value)
        {
        }

        #endregion

        public override SimpleData Create(int id, string text, string value)
        {
            return new CallType(id, text, value);
        }
    }
}

Now that we had a consistent approach to marking up the simple entities, we could create a single generic mapper class called “SimpleMapper”. The class takes in a DataRow, the name of the ID field, the name of the textual (Name) field, and the name of the value field (specifically, if this is different than the ID).

The reason we have a few extra input parameters here is so the SimpleMapper could handle for these two scenarios (we’ll use our Call Type as the example):

  • Scenario 1: My data has an integer ID for the unique identifier of the database record and the name of the Call Type as the textual value. My database table could look like this:
CallTypes Database Table
ID int
Name varchar(50)
  • Scenario 2: My data has an integer ID for the unique identifier of the database record, a name of the Call Type as the textual value, and a 3-digit textual code for the CallType. My database table could look like this:
CallTypes Database Table
ID int
Name varchar(50)
Code varchar(3)

By specifying which column should be returned as the DataValue field (either the ID, the code, or even the textual name itself) the class has a little more flexibility. Below is the SimpleMapper class in its entirety:

The SimpleMapper Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.Entities.General;
using System.Data;

namespace DataAccess.Full
{
    public class SimpleMapper where T : SimpleData, new()
    {
        public static T Map(DataRow row, string idName, string textName, string valueName)
        {
            var creator = new T();
            int id = (int)row[idName];
            string text = (string)row[textName];

            string value = id.ToString();
            if (idName != valueName)
            {
                value = (string)row[valueName];
            }

            var obj = creator.Create(id, text, value);
            
            return (T)obj;
        }

        public static T[] Map(DataTable data, string idName, string textName, string valueName)
        {
            var entities = new List();
            foreach (DataRow row in data.Rows)
            {
                entities.Add(Map(row, idName, textName, valueName));
            }

            return entities.ToArray();
        }
    }
}

Implementing Data Access Interfaces and Stubbing

This article describes one approach on how to interface a data access layer to allow for multiple data access platforms including a stubbed data access layer. I’ll show how to link up a stubbed layer and an MS SQL layer which both utilize the same interface contract.

Sounds Great! What Are We Doing Again?

It sounds a bit more complicated than it really is. Sometimes, especially when you are working with a large project, or one that is very critical (core to the business), you may want to take some additional considerations in your data access architecture. In enterprise applications, or in applications where you may foresee wholesale changes in the data access in the future, you may want to create an interface for any data access to follow in case you decide to switch from SQL to Oracle for example.

Another good use is when you want to “stub” the data access layer. Stubbing is simply when you return fake hardcoded data that emulates what you would expect the data access layer to really return. At first, it sounds like stubbing doesn’t really do anything. But there are many purposes, stubbing allows you to split work between developers (or teams) between layers; allowing one developer to work on the business logic or presentation layer, while another team works on the data access layer. Stubbing is also useful for unit tests when you do not want to actually build up/tear down the database and simply emulate what you would expect to return from the database.

Not 100% convinced? Well, don’t feel alone, it took me a little while to accept there was any real return on developing stubbed layers; but trust me, there is. Hopefully, this will become more clear further down the artice.

The Solution

First, we need to set up the solution. Our solution is going to use several projects to logically (and potentially physically) seperate code into layers/tiers. The projects we will be working with our as follows:

  • P2.Business
  • P2.DataAccess
  • P2.DataAccess.MSSQL
  • P2.DataAccess.Stub
  • P2.Common.Entities

There are other projects within this solution, but for the purpose of this article they’re not referenced.

A Note About Business Entities

For the sake of brevity, I’m not going to go into the details of the business entities, simply note that when the proceeding code references a data type from Common.Entities this simply means there is a business entity created to frame the data into an object oriented view.

For more information on business entities you can view the following posts: Common Reusable Business Entities Part 1, Common Business Entities Part 2, and Common Business Entities Part 3.

Creating the Data Access Interface

First, we need to create an interface (contract) to tell our real data access layers how we want them to interact. In this demonstration, I will have a solution called “P2”. P2 will have the following projects:

The project P2.DataAccess is going to house all of our interface knowledge; P2.DataAccess.MSSQL and P2.DataAccess.Stub will implement the interfaces within P2.DataAccess. We’ll create an interface for working with our “products” first called IProductData.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using P2.Common.Entities.Ecommerce;

namespace P2.DataAccess
{
    public interface IProductData
    {
        bool SaveProduct(ref Product product);
        bool DeleteProduct(Product product);

        Product GetProductByID(int id);
        Product[] GetProducts();

        Product[] SearchProducts(string search);
        Product[] SearchProducts(bool posSearch, string search, bool posCategory, Category[] categories);
    }
}

This interface describes what the product data access code will be able to do, in this case, it will be able to save a product, delete a product, and retrieve products in a variety of ways. Each data access, regardless of platform, will need to implement this interface and address these concerns.

Creating the Stubbed Data Access Layer

Now, let’s create a “real” data access layer which implements this interface. Because the interface resides in a different project, make sure that the P2.DataAccess.Stub project references P2.DataAccess. Next, create a file called ProductData.cs as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using P2.Common.Entities.Ecommerce;
using P2.Common.Entities.General;

namespace P2.DataAccess.Stub
{
    public class ProductData : IProductData
    {
        #region IProductData Members

        public bool SaveProduct(ref Product product)
        {
            return true;
        }

        public bool DeleteProduct(Product product)
        {
            return true;
        }

        public Product GetProductByID(int id)
        {
            var product = new Product(id)
            {
                Name = "Cosmic Uamou",
                Summary = "A handpainted original from Tokyocube.",
                Description = "Tokyocube are very pleased to present a beautiful new custom series from Ayako Takagi called Cosmic Uamou. The series is made up of 7 hand-painted originals, each with its own unique colours and design. The series is based on the Buuts Buuts Uamou, and each figure is signed and exclusively available through Tokyocube.",
                Price = 12.50,
                Weight = .25,
                Width = 2,
                Height = 6,
                Length = 2,
                SKU = "CA001",
                LargeImage = new Image("1_l.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
                MediumImage = new Image("1_m.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
                Thumbnail = new Image("1_t.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
            };

            return product;
        }

        public Product[] GetProducts()
        {
            Product[] products = new Product[3];

            products[0] = new Product(1)
            {
                Name = "Cosmic Uamou",
                Summary = "A handpainted original from Tokyocube.",
                Description = "Tokyocube are very pleased to present a beautiful new custom series from Ayako Takagi called Cosmic Uamou. The series is made up of 7 hand-painted originals, each with its own unique colours and design. The series is based on the Buuts Buuts Uamou, and each figure is signed and exclusively available through Tokyocube.",
                Price = 12.50,
                Weight = .25,
                Width = 2,
                Height = 6,
                Length = 2,
                SKU = "CA001",
                LargeImage = new Image("1_l.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
                MediumImage = new Image("1_m.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
                Thumbnail = new Image("1_t.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
            };

            products[1] = new Product(2)
            {
                Name = "Mermaid",
                Summary = "Designed by Keiichi Tanaami.",
                Description = "Designed by Keiichi Tanaami, and uniquely produced in polystone, the Mermaid is a very special piece showing the potential of current production techniques with its superb detailing and immense quality. Produced to a run of just 200 worldwide, Keiichi Tanaami's Mermaid stands 19 cm tall.",
                Price = 10.50,
                Weight = .25,
                Width = 2,
                Height = 6,
                Length = 2,
                SKU = "CA002",
                LargeImage = new Image("2_l.jpg", "Mermaid", "Mermaid - Front"),
                MediumImage = new Image("2_m.jpg", "Mermaid", "Mermaid - Front"),
                Thumbnail = new Image("2_t.jpg", "Mermaid", "Mermaid - Front"),
            };

            products[2] = new Product(3)
            {
                Name = "Kaiju Blue",
                Summary = "Designed by Sun Guts.",
                Description = "Kaiju Blue release a new collection of art toys from Japanese designers Sun Guts, including variations of the Uraname and Suiko figures. All produced in Japan, and available as a limited run.",
                Price = 12.50,
                Weight = .25,
                Width = 2,
                Height = 6,
                Length = 2,
                SKU = "CA003",
                LargeImage = new Image("3_l.jpg", "Kaiju Blue", "Kaiju Blue - Front"),
                MediumImage = new Image("3_m.jpg", "Kaiju Blue", "Kaiju Blue - Front"),
                Thumbnail = new Image("3_t.jpg", "Kaiju Blue", "Kaiju Blue - Front"),
            };

            return products;
        }

        public Product[] SearchProducts(string search)
        {
            Product[] products = new Product[0];
            products[0] = new Product(1);

            return products;
        }

        public Product[] SearchProducts(bool posSearch, string search, bool posCategory, Category[] categories)
        {
            Product[] products = new Product[0];
            products[0] = new Product(1);

            return products;
        }

        #endregion
    }
}

There are a couple of things to notice in this code example. First, I am including the mapping of data to an entity in my data access layer; some people may do this in the business logic layer and return datatables/datasets or some other generic DTO (data transfer object) from the data layer. Because my objects have very little if any business logic, they are essentially DTO’s. Second, everything is hardcoded with actual values and there is no actual database interaction – this is what makes this a “stub”. The results are always the same, and can be unit tested against with certainty of the expected result. Finally, on a non-technical note, my products all have pretty unique names and descriptions. I found these while looking at e-commerce websites and got a kick out of their products. Here are a few of their product images:

Article Intermission

tokyo cube ex. 1
tokyo cube ex. 2
tokyo cube ex. 3
Visit Tokyo Cube for more…

Creating the MSSQL Data Access Layer

Let’s now create the MSSQL layer. This project will also reference the main P2.DataAccess project and implement the same interface as before:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using P2.Common.Entities.Ecommerce;
using P2.Common.Utilities;
using System.Data;

namespace P2.DataAccess.MSSQL
{
    public class ProductData : IProductData
    {
        #region Mappers
        
        //TODO: Removed for brevity.

        #endregion

        #region IInventoryData Members

        public bool SaveProduct(ref Product product)
        {
            const string sql = "SaveProduct";
            var parameters = new SqlHelperParameter[]
            {
                new SqlHelperParameter("Product", product.ID),
                new SqlHelperParameter("Name", product.Name)
            };

            //Create product
            object id = null;
            var idColumn = "ProductID";
            var result = SqlHelper.ExecuteNonQuery(ConnectionManager.Databases.P2_Ecommerce, sql, parameters, ref id, idColumn);

            product.ID = Convert.ToInt32(id);
            return result;
        }

        public bool DeleteProduct(Product product)
        {
            const string sql = "DeleteProduct";
            var parameters = new SqlHelperParameter[]
            {
                new SqlHelperParameter("Product", product.ID),
            };

            var result = SqlHelper.ExecuteNonQuery(ConnectionManager.Databases.P2_Ecommerce, sql, parameters);

            return result;
        }

        public Product GetProductByID(int id)
        {
            const string sql = "GetProductByID";
            var parameters = new SqlHelperParameter[]
            {
                new SqlHelperParameter("ProductID", id)
            };

            DataRow row = null;
            var data = SqlHelper.RetrieveDataTable(ConnectionManager.Databases.P2_Ecommerce, sql, parameters);
            if (data.Rows.Count > 0)
            {
                row = data.Rows[0];
            }

            return MapDataToProduct(row);
        }

        public Product[] GetProducts()
        {
            const string sql = "GetProducts";

            var data = SqlHelper.RetrieveDataTable(ConnectionManager.Databases.P2_Ecommerce, sql);
            return MapDataToProducts();
        }

        public Product[] SearchProducts(string search)
        {
            const string sql = "SearchProducts";
            var parameters = new SqlHelperParameter[]
            {
                new SqlHelperParameter("Search", search)
            };

            var data = SqlHelper.RetrieveDataTable(ConnectionManager.Databases.P2_Ecommerce, sql, parameters);
            return MapDataToProducts(data);
        }

        public Product[] SearchProducts(bool posSearch, string search, bool posCategory, Category[] categories)
        {
            //TODO: Leaving out for brevity.
            throw new NotImplementedException();
        }

        #endregion
    }
}

Note: I like to use a SQL Helper class that I’ve written, therefore the code above does not look like normal ADO.NET. The actual code for the data access is not as important as the concept that we’ve seperated two versions of the data access layers which are tied together by a common interface.

Now, Let’s Tie It All Together in the Business Layer

The final piece to the puzzle is the business layer, where we want to give it the power to decide which data layer to execute. We can do this several ways but one that I have found really useful is by specifying the desired data access in the business logic constructor. We can do this in the following way:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using P2.DataAccess;
using P2.Common.Entities.Ecommerce;

namespace P2.Business.Ecommerce
{
    public class ProductLogic
    {
        private IProductData _data;

        public ProductLogic() : this(new P2.DataAccess.Stub.ProductData())
        {
        }

        public ProductLogic(IProductData data)
        {
            _data = data;
        }

        public Product[] GetProducts()
        {
            return _data.GetProducts();
        }
    }
}

Notice that the constructor allows the caller to specify what type of Data Access to use. An example of this call could be:

var logic = new ProductLogic(new P2.DataAccess.Stub.ProductData());
var products = logic.GetProducts();

This call would get products from the data access stub. In a later post, I will demonstrate how to default a data access layer with a blank constructor using reflection. Normal usage of code could use the parameterless constructor and specify the default, while unit testing can specifically call the stub to avoid build up/tear down of the database.

And there you have it, the business logic now has total control of calling down to the desired data access and the data access layer becomes platform independent by implementing a common interface. Hope this was helpful, happy coding!

A Tale of Two Architectures

At times, software development can be a complicated task, especially when developing enterprise-level systems with heavy load or critical functionality. It is because of this, that software architecture has become increasingly more important and refined. There are now so many types of software architectural models, that choosing the most appropriate solution is a skill in itself.

In this article, I would like to demonstrate two fairly generic architectures that I prefer to use for enterprise applications which require a decent amount of complexity and the flexibility to be easily maintained and updated in the future. There are of course pro’s and con’s to each of these architecture models, which we will discuss later in this article.

Both architectures take into account the following requirements:

  • Logical seperation between functional purpose.
  • Reuseable code.
  • Flexible for maintenance/upgrades.
  • Tiers are able to be distributed across several servers.

Objects as Actors

Objects as Actors Architecture

First, to clarify what I mean by “actors”: an actor as I am using it refers to the code that executes behavior. In the case of objects as actors, the behavior is derived from the objects (business entities). This architecture is a hybrid and borrows concepts from several official architectural patterns such as Layered Architecture and OOAD.

As you can see, the layers are split into four major areas; data access, business logic, presentation, and common/crosscut. In this case, the business logic layer contains both business logic (such as validation, computations, etc.) and business entities. Business entities become the main section of code that all interaction with the presentation layer derives from.

Logic and entities are tightly coupled because they are packaged together in the same layer. This is more closely related to object oriented design, where everything is object-centric. Now, many of you may have read the words “tightly coupled” and were turned off; but, choosing this architecture means you are deciding that if a business entity structure changes, the business logic will need to change as well. In most real-world cases, this is true, so tightly coupling is not as negative as you may expect.

Additionally in the architecture, there is are common assemblies that cross across all layers such as Utilities (ex: extension methods), Communication (such as between layers on multiple servers), and security.

The presentation layer could include any form of presentation platform such as Web, Windows, Mobile, Silverlight, etc. The primary point is that as much as possible, whichever presentation is in use, or if multiple presentations exist, all code interacts with a common point, the business entities.

Optionally, you can implement a services layer between the business and presentation layers to lean closer to a service-oriented architecture.

Presentation-Level Code Interaction Example

Let’s look at a short example of how you would interact with the entities from any layer above business logic. This code creates a “person” entity, assigns a first and last name in the constructor, and then saves the person to the database.

var person = new Person("John", "Smith");
person.Save();

Business Logic as Actors

Business Logic as Actors Architecture

The main differece between this architecture and “Objects as Actors” is that business entities are moved into the common crosscut section and treated more as data structures (thin vs. thick entities).

The interaction with these business entities occur through calls to the business logic layer directly instead of the entity. The advantages of this are that entities are lighter, changes in the structure do not have to be changed in the business tier, and the objects tend to conform better to WCF.

The drawbacks are that entities no longer have structured behavior of what they can and cannot do. You use another class (in the business logic layer) to define and execute the actiosn of business entities – the business logic actor.

Presentation-Level Code Interaction Example

Our same “person” example using the Business Logic Actor. The MembershipActor contains a list of possible functions to do with various membership-related logic (such as saving Person).

var person = new Person("John", "Smith");
var membershipActor = new MembershipActor();
membershipActor.SavePerson(person);

Additional Resources

While in the middle of writing this article, I came across the Application Architecture Guide 2.0 by Microsoft Patterns and Practices. You can download the complete guide from Codeplex here. Kudos to Microsoft for putting this out for free!

Closing Notes

Various points on architecture are debateable as to what is good or bad; mostly because it depends so heavily on the individual situation. However, I hope after you’ve read this article you will leave your feedback as to what you agree, and more importantly, disagree about the above architectural models.

Happy coding!

Developing ASP.NET Custom Controls

You can do a lot of home improvement with a hammer, screwdriver, and tape measurer; however, certain jobs require more specialized tools. Programming is no different. You can do almost anythign with the base toolset provided by Microsoft, but sometimes, to make life easier and code cleaner, developing your own Custom Controls are the way to go.

I’ll discuss how to develop ASP.NET custom controls by inheriting from an existing control and providing a bit of additional functionality. This is probably the easiest way to begin writing custom controls and can be very useful in your projects.

MyButton: A Better .NET Button

Let’s take a fairly simple example that can be used in nearly every web application you develop, the noble button. The button already comes in several flavors through .NET, such as a LinkButton, ImageButton, and the standard Button. However, it does have a few drawbacks that we’re going to aim to correct with our own custom button called MyButton.

The first drawback is that, by default, a button can be clicked several times causing a postback each time you click the button. For most pages, this is a non-issue because they’re fast enough the user does not have time to “double-post”. This does occur, however, when a long process runs in codebehind wired against the button being clicked. We’ll attempt to provide functionality so once the button is clicked, it will be disabled from the user to prevent multiple triggers of the button.

Next, we’ll want to fix a common problem when you request client-side confirmation of the button (javascript confirm() function), and even upon cancelling (negating the confirmation), the button posts regardless.

Finally, we’ll provide the ability to use a button as a simple redirecting url without causing a postback.

View the demo and see what we’re getting into ahead of time.

The MyButton Class

We’ll want to begin by create a new C# Class Library Project called CustomControls. Once you’ve created this, you’ll want to add a reference to System.Web, since, by default, class libraries do not reference web-specific controls. We can later add this class library reference to any of our projects which would like to consume the MyButton (and all other custom controls you may create).

Below is the MyButton Class in it’s entirety. I’ve added a number of properties to enhance the standard button (which as you can see, it is inherited from), such as:

  • CSSClass

    Specifies the CSS class of the button in a normal/default state. Overridden to provide a default CSSClass to match built in CSS. By default, this is MyButton_button.

  • DisabledCSSClass

    Specifies the CSS class of the button when it is in a disabled state. This is only visible if DisableButtonOnClick is true (default). By default, this is MyButton_buttonDisabled.

  • DisableButtonOnClick

    Disables the button after it is clicked. By default, this is true.

  • DisableBuiltInCSS

    Disables the built in CSS reference to be included (both in the control and on the page). By default, this is false.

  • DisableBuiltInClientSideCode

    Disables the built in clientside (javascript) code to be included (both in the control and on the page). By default, this is false.

  • ControlPath

    Specifies the path to the control external files from the current file location. By default, this is ~/Controls/MyButton/.

  • IsSimpleRedirect

    Specifies whether this button acts like a simple redirect (by calling window.location).

  • AutoPostBack

    Specifies whether the control will cause a postback. By default, this is true. If IsSimpleRedirect is true, AutoPostBack will be overridden as false.

  • RequiresConfirmation

    Specifies whether the button will show a confirmation dialog box and require the user to confirm before submitting. By default, this is false.

  • ConfirmationText

    If RequiresConfirmation is true, this specifies the text that will be shown to confirm the button click action. By default, this reads “Are you sure you want to proceed?”

The button will implement some javascript and have default CSS packaged with it to assist in the increased functionality and look a bit nicer. These files could have been made as embedded resources, but to make them easy to customize for each project, and to utilize resource caching, they’ve been maintained as external files and placed in a ControlPath directory, which is configurable.

using System;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Web.UI.HtmlControls;

namespace CustomControls
{
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.Demand,
Level=AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,
Level=AspNetHostingPermissionLevel.Minimal),
DefaultProperty("Text"),
ToolboxData("<{0}:MyButton runat=\"server\"></{0}:MyButton>")]
public class MyButton : Button
{
/// <summary>
/// The default css filename for the control.
/// </summary>
const string baseCssFileName = "MyButton.css";

/// <summary>
/// The default javascript filename for the control.
/// </summary>
const string baseJSFileName = "MyButton.js";

/// <summary>
/// Flag to determine if the css tag has already been added or not.
/// (so the css reference isn't added every time a control exists on the page.)
/// </summary>
private bool cssTagIsAdded = false;

/// <summary>
/// Flag to determine if the js script tag has already been added or not.
/// (so the script tag isn't added every time a control exists on the page.)
/// </summary>
private bool jsTagIsAdded = false;

/// <summary>
/// Assign a CSS class to the control.
/// </summary>
[Category("Custom"), Description("The css class to apply to this button.")]
public override string CssClass
{
get
{
if(string.IsNullOrEmpty(_cssClass))
{
_cssClass = "MyButton_button";
}

return _cssClass;
}
set
{
_cssClass = value;
}
}
private string _cssClass;

/// <summary>
/// Assign a CSS class to the control when the control is in a disabled state.
/// </summary>
[Category("Custom"), Description("The css class to apply after the button is clicked.")]
public string DisabledCssClass
{
get
{
if(string.IsNullOrEmpty(_disabledCssClass))
{
_disabledCssClass = "MyButton_buttonDisabled";
}

return _disabledCssClass;
}
set
{
_disabledCssClass = value;
}
}
private string _disabledCssClass;

/// <summary>
/// Confirmation text to be displayed if the button requires confirmation.
/// </summary>
[Category("Custom"), Description("The confirmation question to be asked when the button is clicked and requires confirmation.")]
public string ConfirmationText
{
get
{
if(string.IsNullOrEmpty(_confirmationText))
{
_confirmationText = "Are you sure you want to proceed?";
}

return _confirmationText;
}
set
{
_confirmationText = value;
}
}
private string _confirmationText;

/// <summary>
/// The path to the external javascript, css, and image resources the defaults for this control uses.
/// </summary>
[Category("Custom"), Description("The path to the external javascript, css, and image resources the defaults for this control uses.")]
public string ControlPath
{
get
{
if(string.IsNullOrEmpty(_controlPath))
{
_controlPath = "~/Controls/MyButton/";
}

if (!_controlPath.EndsWith("/"))
{
_controlPath += "/";
}

return _controlPath;
}
set
{
_controlPath = value;
}
}
private string _controlPath;

/// <summary>
/// Determines whether to use the built in css file and page reference for the control.
/// By default, this is false.
/// </summary>
[Category("Custom"), Description("Determines whether to register a css class at the top of the html.")]
public bool DisableBuiltInCSS
{
get
{
if (_disableBuiltInCSS == null)
{
_disableBuiltInCSS = false;
}

return (bool)_disableBuiltInCSS;
}
set
{
_disableBuiltInCSS = value;
}
}
private bool? _disableBuiltInCSS;

/// <summary>
/// Determines whether to use the built in javascript file and page reference for the control.
/// By default, this is false.
/// </summary>
[Category("Custom"), Description("Determines whether to register an external clientside file to the html.")]
public bool DisableBuiltInClientSideCode
{
get
{
if (_disableBuiltInClientsideCode == null)
{
_disableBuiltInClientsideCode = false;
}

return (bool)_disableBuiltInClientsideCode;
}
set
{
_disableBuiltInClientsideCode = value;
}
}
private bool? _disableBuiltInClientsideCode;

/// <summary>
/// Determines whether to run built in javascript code to disable the button after being clicked to
/// prevent multiple postbacks by users.
/// By default, this is true.
/// </summary>
[Category("Custom"), Description("Determines whether to disable the button after click or not.")]
public bool DisableButtonOnClick
{
get
{
if (_disableButtonOnClick == null)
{
_disableButtonOnClick = true;
}

return (bool)_disableButtonOnClick;
}
set
{
_disableButtonOnClick = value;
}
}
private bool? _disableButtonOnClick;

/// <summary>
/// Determines whether this button simply acts as a clientside redirect.
/// </summary>
[Category("Custom"), Description("Determines whether this button simply acts as a clientside redirect.")]
public bool IsSimpleRedirect
{
get
{
if (_isSimpleRedirect == null)
{
_isSimpleRedirect = false;
}

return (bool)_isSimpleRedirect;
}
set
{
_isSimpleRedirect = value;
}
}
private bool? _isSimpleRedirect;

/// <summary>
/// Redirect URL for simple clientside redirect.
/// </summary>
[Category("Custom"), Description("Redirect URL for simple clientside redirect.")]
public string RedirectURL { get; set; }

/// <summary>
/// Determines whether clicking the button requires a confirmation.
/// </summary>
[Category("Custom"), Description("Determines whether clicking the button requires a confirmation.")]
public bool RequiresConfirmation { get; set; }

/// <summary>
/// Determines whether this control should cause a postback.
/// </summary>
[Category("Custom"), Description("Should this control cause a postback?")]
public bool AutoPostBack
{
get
{
if (_autoPostBack == null)
{
_autoPostBack = true;
}

return (bool)_autoPostBack;
}
set
{
_autoPostBack = value;
}
}
private bool? _autoPostBack;

protected override void OnPreRender(EventArgs e)
{
//Embed javascript tag
if (this.DisableBuiltInClientSideCode == false && jsTagIsAdded == false)
{
string jsPath = this.ControlPath.Replace("~", Context.Request.ApplicationPath.TrimEnd('/')) + baseJSFileName;
HtmlGenericControl js = new HtmlGenericControl("script");
js.Attributes["type"] = "text/javascript";
js.Attributes["src"] = jsPath;
this.Page.Header.Controls.Add(js);

jsTagIsAdded = true;
}

//Embed css tag
if (this.DisableBuiltInCSS == false || (string.IsNullOrEmpty(this.CssClass) && this.CssClass != baseCssFileName) && cssTagIsAdded == false)
{
string cssPath = this.ControlPath.Replace("~", Context.Request.ApplicationPath.TrimEnd('/')) + baseCssFileName;
HtmlLink cssLink = new HtmlLink();
cssLink.Href = cssPath;
cssLink.Attributes.Add("rel", "stylesheet");
cssLink.Attributes.Add("type", "text/css");
this.Page.Header.Controls.Add(cssLink);

cssTagIsAdded = true;
}

base.OnPreRender(e);
}

protected override void Render(HtmlTextWriter writer)
{
StringBuilder sb = new StringBuilder();

//If simple redirect and redirect url is provided add clientside redirect without postback
if (this.IsSimpleRedirect && !string.IsNullOrEmpty(this.RedirectURL))
{
//Add javascript to redirect page
sb.Append("window.location='");
sb.Append(this.RedirectURL);
sb.Append("';");

this.UseSubmitBehavior = false;
this.AutoPostBack = false;
}

//If not a confirmation button, disables button to prevent double postbacks
if (this.DisableButtonOnClick && this.RequiresConfirmation == false)
{
sb.Append("doDisable('");
sb.Append(base.ClientID);
sb.Append("', '");
sb.Append(this.DisabledCssClass);
sb.Append("', ");
sb.Append(this.AutoPostBack.ToString().ToLower());
sb.Append(");");
}
if (!string.IsNullOrEmpty(this.OnClientClick))
{
sb.Append(this.OnClientClick);
}

//If requires confirmation, else if clientside button only and no postback should occur
if (this.RequiresConfirmation)
{
this.UseSubmitBehavior = false;
this.AutoPostBack = false;

sb.Append(string.Format("var val = confirm('{0}'); if(val) __doPostBack('{1}','');", this.ConfirmationText, this.ClientID));
}
else if ((this.IsSimpleRedirect && !string.IsNullOrEmpty(this.RedirectURL)) || this.AutoPostBack == false)
{
//Add javascript to disable the postback that occurs on ASP.NET buttons
sb.Append("return false;");
}

writer.AddAttribute("class", this.CssClass);
writer.AddAttribute("onclick", sb.ToString());

base.Render(writer);
}
}
}

The Render method is where most of the logic occurs based on the properties provided to the control. The Render method is where I’m injecting the Javascript to help with much of the new functionality.

Miscellaneous MyButton Files

As mentioned, I’m using external files for the javascript and CSS. Here are the files as I currently have them. They are completely open to be configured to add additional features or customize based on project.

Client-side code (javascript) – MyButton.js

/*
Set's timer to call doDisableButton() after it is clicked, the reason this fires instead of directly
calling doDisableButton() is because a disabled control will not postback, so there needs to be a
slight delay.

param id = the id of the button being disabled.
param css = the css class being used for the disabled state (visual appearance).
param submit = bool flag whether to submit form or not
*/
function doDisable(id, css, submit)
{
if(submit)
{
//document.forms[0].submit();  //removed to allow AJAX calls
document.getElementById(id).click();
}

window.setTimeout("doDisableButton('" + id + "', '" + css + "')", 0);
}

/*
Disables the button and sets the button's css class to a disabled state.

param id = the id of the button being disabled.
param css = the css class being used for the disabled state (visual appearance).
*/
function doDisableButton(id, css)
{
var obj = document.getElementById(id);
obj.disabled = true;
obj.className = css;
}

CSS – MyButton.css

/*The button control - normal state*/
.MyButton_button
{
background-color: #000033;
background-image: url("bg_nav.gif");
background-repeat: repeat;

font-size: 10px;
font-weight: bold;
color: #FFFFFF;

border: 1px solid;
padding-top: 2px;
padding-bottom: 2px;

cursor: pointer;
}

/*The button control - hover state*/
.MyButton_button:hover
{
/*background-image: url("bg_nav_red.gif"); -- Uncomment this when we have a hover state to add by default*/
color: #FFFFCC;
}

/*The button control - after clicked if disable button is turned on*/
.MyButton_buttonDisabled
{
background-color: #CCC;
background-image: none;

font-size: 10px;
font-weight: bold;
color: #FFFFFF;

border: 1px solid;
padding-top: 2px;
padding-bottom: 2px;

cursor: pointer;
}

You’ll notice in the CSS there is also a hover state. I added this to demonstrate that even though there is not an explicit CSS class name property in the control for hoverstate, additional flexibility can be included through the CSS.

These files will need to reside in the application which is consuming your custom control, so you will need to create a Controls\MyButton directory (or similar if pointing to another directory through the ControlPath property). I find it also useful to create this in the C# class library project to maintain the original files even thought they are not technically used from there.

You will want to also include any graphics that you are calling from your CSS file in this directory as well. In this case, we have two, one for each state of the button.

Let’s Take it for a Spin

Let’s begin by setting up a quick demo page using our new custom button control. The following is a couple of scenarios that highlight the use of the custom button as opposed to the regular button you would normally use.

Feel free to modify the code as much as you like to provide a working template of various button uses in your own applications.

<asp:Label ID="TimeLabel" runat="server"></asp:Label><br />
<p>The above label helps to demonstrate whether a postback is ocurring or not in the following examples.</p>

<hr />
<cc1:MyButton ID="MyButton1" runat="server" Text="Plain Button" /><br />
<p>This is the default button which causes a server-side post back.</p>

<hr />
<cc1:MyButton ID="MyButton2" runat="server" Text="Confirmation Button" RequiresConfirmation="true" ConfirmationText="Are you sure you like waffles?" /><br />
<p>This button causes a server-side post back if the confirmation question is answered positively.  If not confirmed, no postback will occur fixing the ASP.NET button issue for posting upon not confirmed response.</p>

<hr />
<cc1:MyButton ID="MyButton3" runat="server" Text="Redirect to Google" IsSimpleRedirect="true" RedirectURL="http://www.google.com" /><br />
<p>This button acts to redirect to a url.  It does not cause a postback to occur.</p>

<hr />
<cc1:MyButton ID="MyButton4" runat="server" Text="Clientside Hello World - No Postback" AutoPostBack="false" OnClientClick="alert('Hello World');" DisableButtonOnClick="false" /><br />
<p>This button does not cause a postback and instead fires any clientside code.  In this example, a hello world alertbox.</p>

<hr />
<cc1:MyButton ID="MyButton5" runat="server" Text="No Double Postback" /><br />
<p>This button demonstrates the default behavior of disabling the button after clicking it, to eliminate a double-post back scenario on long server-side processing.  A 2 second sleep is called when clicking this button.</p>

Don’t forget to register your new control on your page like the following: (Note: This may be in a different namespace depending on where you’ve created the custom controls project.)

<%@ Register Assembly="CustomControls" Namespace="CustomControls" TagPrefix="cc1" %>

We’ll also need just a slight bit of code behind (written in VB) to help highlight the features we’re demonstrating. The code here is not necessary for using the buttons.

First, we are going to assign the current timestamp to the TimeLabel label everytime the page loads so we can easily track when a postback is ocurring and when it is not. Second, for our example #5, “No Double Postback”, we’re going to add a time delay so we can show the button being disabled and disallowing users to click causing multiple postbacks during lenthy server-side processes.

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
TimeLabel.Text = Now()
End Sub

Protected Sub MyButton5_Click(ByVal sender As Object, ByVal e As EventArgs) Handles MyButton5.Click
Threading.Thread.Sleep(2000)
End Sub

At this point, you should have a completely functioning collection of demo’s for using the MyButton custom control and a pretty good idea on how to begin developing your own custom controls.

Keep in mind, in this scenario, we’ve built custom code onto a pre-existing control (the ASP.NET button control); however, if we need even greater customization, we can create custom controls from scratch, a topic I’ll attempt to cover down the road.

Good luck in your next custom coding control project, and please leave any suggestions or questions in the comments below!

Southwest Florida Code Camp 2009

Southwest Florida Code Camp 2009

Development in my neck of the woods seems to be growing and it is evidenced by the 2nd annual Southwest Florida .NET Code Camp on October 3rd, 2009. Code Camp will be held at Florida Gulf Coast University in Fort Myers, FL and is sure to turn out a substantial crowd of developers as well as a great collection of distinguished speakers.

If you’re interested in attending the Southwest Florida Code Camp, head over to to find out more and sign up for free.

Code Camp should cover several different topics in various tracks including the following:

  • .NET 4.0 Entity Framework
  • Be Productive with MVC: Telerik Open Source Extensions for ASP.NET MVC
  • Bing Map Web Control API
  • Building Windows Mobile Widgets
  • Cloud Computing: What you can do now to move your software infrastructure online
  • Code Style & Standards
  • Enterprise Library and Unity Tips & Techniques
  • Game Programming on Windows Mobile
  • Introduction to DotNetNuke 5
  • Introduction to DotNetNuke 5 Widgets

Preparing for Code Camp

Before attending an educational seminar such as Code Camp, I prefer to do a bit of research on the topics presented. Most lectures provide a good introduction to the topic, so it’s not critical to “study-up”, but I’ve found that you get the most “bank for your buck” when you come in knowing a little already.

Additionally, a great way to understand and retain the information from Code Camp is to come with a specific project in mind and ask yourself “How can this subject make my project better?”. At least in my case, I tend to retain much more by applying the topic to a real life application.

A Few Links to Help You Prep

Job Hunting

For those of you developers that have been hit hard by the current economic market, Code Camp acts as a double wammy to keep you sharp and build on your current skillset as well as being a forum to meet recruiters, fellow developers, and potentially, your next job opening. Now’s the time to network and Code Camp is the perfect place to do so. Hope to see you there!

Battle of the Third Party Controls

For those of you local to the Southwest Florida area and don’t know, there is a nice little .NET user group for developers, hobbyists, managers/owners, and various other IT-centric folks. The developer group is very welcoming to new visitors, always provide a great array of topics, and if you’re lucky, you might even get a bit of “swag” for attending one of the free meeetings.

On March 18th, 2009, the Naples .NET Usergroup put on a Battle of the Third Party Controls session which consisted of developers presenting on third party development controls they’ve used in their own projects. In the ring – Telerik, Infragistics, DevExpress, and FarPoint.

Here’s a quick recap:

Infragistics

Round 1: Infragistics

Well, Infragistics was battered, bloody, and bruised from the start. John Dunagan took his best shots at a set of developer controls that really didn’t want to play nice. Obviously, sometimes things happen with systems, user error, and Murphy’s Law; but too much went wrong to make Infragistics even a contender in this battle.

John pointed out difficulties while installing, references not automatically being added properly when dragging controls from the Visual Studio toolbox, and gasp, Visual Studio crashed while importing Infragistic Themes into the project during the demonstration. John had a lot to contend with.

Grade: D-

It was over when…

Unfortunately, before it began – and if you want more specifics, I pronounced it dead when we viewed source and saw a mindnumbing, mouse scrolling nightmare of Viewstate, excessive inline styles, and what looked like poor auto-generated HTML throughout a very basic UI sample.

Telerik

Round 2: Telerik

Telerik on the other hand, came out with gold trimmed, bright white trunks and a mean look ready for a fight. Philip Lippard had a pretty cushy job with Telerik providing a great source of pre-built, fully interactive examples publicly available on their website (http://www.telerik.com). Phil did a nice job demonstrating the controls, and with Telerik’s one-two punch of very attractive, and much lighter (relatively speaking) controls, Telerik was the star of the show and the favorite among developers.

Grade: B+, Though Telerik is a strong control suite, and a personal preference in most cases, I do hold criticism of some of the controls. For example, the Charting controls I strongly dislike and favor Component Art, a control company not featured in this battle.

It was over when…

Phil typed http://www.telerik.com into the address bar. Telerik does a great job with their website as well as their controls, and the marketing really shows through (in a good way).

DeveloperExpress

Round 3: DevExpress

Presented by Amy Hatfield, a darkhorse for me personally, DevExpress really impressed. Like a middleweight fighter moving up in the ranks, DevExpress was not as robust as Telerik, but certainly showed a lot and is a solid choice for a control suite. Amy did a very nice job showing off a lot of code-less (minimal) wiring of the grid control; featuring Ajax, paging, and detail template binding with little to no codebehind required.

I certainly intend on looking a little harder at DevExpress in the future.

Grade: B-

It was over when…

If this was a fight, it would have ended in a judges decision. Nothing knocked out DevExpress by any means, and is a viable scrapper through and through.

Farpoint Spread

Round 4: Farpoint

If the other three suites were fighters brawling it out, Farpoint would have been the bookie – involved, but not quite in the competition.

Farpoint was ready to go, energetic, and full of zest; but competing in a completely different arena than the other controls. Farpoint, as it turns out, is a Spreadsheet control that does one thing and does it well. Opening existing excel documents (regardless of version) seems to be a breeze, working interactively with bound or empty workbooks is smooth, and you should have no problem importing, exporting, or printing spreadsheet objects.

We were fortunate enough to have a presenter all the way from Raleigh, North Carolina, and I applaud his effort in presenting.

Grade: Not eligible. Unfortunately it’s just not a sound comparison with the other controls. Certainly I see some benefit to the control, but at the same time, I personally was not blown away by the product. For Office-heavy users who need an online spreadsheet solution, it sounds like the way to go.

It was over when…

For me, it was over when I realized it was just a spreadsheet. I have not come across a lot of client requests where they simply want an excel document or template online to do calculations/forms etc. For me, I would much rather relegate that to an HTML form with calculations occuring in code and presented with more of a “face” to it, rather than an older, spreadsheet-style UI.

It was a fun night, a good battle, and great people to share knowledge and viewpoints with. If you happen to be in the area, check out http://www.naplesdot.net for more information on future meetings.

Channel 9

P.S. – interviews with the presenters will be featured on Channel 9 by Russ Fustino from Microsoft. Check it out here!

Common Reusable Business Entities Part 3

This is part two of an ongoing series on common reusable business entities. This series does not need to be read in order, but here are the preceeding posts if you would like to catch up:

This will be the final article in a three part series regarding Common Business Entities. The original hope was to provide a a few common business objects that are used across most business-style applications. Additionally, I hoped to demonstrate at least a skeleton of business entitities that those unfamiliar with custom object creation could begin playing with.

In this article, we’ll cover a few important numbers; such as the CreditCardNumber, SocialSecurityNumber, and PhoneNumber objects. The structures do not have a lot of business logic (such as validation), because I am attempting to demonstrate very lean entities (as recently seen in WCF-style applications) and leave the logic up to the business layer to be called upon. If you chose to use these objects in your own application, please ensure you add validation as you could see unexpected results.

So let’s get to it:

CreditCardNumber Structure

public struct CreditCardNumber
{
    #region "PROPERTIES"

    /// <summary>
    /// The type of credit card (VISA, Mastercard, etc.).
    /// </summary>
    public enum CreditCardType
    {
        VISA,
        Mastercard,
        AmericanExpress,
        Discover
    }

    /// <summary>
    /// The credit card number without any formatting.
    /// </summary>
    public string Value { get; set; }

    /// <summary>
    /// The type of credit card (VISA, Mastercard, etc.).
    /// </summary>
    public CreditCardType Type { get; set; }

    /// <summary>
    /// The secure credit card CVV number.
    /// </summary>
    public int? CVV { get; set; }  //123

    /// <summary>
    /// The expiration month of the credit card as a two digit integer.
    /// </summary>
    public int ExpirationMonth { get; set; }  //12    //NOTE: This could be an enum or it's own object (Month), but for simplicity I am just showing an integer

    /// <summary>
    /// The expiration year of the credit card as a four digit integer.
    /// </summary>
    public int ExpirationYear { get; set; }  //2009

    /// <summary>
    /// The masked credit card number.
    /// </summary>
    public string Masked  //XXXXXXXXXXXX1234
    {
        get
        {
            string result = string.Empty;

            if (!string.IsNullOrEmpty(Value))
            {
                int length = Value.Length;
                string suffix = Value.Substring(length - 4, 4);
                return string.Format("XXXXXXXXXXXX{0}", suffix);
            }

            return result;
        }
    }

    #endregion

    #region "CONSTRUCTORS"

    /// <summary>
    /// Create a new credit card number.
    /// </summary>
    /// <param name="value"></param>
    /// <param name="expirationMonth"></param>
    /// <param name="expirationYear"></param>
    /// <param name="creditCardType"></param>
    public CreditCardNumber(string value, int expirationMonth, int expirationYear, CreditCardType creditCardType) : this()
    {
        Value = value;
        ExpirationMonth = expirationMonth;
        ExpirationYear = expirationYear;
        this.Type = creditCardType;
    }

    /// <summary>
    /// Create a new credit card number.
    /// </summary>
    /// <param name="value"></param>
    /// <param name="expirationMonth"></param>
    /// <param name="expirationYear"></param>
    /// <param name="creditCardType"></param>
    /// <param name="cvv"></param>
    public CreditCardNumber(string value, int expirationMonth, int expirationYear, CreditCardType creditCardType, int? cvv) : this()
    {
        Value = value;
        ExpirationMonth = expirationMonth;
        ExpirationYear = expirationYear;
        this.Type = creditCardType;
        CVV = cvv;
    }

    #endregion
}

SocialSecurityNumber Structure

public struct SocialSecurityNumber
{
    #region "PROPERTIES"

    /// <summary>
    /// The social security number without any formatting.
    /// </summary>
    public string Value { get; set; } //123121234

    /// <summary>
    /// A masked version of the social security number.
    /// </summary>
    public string Masked  //XXX-XX-1234
    {
        get
        {
            string result = string.Empty;

            if(Value.Length == 9)
            {
                result = string.Format("XXX-XX-{0}", Value.Substring(4, 4));
            }

            return result;
        }
    }

    #endregion

    #region "CONSTRUCTORS"

    /// <summary>
    /// Create a new social security number.
    /// </summary>
    /// <param name="value"></param>
    public SocialSecurityNumber(string value)
    {
        Value = value;
    }

    #endregion
}

PhoneNumber Structure

public struct PhoneNumber
{
    #region "PROPERTIES"

    /// <summary>
    /// The phone number without any formatting.
    /// </summary>
    public string Value { get; set; } //2395551234

    /// <summary>
    /// The areacode of the phone number.
    /// </summary>
    public string Areacode //239
    {
        get
        {
            string result = string.Empty;

            if(Value.Length == 10)
            {
                result = Value.Substring(0, 3);
            }

            return result;
        }
    }

    /// <summary>
    /// The prefix of the phone number.
    /// </summary>
    public string Prefix //555
    {
        get
        {
            string result = string.Empty;

            if(Value.Length == 10)
            {
                result = Value.Substring(3, 3);
            }

            return result;
        }
    } 

    /// <summary>
    /// The suffix of the phone number.
    /// </summary>
    public string Suffix //1234
    {
        get
        {
            string result = string.Empty;

            if(Value.Length == 10)
            {
                result = Value.Substring(6, 4);
            }

            return result;
        }
    } 

    /// <summary>
    /// The masked phone number.
    /// </summary>
    public string Masked //(XXX)XXX-1234
    {
        get
        {
            string result = string.Empty;

            if(Value.Length == 10)
            {
                result = string.Format("(XXX)XXX-{0}", Suffix);
            }

            return result;
        }
    }

    #endregion

    #region "CONSTRUCTORS"

    public PhoneNumber(string value) : this()
    {
        Value = value;
    }

    #endregion

    #region "METHODS"

    public override string ToString()
    {
        return string.Format("({0}){1}-{2}", Areacode, Prefix, Suffix);
    }

    #endregion
}

One Very Important Note

I’ve mentioned it already, but please note that in an effort to make these lean, I have not really added any validation to the entities. This is most certainly something you will need to add, perhaps in the entity itself, a business layer, or wherever you see fit to add this validation. You will certainly get unexpected results without validating your entities. Also, it may be appropriate to throw exceptions when attempting to retrieve properties that are dependent on another property that is not filled out. Here, I’ve simply returned an empty string in most cases, so it depends on your preferential usage.

As always, please feel free to add any comments about entities, best practices, code standards, structure, pro’s and con’s of logic within entities, and any other goodies you would like to contribute. Good luck and happy coding!

Common Reusable Business Entities Part 2

This is part two of an ongoing series on common reusable business entities. This series does not need to be read in order, but here are the preceeding posts if you would like to catch up:

In this post, we’re going to examine a short but useful structure that can be used in almost every business-style application you will write; the Address structure.

Address Structure

The Address structure specifies information regarding a physical mailing address. Obviously, this is used all over the place, but a few examples include E-Commerce shipping and billing addresses, user profile addresses, and a foundation for geocoding property/locations. Overall, all of the entities to be demonstrated in this short series are meant to be thin, without a lot of functionality or business logic.

public struct Address
{
    #region "PROPERTIES"

    /// <summary>
    /// The prefix of the street address.  (Ex: 1400 Pennsylvania Ave.)
    /// </summary>
    public string Address1 { get; set; }

    /// <summary>
    /// The suffix of the street address.  (Ex: Apt. 14)
    /// </summary>
    public string Address2 { get; set; }

    /// <summary>
    /// The city of the address.
    /// </summary>
    public string City { get; set; }

    /// <summary>
    /// The state of the address (2 character string - could be entity).
    /// </summary>
    public string State { get; set; }  //NOTE: it may be more appropriate to have this as an object

    /// <summary>
    /// The country of the address (3 character representation: USA, CHN, JPN)
    /// </summary>
    public string Country { get; set; }  //NOTE: it may be more appropriate to have this as an object

    /// <summary>
    /// The postal code of the address.
    /// </summary>
    public string Zip { get; set; }  

    #endregion

    #region "CONSTRUCTORS"

    /// <summary>
    /// Constructor for address.
    /// </summary>
    /// <param name="address1"></param>
    /// <param name="address2"></param>
    /// <param name="city"></param>
    /// <param name="state"></param>
    /// <param name="country"></param>
    /// <param name="zip"></param>
    public Address(string address1, string address2, string city, string state, string country, string zip)
        : this()
    {
        this.Address1 = address1;
        this.Address2 = address2;
        this.City = city;
        this.State = state;
        this.Country = country;
        this.Zip = zip;
    }

    #endregion

    #region "METHODS"

    /// <summary>
    /// Overrides ToString() to display default address format (Address1 Address2 City, State Zip).
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return (this.Address1 + " " + this.Address2 + " " + this.City + ", " + this.State + " " + this.PostalCode);
    }

    #endregion
}

What’s Next?

I’ll cover three short entities in the next part; SocialSecurityNumber, CreditCardNumber and PhoneNumber, so feel free to come back and check it out. Also, please add any requests for specific common entities or add your own in the comments below.

Common Reusable Business Entities Part 1

I noticed that several of my projects both for work and hobby tend to reuse certain common business entities. A Name structure is pretty much always the same (A name is a name is a name… maybe that’s a horse – nevermind the sidetrack.) In most cases, these entities were needed without change or very little change and therefore made for a great base entity to build off of.

Here are a few common entities that you may like to begin building a library with. You never know, it may save some time and build more consistency throughout several of your applications.

PersonName Structure: What’s in a Name?

The PersonName structure specifies a person’s name, including first, middle, and last as well as common prefix and suffix values. It has built in formatting options that you may like to expand on. Overall, all of the entities to be demonstrated in this short series are meant to be thin, without a lot of functionality or business logic.

public struct PersonName
{
    #region "ENUMERATIONS"
    /// <summary>
    /// The prefix to a persons name.
    /// </summary>
    public enum PrefixType
    {
        [Description("~")]
        NONE,
        [Description("Dr.")]
        DR,
        [Description("Father")]
        FATHER,
        [Description("Miss")]
        MISS,
        [Description("Mr.")]
        MR,
        [Description("Mrs.")]
        MRS,
        [Description("Ms.")]
        MS,
        [Description("Prof.")]
        PROF,
        [Description("Rev.")]
        REV
    }

    /// <summary>
    /// The suffix to a persons name.
    /// </summary>
    public enum SuffixType
    {
        [Description("~")]
        NONE,
        [Description("I")]
        I,
        [Description("II")]
        II,
        [Description("III")]
        III,
        [Description("IV")]
        IV,
        [Description("Jr.")]
        JR,
        [Description("Sr.")]
        SR,
        [Description("1st")]
        FIRST,
        [Description("2nd")]
        SECOND,
        [Description("3rd")]
        THIRD,
        [Description("4th")]
        FOURTH,
        [Description("D.D.S.")]
        DDS,
        [Description("M.D.")]
        MD,
        [Description("Ph.D.")]
        PHD
    }

    /// <summary>
    /// The display format for a persons name.
    /// </summary>
    public enum DisplayMode
    {
        //TODO: add any additional formats needed
        LastFirstMiddle,
        LastFirstMiddleInit,
        LastFirst,
        FirstMiddleLast,
        FirstMiddleInitLast,
        FirstLast
    }
    #endregion

    #region "PROPERTIES"
    /// <summary>
    /// Persons first name.
    /// </summary>
    public string First { get; set; }

    /// <summary>
    /// Persons middle name.
    /// </summary>
    public string Middle { get; set; }

    /// <summary>
    /// Persons last name.
    /// </summary>
    public string Last { get; set; }

    private string _MiddleInit;
    /// <summary>
    /// Persons middle initial.
    /// </summary>
    public string MiddleInit
    {
        get
        {
            if (string.IsNullOrEmpty(this._MiddleInit) == true)
            {
                if (string.IsNullOrEmpty(this.Middle) == false)
                {
                    return this.Middle.Substring(0, 1);
                }
                else
                {
                    return string.Empty;
                }
            }
            else
            {
                return this._MiddleInit;
            }
        }
        set
        {
            _MiddleInit = value.Substring(0, 1);
        }
    }


    /// <summary>
    /// Persons name prefix (Mr., Mrs., Ms., etc.)
    /// </summary>
    public PrefixType? Prefix { get; set; }

    /// <summary>
    /// Persons name suffix (Jr., Sr., etc.)
    /// </summary>
    public SuffixType? Suffix { get; set; }

    #endregion

    #region "CONSTRUCTORS"

    /// <summary>
    /// PersonName constructor.
    /// </summary>
    public PersonName(string first, string last)
        : this()
    {
        First = first;
        Last = last;
    }

    public PersonName(PrefixType? prefix, string first, string middle, string last, SuffixType? suffix)
        : this()
    {
        Prefix = prefix;
        First = first;
        Middle = middle;
        Last = last;
        Suffix = suffix;
    }

    #endregion

    #region "METHODS"
    /// <summary>
    /// Overrides ToString() to display default name format (Last, First Middle).
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return String.Format("{0}, {1} {2}", this.Last, this.First, this.Middle);
    }

    /// <summary>
    /// Additional ToString() to display name by specified format.
    /// </summary>
    /// <param name="dm"></param>
    /// <returns></returns>
    public string ToString(DisplayMode dm)
    {
        //TODO: add all the rest of the formats needed here
        switch (dm)
        {
            case DisplayMode.LastFirstMiddleInit:
                return string.Format("{0}, {1} {2}", this.Last, this.First, this.MiddleInit);
                break;
            case DisplayMode.LastFirst:
                return string.Format("{0}, {1}", this.Last, this.First);
                break;
            case DisplayMode.FirstMiddleLast:
                return string.Format("{0} {1} {2}", this.First, this.Middle, this.Last);
                break;
            case DisplayMode.FirstMiddleInitLast:
                return string.Format("{0} {1} {2}", this.First, this.MiddleInit, this.Last);
                break;
            case DisplayMode.FirstLast:
                return string.Format("{0} {1}", this.First, this.Last);
                break;
            default:
                return ToString();
                break;
        }
    }

    #endregion
}

What’s Next?

I’ll cover an Address structure in the next part, so feel free to come back and check it out. Also, please feel free to add any requests for specific common entities or add your own in the comments below.