Click here to Skip to main content
15,881,852 members
Articles / Database Development / SQL Server

Overview of XQuiSoft Data Using the Provider Pattern (Open Source)

Rate me:
Please Sign up or sign in to vote.
3.59/5 (12 votes)
16 Jun 2009BSD8 min read 40.7K   28   8
A data abstraction layer for .NET applications. Write your application code to be database neutral. Swap out the type of database your application uses without updating or even recompiling your business components.

Introduction

XQuiSoft Data is an open source component for database neutral .NET applications. It is part of the XQuiSoft application framework, which is entirely open source. The source download includes the demo business component and demo web site. All the source code in this article can be found in the download. You can get the latest version of the project here.

This data component has a dependency on another open source project in the xquisoft framework, the xquisoft provider component. It is basically an implementation of the provider pattern for the 1.1 framework similar to that in the 2.0 framework. This open source project can be found here.

In this article, I will show examples of how to use the XQuiSoft Data component from the business tier of your application. The downloadable source will include a sample web project that calls these components, which I will only briefly discuss.

Demo Setup

First of all, make sure you download the correct version. The latest version at the time of this article is 2.2.50727.0. Earlier versions show a different way to use the component that may be obsolete in the future. Future versions will also include the demo projects. To get the required xqs-provider reference, you can either download the XQuiSoft Data binaries which include it in compiled form, or you can download the source from the xqs-provider downloads area.

The source download for XQuiSoft.Data contains three projects. The first is the Data class library source, which compiles to XQuiSoft.Data.dll. The second is a demo library that contains most of the code snippets in this article, which compiles to XQuiSoft.Data.Demo.dll. The third project is a web application project showing usage of the code in the sample code below.

  1. Create a new solution file in the folder above these projects, and add each project to it. I have a different solution that contains other core xquisoft projects, and I could not include it.
  2. Update the web.config of the web application. You must update the database connection string to a database of which you have an account with create table access. This access is required for the installer. If you prefer to run the scripts yourself feel free, then all the user in the connection string needs to have access to do is run the stored procedures created by the script.
  3. The solution should now run / debug normally. The first time you run it, set the start page as Install.aspx. Then navigate to the EmployeeList.aspx page to edit records.

The goal of this demo is to demonstrate how to design your business layer code. The user interface is bare minimum in appearance.

Basic Features

Ease of use: You don't have to worry about opening connections, creating commands, or disposing of resources such as data readers. You just have to know which command you want to call, and pass the command the appropriate parameter values.

Easy to learn: The method names on the supplied classes correspond to method names in the base .NET Framework. This should make it easy to upgrade your current code to use this component.

Extensibility: The data component is based on the provider pattern. This means that the DataManager class delegates the actual writing call to one or more configured providers (implementations) that derive from the base DataProvider class. Each provider defines the type of database that it interacts with, and how to fulfill database commands. Currently there is a built in SqlDataProvider and a OracleDataProvider.

Application Design

Configuration Setup

The first thing you need to do is add some configuration required by the data component. The first portion tells the framework where to load the class that will handle the next custom configuration section. There can be only one 'configSections' tag in your application configuration. So if you have other components with custom configuration sections, merge those with this one.

XML
<configSections>
    <sectionGroup name="XQuiSoft.Data">
        <section name= "DataManager" type="XQuiSoft.Data.DataManagerConfiguration, 
		XQuiSoft.Data" />
    </sectionGroup>
</configSections> 

The next part you need is the configuration section that defines all your database connections, which was just described in the 'configSections'.

XML
    <XQuiSoft.Data>
        <DataManager defaultProvider="Production">
            <providers>
                <!-- The provider instance to use on localhost development -->
                <add name="Development"
                    type="XQuiSoft.Data.SqlClient.SqlDataProvider, XQuiSoft.Data"
                    connectionString="Password=pw;Persist Security Info=True;
			User ID=DATABASEUSER
;Initial Catalog=DATABASENAME;Data Source=SERVERNAME\INSTANCENAME" />
                <!-- The provider instance to use when deployed to production -->
                <add name="Production"
                  type="XQuiSoft.Data.SqlClient.SqlDataProvider, XQuiSoft.Data"
                  connectionString="Password=pw;Persist Security Info=True;
		User ID=DATABASEUSER;
		Initial Catalog=DATABASENAME;Data Source=SERVERNAME\INSTANCENAME" />
            </providers>
        </DataManager>
    </XQuiSoft.Data> 

The providers node contains child nodes for each database connection you want to configure. The type attribute is the database implementation provider that represents the type of database you wish to connect to. In the case of a built in type, you really don't need the full assembly name. In this case, "XQuiSoft.Data.SqlClient.SqlDataProvider" would have been sufficient. The provider component will search the executing assembly for the specified type if the assembly is not specified. The connectionString attribute must be valid for the database type you are using.

