Creating the business objects to encapsulate your DataSets

Using raw DataSet objects directly in the presentation layer is not a good practice and should be avoided whenever possible. It makes maintenance a relatively difficult job later on when there are changes to the underlying data tier.

Consider the following code snippet. It retrieves a dataset from the data layer and references the data inside using table column names.

DataSet _accountsDataset= GetAccountsList();
Datarow _row = _accountsDataset.Tables["Accounts"].Rows[0];
txtFirstName.text = _row["FirstName"];
txtLastName.text = _row["LastName"];

Now, try to imagine this same code snippet peppered all over your application across your forms, custom controls, and user controls. This will translate into a lot of trouble when the underlying database column name or type changes in the future. Many programmers commonly make the mistake of passing dataset objects directly to the presentation tier (a classic example of this is the Datagrid control, which readily takes in and renders a DataSet object).

Note

A logic or business object tier shields the presentation tier from having to worry about the underlying data tier.

The great thing about data binding is that it not only works on datasets but also on custom business object classes. This allows you to wrap or encapsulate the datasets within your own custom class and to expose each field as properties in your class. The following diagram best illustrates this concept:

Creating the business objects to encapsulate your DataSets

Let's take a look at what happens when the underlying database column or type changes this time. If the Database Administrator decided to rename the FirstName column as IndividualFirstName one day, you would only need to change the property definition in your logic tier to the following:

public string FirstName {
get {return_row["IndividualFirstName"]; }}

The following table lists the various business objects you will need to build in the sales force application:

Class name

Description

Baseobject

This is the base class for the LeadAccount, OpportunityAccount, and CustomerAccount business objects. Internally, it holds all the properties that are common across leads, opportunities, and customers. It also holds a collection of Task, File, and History objects related to the account.

LeadAccount

The LeadAccount represents a lead. This account inherits from the BaseAccount class.

OpportunityAccount

The OpportunityAccount represents an opportunity. This account inherits from the BaseAccount class.

CustomerAccount

The CustomerAccount represents a customer. This account inherits from the BaseAccount class.

Task

The Task class represents a task record.

History

The History class represents a history record. Historical records are usually auto-generated by the application.

File

A File class represents a file attachment object.

Product

The Product class represents a product item.

TaskCollection

This is a collection class that stores Task objects.

HistoryCollection

This is a collection class that stores History objects.

FileCollection

This is a collection class that stores File objects.

ProductCollection

This is a collection class that stores Product objects.

Let's take a look at the skeleton code for the BaseAccount class. As each sales force account is represented by a Datarow in the dataset, you will need to pass in the data row to the constructor of the BaseAccount class.

