Dataset Vs custom object (Entity)

Labels:

Dataset

A dataset is a class in .Net representing the structure similar of a SQL database. It contains one or more tables. You can create typed dataset, it's a class which inherits from the base class dataset and have tables with specified name, columns with specified names and types. It's very easy to create with Visual Studio, in the server explorer window when you are connected to a database, you simply create a new dataset and you drag and drop the tables you need. Next you run a query against a database or via a table adapter and you fill this dataset, it's very easy. A dataset implements all the interfaces for databinding such as IErrorDataInfo, INotifyPropertyChanged, ... (see Databinding and error validation in WinForm).

Custom object (Entity)

A custom object is a class that your write by your own. It contains properties (generally no business code) and basic validations (see Databinding and error validation in WinForm). You run a query against a database and you map SQL columns to the properties of your custom classes.

Why to use dataset or custom object (Entity)

A dataset is very easy and is rapidly created but there are some problems for big projects :

  • A dataset is a fixed structure, if you have a SQL table person and in the future you have in this SQL table more than one type of person, a seller and a buyer you can't do inheritance with datatable.
  • If you plan to do a web service returning data, dataset is a big structure, if your table contains only one row of data with one column, the XML result of your web service contains more data for the definition of the dataset than for the data itself.
  • Also for web service, dataset is a class of the .Net framework and it doesn't exist in other platform, it is not WSI compliant, and it will be very difficult for not .Net client to use it.

Custom object doesn't have this problem :

  • A custom object is a basic class and you can do inheritance, you can create a class person and in the future create 2 classes, one Buyer and other one Seller and these classes inherit from the class Person. So you can benefit of polymorphism and you don't have maintenance problems.
  • Into web service, the XML output contains only properties which contains data and no more and the definitions is very small.
  • Custom object produces basic XML output WSI compliant if you use properties in your custom object which contains only primitive types or other custom objects which also contain properties with primitive types or custom objects ... You can also create properties defined as list, collection, ... and these properties will be transformed to XML array.

Conclusion

For small projects dataset is a good choice, you can create rapidly an application with databinding. Pay attention that if you create a web service it's only for .Net clients.

For big projects dataset can lead to maintenance, evolution problems, it'll be difficult to change an existing dataset, the only solution will be to create a new one. With custom object we can make inheritance, create interfaces, ... to have a flexible design for future changes. It takes at the beginning a little more time to start the project in comparison with dataset because you need to create your objects and implements the necessary interfaces if you want to take advantages of databinding (see Databinding and error validation in WinForm). But now with linq and linq to SQL it's more easier to work with custom objects.







Databinding and error validation in WinForm

image

I write this post because I have searched a long time before to obtain an acceptable result with error validation and databinding.

With databinding and a little bit of code you can obtain the result from the screen shot rapidly and this code is very stable.

The first step is to write our business object, in this sample the class Person with 2 properties "Lastname" and "Firstname".

Next via the datasource window we drag and drop the first name and last name property into the window. The error property in the following screen shot will be explained later.

image

Next to show errors on our winform we add an error provider to the window and we can set the current error for each control via the validate event of each control but it's long and it's for each new form with the same object.

The error provider component have a property datasource, we can bind to it a dataset and it will show errors into data grid but for custom object?

You have to implement the interface IDataErrorInfo. This interface have 2 get properties Error and an indexer with a string as key. The error property return a message saying if the whole object have one or more errors, the indexer return for a property if the property is good or not. When a bound control lose focus, it will read the indexer property and the string parameter will be the property name to check, if we return a string different of null or empty, the property is in error state and the error provider will show it.

See the implementation of the Person class:

 

   1 using System;

    2 using System.Collections.Generic;

    3 using System.Text;

    4 using System.ComponentModel;

    5 

    6 namespace DataBindingTest

    7 {

    8     class Person : IDataErrorInfo

    9     {

   10         private Dictionary<string, string> errors = new Dictionary<string, string>();

   11 

   12         private string error = string.Empty;

   13 

   14         private string lastname;

   15         private string firstname;

   16 

   17         private void Validate(string field, string value)

   18         {

   19             switch (field)

   20             {

   21                 case "Lastname":

   22                     if (String.IsNullOrEmpty(value))

   23                     {

   24                         this.errors["Lastname"] = "There is an error";

   25                     }

   26                     else

   27                     {

   28                         this.errors.Remove("Lastname");

   29                     }

   30                     break;

   31                 case "Firstname":

   32                     if (String.IsNullOrEmpty(value))

   33                     {

   34                         this.errors["Firstname"] = "There is an error";

   35                     }

   36                     else

   37                     {

   38                         this.errors.Remove("Firstname");

   39                     }

   40                     break;

   41                 default:

   42                     break;

   43             }

   44         }

   45 

   46         public string Lastname

   47         {

   48             get

   49             {

   50                 this.Validate("Lastname", this.lastname);

   51                 return this.lastname;

   52             }

   53 

   54             set

   55             {

   56                 this.lastname = value;

   57                 this.Validate("Lastname", this.lastname);

   58             }

   59         }

   60 

   61         public string Firstname

   62         {

   63             get

   64             {

   65                 this.Validate("Firstname", this.firstname);

   66                 return this.firstname;

   67             }

   68 

   69             set

   70             {

   71                 this.firstname = value;

   72                 this.Validate("Firstname", this.firstname);

   73             }

   74         }

   75 

   76         #region IDataErrorInfo Members

   77 

   78         public string Error

   79         {

   80             get

   81             {

   82                 if (this.errors.Count > 0)

   83                 {

   84                     return "There are " + this.errors.Count + " errors";

   85                 }

   86 

   87                 return null;

   88             }

   89         }

   90 

   91         public string this[string columnName]

   92         {

   93             get

   94             {

   95                 if (this.errors.ContainsKey(columnName))

   96                 {

   97                     return this.errors[columnName];

   98                 }

   99                 else

  100                 {

  101                     return null;

  102                 }

  103             }

  104         }

  105 

  106         #endregion

  107     }

  108 }

 

This method is good for basic validation such as null, length, regex validation.

Now if you change the property of our custom object via business code, it will not be reflected directly to the screen. The binding source doesn't know that the value has changed. The solution is to implement the interface INotifyPropertyChanged, it will declare the event PropertyChanged on your class. Then you raise this event giving to it the name of the property into the setter of all of the properties of the Person class and now the binding source will be notified of the change and refresh it on the screen.

61 public string Firstname

   62         {

   63             get

   64             {

   65                 this.Validate("Firstname", this.firstname);

   66                 return this.firstname;

   67             }

   68 

   69             set

   70             {

   71                 this.firstname = value;

   72                 if (this.PropertyChanged != null)

   73                 {

   74                     this.PropertyChanged(this, new PropertyChangedEventArgs("Firstname"));

   75                 }

   76                 this.Validate("Firstname", this.firstname);

   77             }

   78         }

There are a lot of interfaces you can implement to have your live easier...