Notice that there are two attributes that can be on the DataManager node. You can either have a 'defaultProvider' which equals one of the names of the child nodes, or you can have a 'defaultProviderAppSettingName' which equals a setting name in the app settings node. The value of that setting must equal one of the provider names in the child nodes.

XML
<appSettings file="Environment.config">
    <add key="ENVCODE" value="Development" />
</appSettings>

The reason for doing this is so you can keep your web.config identical in all deployed environments. Then each environment can have a separate Environment.config file that has a value for "ENVCODE". That file should look identical to the appSettings node above, except without the file attribute specified.

Basic Entities

In our sample application, we are going to view and edit employees only. This is just to keep it simple. So the first thing we need to do is define our custom entity class 'Employee', and a strongly typed collection 'EmployeeCollection' which derives from collection base. This is provided in the download, and there is nothing special about these classes. I'm not going to explain them here.

Concrete Provider Implementation using DataManager

Start with a static service class defining methods required by your application. In our case we need to get employees, save, and delete employees. Next define a base provider class with abstract methods of the same signature. Next we need a concrete implementation class for EmployeeProvider called DbEmployeeProvider. Below is an outline of the class with a few of the method implementations shown for discussion. To see the EmployeeProvider base class or the EmployeeManager service class, see the code in the download.

C#
public class DbEmployeeProvider: EmployeeProvider
{
	#region Fields
	/*...*/
	#endregion Fields

	#region Constructors
	/*...*/
	#endregion Constructors

	#region Properties
	/*...*/
	#endregion Properties

	#region Methods
	public override void Initialize(string name, 
			NameValueCollection configValue)/*...*/
	public override ExecutionResults Install()/*...*/
	/*...*/
	public override Employee GetEmployee(int id)
		{
		Procedure proc = new Procedure("Demo_GetEmployee");
		proc.AddInputParameter("EmployeeID", DbType.Int32, id);
		
		DataFactoryDelegate builder = 
			new DataFactoryDelegate(this.AddEmployee);
		EmployeeCollection col = new EmployeeCollection();
		dataProvider_.Execute(proc, builder, col);

		if (col.Count > 0)
			return col[0];
		else
			return null;
	}
	/*...*/
	public override EmployeeCollection GetSubOrdinates(int managerID)/*...*/
	/*...*/
	public override bool SaveEmployee(Employee item)
	{
		Procedure proc = new Procedure("Demo_SaveEmployee");
		proc.AddParameter("EmployeeID", 
			DbType.Int32, ParameterDirection.InputOutput, item.ID);
		proc.AddInputParameter("ManagerID", DbType.Int32, item.ManagerID);
		proc.AddInputParameter("FirstName", DbType.AnsiString, item.FirstName);
		proc.AddInputParameter("LastName", DbType.AnsiString, item.LastName);

		int rows = dataProvider_.ExecuteNonQuery(proc);
		Parameter prmID = proc.Parameters.Find("EmployeeID");
		item.ID = dataProvider_.GetInt32(prmID.Value);
		return (rows > 0);
	}
	/*...*/
	public override bool DeleteEmployee(int id)/*...*/
	/*...*/
	protected virtual void AddEmployee(IList target, IDataRecord record)
	{
		Employee item = new Employee();
		item.ID = dataProvider_.GetInt32(record, "EmployeeID", -1);
		item.ManagerID = dataProvider_.GetInt32(record, "ManagerID", -1);
		item.FirstName = dataProvider_.GetString(record, "FirstName");
		item.LastName = dataProvider_.GetString(record, "LastName");
		target.Add(item);
	}
	#endregion Methods
}

Stored procedures can be called by instatiating the Procedure class with the required name, adding parameter values, and then calling the appropriate Execute method. Note that a procedure parameter name should not include a database provider specific prefix. The data provider will handle adding that, if applicable, before the parameter is passed to ADO.NET. The default SqlDataManager provider class adds a @ prefix to ADO.NET parameters if it does not already exist.

The GetEmployee method executes the procedure, then another method AddEmployee is passed into the DataFactoryDelegate parameter of the Execute method. This delegate method will be called for each record the data reader returned from the database connection. This method instantiates a new data object instance (in this case class Employee), populates the properties, and adds the new item to the supplied collection.

The SaveEmployee method uses a procedure also, but it inserts or updates a record (and does not return any records.) It calls ExecuteNonQuery passing the Procedure instance filled with parameters. The output parameter defined on the procedure is used to populate the id of the record after the ExecuteNonQuery call completes.