namespace CRMLive
{
public class BaseAccount
{
public enum AccountTypes
{
Lead,
Opportunity,
Customer
}
private TaskCollection _Tasks;
private HistoryCollection _Histories;
private FileCollection _Files;
private DataSet _MappedDataSet;
private DataRow _MappedDataRow;
private string _ValidationResult;
public DataRow MappedDatarow
{
get
{
return _MappedDataRow;
}
}
public DataSet MappedDataset
{
get
{
return _MappedDataSet;
}
}
public BaseAccount(DataRow MappedDatarow)
{
_MappedDataRow = MappedDatarow;
_MappedDataSet = _MappedDataRow.Table.DataSet;
_Tasks = new TaskCollection(this,
_MappedDataSet.Tables["AccountTasks"]);
_Histories = new HistoryCollection(this,
_MappedDataSet.Tables["AccountHistories"]);
_Files = new FileCollection(this,
_MappedDataSet.Tables["AccountFiles"]);
}
}

The BaseAccount class would need to expose each column in the Datarow as a property. The following code shows an example on how you can expose the FirstName field as a property.

public string FirstName
{
get
{
if (Information.IsDBNull(_MappedDataRow["FirstName"]) == true)
{
return "";
}
else
{
return _MappedDataRow["FirstName"].ToString();
}
}
set
{
_MappedDataRow["FirstName"] = value;
}
}

Let's take a look now at both the Task class and TaskCollection classes. A data row is also passed in to the constructor of the Task class, which can then be exposed and accessed via properties in the same fashion.

public class Task
{
private DataRow _MappedRow;
public string TaskSubject
{
get
{
if (Information.IsDBNull(_MappedRow["TaskSubject"]) == true)
{
return "";
}
else
{
return _MappedRow["TaskSubject"].ToString();
}
}
set
{
_MappedRow["TaskSubject"] = value;
}
}
public DateTime TaskDate
{
get
{
if (Information.IsDBNull(_MappedRow["TaskDate"]))
{
return DateTime.Now;
}
else
{
return
System.Convert.ToDateTime
(_MappedRow["TaskDate"]);
}
}
set
{
_MappedRow["TaskDate"] = value;
}
}
.
.
.
public DataRow MappedRow
{
get
{
return _MappedRow;
}
}
public Task(DataRow DataRowItem)
{
_MappedRow = DataRowItem;
}
}

Collection classes will be created differently. Most of the collection classes that you create in this application will need to inherit from the System.Collections.Generic.List class. The reason for this is that the .NET Datagrid only recognizes and binds to objects that implement either the IList or IListView interface. The DataSet object, for example implements the IList interface.

Most of the methods required of a collection object such as the Count() and Contains() method are already provided in the List base class. You only need to create your own custom AddTask() and RemoveTask() functions. These custom functions do the additional step of adding the new data rows to the data table of the internally stored dataset. The following code shows how the TaskCollection class can be created.

public class TaskCollection :
System.Collections.Generic.List<Task>
{
private DataTable _TaskDataTable;
private BaseAccount _parent;
public BaseAccount Parent
{
get
{
return _parent;
}
}
public Task AddTask(Task TaskItem)
{
base.Add(TaskItem);
_TaskDataTable.Rows.Add(TaskItem.MappedRow);
return TaskItem;
}
public void RemoveTask(Task TaskItem)
{
TaskItem.MappedRow.Delete();
base.Remove(TaskItem);
}
public TaskCollection(BaseAccount Parent, DataTable
TableItem)
{
int _counter;
Task _task;
_parent = Parent;
_TaskDataTable = TableItem;
for (_counter = 0; _counter <= _TaskDataTable.Rows.Count
- 1; _counter++)
{
_task = new Task(_TaskDataTable.Rows[_counter]);
base.Add(_task);
}
}
}

Adding a new task to a BaseAccount object, for example, is a two-step process usually done in the following fashion. The first step creates the new task object and the second step adds it to the Tasks collection.

Task _task = _myBaseAccount.NewTask();
.
.
.
_myBaseAccount.Tasks.AddTask(_task);

The NewTask() method can be implemented in the BaseAccount class using the following code snippet:

public Task NewTask()
{
DataRow _row =
_MappedDataSet.Tables["AccountTasks"].NewRow();
Task _task = new Task(_row);
return _task;
}

Validating data in your business objects

Another important role of the logic tier is to validate its data before it reaches the data tier. You can provide validation functionality to the rest of the application by exposing a Validate() function in the business object class. This function will run through all the necessary validations required and finally return true or false depending on the outcome of the validation. The following code shows how this function might look in the BaseAccount class.

private string _ValidationResult;
public string GetValidationResult()
{
return _ValidationResult;
}
public bool Validate()
{
_ValidationResult = "";
//We ensure that the Account GUID is not empty
if (AccountGUID.ToString().Length == 0)
{
_ValidationResult = "Account GUID cannot be empty";
return false;
}
//We ensure that at least one phone number have been filled in
if (ResPhoneNo.Trim().Length == 0 &&
MobPhoneNo.Trim().Length == 0)
{
_ValidationResult = "Please fill in at least one contact
number";
return false;
}
//We ensure that both the first name and last name fields
//have been filled in
if (FirstName.Length == 0 || LastName.Length == 0)
{
_ValidationResult = "Please fill in both the first
name and last name fields";
return false;
}
//Make sure that an account with the same name does not //already exist
if
(GlobalArea.PluginManager.GetActivePlugin.AccountExists
(FirstName, LastName, this.AccountGUID) == true)
{
_ValidationResult = "An account with the same name
already exists. Please use a different name";
return false;
}
return true;
}

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.214.32