Provider Model Configuration

In order to use this EmployeeManager service, you'll need to configure it to use the DbEmployeeProvider as follows. Note you'll have to merge the contents of this new sectionGroup into the same configSections node used to configure XQuiSoft.Data. Notice the connection name is not specified below. When the connectionName is not specified, the DbEmployeeProvider uses the default DataProvider defined in the DataManager section of the configuration.

XML
<configSections>
    <sectionGroup name="XQuiSoft.Data.Demo">
        <section name= "EmployeeManager" 
		type="XQuiSoft.Data.Demo.EmployeeManagerConfiguration" />
    </sectionGroup> 
</configSections>
<XQuiSoft.Data.Demo>
    <EmployeeManager>
        <providers>
            <add name="Default" 
	    type="XQuiSoft.Data.Demo.Data.DbEmployeeProvider, XQuiSoft.Data.Demo" />
        </providers>
    </EmployeeManager>
</XQuiSoft.Data.Demo>

If your database is shared so that records are split across databases, you will likely want to specify each of the multiple connections to be used by the DbEmployeeProvider, or the default connection could be used to execute a query on a main database connection to determine which connection holds the data for a given query. Database sharing design is beyond the scope of this article. However, the DataManager does support an easy programmatic experience for scenarios that require database sharing.

Application Usage (UI)

Usage of the service classes from the user interface is really simple. First we need a form that can bind to the results. Since EmployeeCollection derives from CollectionBase, it implements IList which is what the DataGrid is designed to work with (not just datasets).

C#
EmployeeCollection emps = EmployeeManager.GetSubordinates(mgrId);
DataGrid1.DataSource = emps;
DataGrid1.DataBind();

That's it! You can now swap out the DbEmployeeProvider with a different implementation, and the user interface code does not have to change, or even be recompiled!

Conclusion

I've convered the basics features and usage of the XQuiSoft Data component. Some of the features are really inherited from the XQuiSoft Provider component, so be sure to check that out too. I would be happy to hear your results in using these components. I didn't cover all the code that you'll see in the demo project. Some of the code is for supporting the provider auto-installation feature. I may discuss that feature in a future article. It is not really necassary in a simple application like this demo, but is very useful in a plugin architecture.

If you start a new project, consider the benefits of XQuiSoft Data. Evaluate it, and see if it fits into your toolbox. It would be great to hear feedback on your evaluation in either case.

History:

  • April 27, 2006: Initial submission of article
  • April 28, 2006: Added extra download instructions
  • June 16, 2009: Updated to use the latest features of XQuiSoft.Data introduced in the last year and a half. This update was way overdue.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Web Developer Nexul Software LLC
United States United States
Mike has worked in the .Net platform since the beta 2 release of version 1.0. Before that he worked on VB6 windows forms applications automating other applications such as AutoCAD and "Intent".

Mike has released a number of open source applications in javascript and C#.Net. Most of them can be found on github.
github/michael-lang

You can find older .Net open source projects on sourceforge at:
http://sourceforge.net/users/versat1474/

Mike is currently blogging at candordeveloper.com

Comments and Discussions

 
GeneralOracleDataProvider uses Microsoft implementation Pin
Mike Lang18-Jun-09 6:00
Mike Lang18-Jun-09 6:00 
NewsVersion 2.2 now released Pin
Mike Lang16-Jun-09 17:49
Mike Lang16-Jun-09 17:49 
NewsDownload the correct version! Pin
Mike Lang28-Apr-06 2:59
Mike Lang28-Apr-06 2:59 
QuestionLow vote scores?! Pin
Mike Lang27-Apr-06 11:03
Mike Lang27-Apr-06 11:03 
AnswerRe: Low vote scores?! Pin
Nish Nishant27-Apr-06 11:19
sitebuilderNish Nishant27-Apr-06 11:19 
GeneralRe: Low vote scores?! Pin
Bill S27-Apr-06 12:29
professionalBill S27-Apr-06 12:29 
GeneralRe: Low vote scores?! Pin
Mike Lang27-Apr-06 12:30
Mike Lang27-Apr-06 12:30 
GeneralRe: Low vote scores?! Pin
Ravi Bhavnani28-Apr-06 2:20
professionalRavi Bhavnani28-Apr-06 2:20 
Nishant Sivakumar wrote:
Some people are anti-opensource.


To me that makes as much sense as "some people are against people whose first names contain more than one vowel". I thought the purpose of CP was to share sourcecode? Confused | :confused:

/ravi

My new year's resolution: 2048 x 1536
Home | Music | Articles | Freeware | Trips
ravib(at)ravib(dot)com

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.