Friday, October 17, 2008

DataMapper implementation in C# using Enterprise Library Data Access Block

Note: I have added an update post on this article in my blog...

Data Access Layer in C# using Repository and Data Mapper


According to Fowler DataMapper is a layer of software that separates the in-memory objects from the database. Its responsibility is to transfer data between the two and also to isolate them from each other. Creating a DataMapper will help you eliminate most of the cross concerns and problems in your code.
In this article I will show how to create a simple DataMapper using the Template pattern and Generics in C#. I am using Enterprise Library 3.1 Data Access Block in this example.
I have my Users Table in the database as follows.
Column Name
Data type
UserID
int
FirstName
varchar(50)
LastName
varchar(50)
Address
varchar(250)
I will create an example to query the user details from this table by using a Template Pattern implementation of DataMapper
I have my DataMapper class which serves are the base class for any DataMapper implemented using Generics as following.
internal abstract class DataMapper
{
protected abstract T EntityMap(IDataRecord record);
public Collection EntityCollectionMap(IDataReader reader)
{
Collection dataCollection = new Collection();
while (reader.Read())
{
dataCollection.Add(EntityMap(reader));
}
return dataCollection;
}
}
The step to follow is to create a Mapper class for my User class.
internal class UserDataMapper : DataMapper<User>
{
protected override User EntityMap(IDataRecord record)
{
User userEntity = new User();
userEntity.Id = (DBNull.Value == record["UserID"]) ? 0 : (int)record["UserID"];
userEntity.FirstName = (DBNull.Value == record["FirstName"]) ? string.Empty : (string)record["FirstName"];
userEntity.LastName = (DBNull.Value == record["LastName"]) ? string.Empty : (string)record["LastName"];
userEntity.Address = (DBNull.Value == record["Address"]) ? string.Empty : (string)record["Address"];
return userEntity;
}
}
To access the data from the database we need to create a Reader class as follows.
internal abstract class EntityReader
{
Database GetDatabase()
{
return DatabaseFactory.CreateDatabase("LocalSqlServerConnection");
}
protected abstract string CommandText { get; }
protected abstract CommandType CommandType { get; }
protected abstract Collection<DbParameter> GetCommandParameters(DbCommand command);
protected abstract DataMapper GetDataMapper();
public Collection GetEntityList()
{
Collection entityList = new Collection();
DbCommand command = (CommandType == CommandType.StoredProcedure) ? GetDatabase().GetStoredProcCommand(CommandText) : GetDatabase().GetSqlStringCommand(CommandText);
foreach (DbParameter commandParameter in GetCommandParameters(command))
command.Parameters.Add(commandParameter);
using (IDataReader reader = GetDatabase().ExecuteReader(command))
{
DataMapper mapper = GetDataMapper();
entityList = mapper.EntityCollectionMap(reader);
return entityList;
}
}
}
The EntityReader class contains an instance of the Enterprise Library Database object that will be used to call the DB operations.
Now to create a Reader class for the User object I need to create a UserReader class as
internal class UserReader : EntityReader<User>
{
protected override string CommandText
{
get
{
return "SELECT USERID, FIRSTNAME, LASTNAME, ADDRESS FROM USERS";
}
}
protected override CommandType CommandType
{
get
{
return CommandType.Text;
}
}
protected override Collection<DbParameter> GetCommandParameters(DbCommand command)
{
Collection<DbParameter> parameters = new Collection<DbParameter>();
switch (command.CommandText)
{
case "GetUsersByName":
DbParameter param = command.CreateParameter();
param.Direction = ParameterDirection.Input;
param.ParameterName = "FirstName";
parameters.Add(param);
break;
}
return parameters;
}
protected override DataMapper<User> GetDataMapper()
{
DataMapper<User> mapper = new UserDataMapper();
return mapper;
}
}
That’s all. I can use the mapper created to query my User collection as
UserReader userReader = new UserReader();
Collection<User> users = userReader.GetEntityList();

Monday, October 13, 2008

Using Extensible Object Pattern to extend .NET classes

The Extensible object pattern can be used to extend existing runtime classes with new functionality or to add new state to an object. Mainly used with to extend the functionality of the extensible objects like ServiceHostBase, InstanceContext, OperationContext and IChannelContext in WCF can also be used with other .NET classes and objects.

In this post i will explain how to extend the button control in System.Windows.Forms namespace using the IExtensibleObject interface. I can use the Extensible pattern to extend the existing Control and add the extensions.

public class ExtensibleButton : Button, IExtensibleObject<ExtensibleButton>

{

public ExtensibleButton() : base()

{

this.BackColor = Color.Gold;

this.Extensions = new ExtensionCollection<ExtensibleButton>(this);

}

#region IExtensibleObject Members

public IExtensionCollection<ExtensibleButton> Extensions

{

get;

private set;

}

#endregion

}

And my extension is like

public class ButtonExtension : IExtension<ExtensibleButton>

{

#region IExtension Members

public void Attach(ExtensibleButton owner)

{

owner.Click += new EventHandler (

delegate( Object sender, EventArgs e)

{

MessageBox.Show("Clicked!!!");

});

}

public void Detach(ExtensibleButton owner)

{

//Do nothing here

}

#endregion

}

Now later if I want to add something new to the control like changing a text after the first extension is added. All is to add another extension and then add that to the extensions collection of the new Button control like…

public class ButtonTextExtension : IExtension<ExtensibleButton>

{

#region IExtension Members

public void Attach(ExtensibleButton owner)

{

owner.Click += new EventHandler(

delegate(Object sender, EventArgs e)

{

owner.Text = "Another Extension";

});

}

public void Detach(ExtensibleButton owner)

{

//Do nothing

}

#endregion

}

And then change the code in the first extension to

public class ButtonExtension : IExtension<ExtensibleButton>

{

#region IExtension Members

public void Attach(ExtensibleButton owner)

{

owner.Click += new EventHandler (

delegate( Object sender, EventArgs e)

{

MessageBox.Show("Clicked!!!");

owner.Extensions.Add(new ButtonTextExtension());

});

}

public void Detach(ExtensibleButton owner)

{

//Do nothing here

}

#endregion

}

That’s all.

My form constructor looks like

public Form1()

{

InitializeComponent();

_demoExtBtn.Extensions.Add(new ButtonExtension());

}

You can use the Extensible pattern very effectively in all situations where you need to extend the functionality of an existing class.

Wednesday, October 8, 2008

Creating multiple channels using ChannelFactory in WCF

The System.ServiceModel.ChannelFactory class creates channels of different types that are used by clients to send messages to variously configured service endpoints. The channelFactory class is useful when you want share a common service contract dll between the client and the server. In this post I will show how to create a ChannelFactory implementation using WCF.

  • Create a new ClassLibrary project and add references to System.ServiceModel and System.Runtime.Serialization namespaces.
  • Create a service contract and data contract in the newly created project

[ServiceContract]

public interface IWCFService

{

[OperationContract]

string SayHello(Employee emp);

}

[DataContract(Namespace = "ChannelFactoryTest", Name = "Employee")]

public class Employee

{

[DataMember]

public int Id { get; set; }

[DataMember]

public string Name { get; set; }

public override string ToString()

{

return this.Name;

}

}

  • Create the class that implements the IWCFService interface

public class WCFService : IWCFService

{

public string SayHello(Employee emp)

{

return "Hello " + emp.ToString();

}

}

You can use this service contract and Data Contract created from both the Client and Host application for the service.

  • Create a new Console Application and name it WCFHost and add references to the already created project along with System.ServiceModel and System.Runtime.Serialization assemblies.
  • In the main method add the following lines to run the application as a Service Host

static void Main(string[] args)

{

Uri[] baseAddresses = new Uri[] { new Uri("net.tcp://localhost:8001/") };

using (ServiceHost host = new ServiceHost(typeof(WCFService), baseAddresses))

{

host.Open();

Console.WriteLine("Service runnning...");

Console.ReadKey();

host.Close();

}

}

  • Add a new App.Config file and add the following lines to the configuration Section of the application configuration file.

<configuration>

<system.serviceModel>

<services>

<service name="ChannelFactoryTest.WCFService">

<endpoint address="WCFService" binding="netTcpBinding" contract=" ChannelFactoryTest.IWCFService" />

service>

services>

system.serviceModel>

configuration>

  • Create another Console application in the same solution and name it WCFClient and add references to the ChannelFactoryTest project and System.ServiceModel and System.Runtime.Serialization assemblies.
  • Add an application configuration file and add the following lines to the configuration Section of the application configuration file.

<configuration>

<system.serviceModel>

<client>

<endpoint name="WCFServiceConfiguration" address="net.tcp://localhost:8001/WCFService" binding="netTcpBinding" contract=" ChannelFactoryTest.IWCFService" />

client>

system.serviceModel>

configuration>

  • Change the main method of the client to use a ChannelFactory to create a proxy for using the service.

static void Main(string[] args)

{

Console.WriteLine("Press any key to start...");

Console.ReadKey();

ChannelFactoryTest.IWCFService proxy = new ChannelFactory< ChannelFactoryTest.IWCFService>IWCFService>("WCFServiceConfiguration").CreateChannel();

ChannelFactoryTest.Employee emp = new ChannelFactoryTest.Employee { Id = 1, Name = "Prajeesh" };

Console.WriteLine(proxy.SayHello(emp));

((IChannel)proxy).Close();

Console.ReadLine();

}

Friday, October 3, 2008

Tutorial: Creating a simple ADO.NET Data Service application.

ADO.NET Data Services enable applications to expose data as a data service that can be consumed by web clients within corporate networks and across the internet. A data service is reachable via regular HTTP requests, using standard HTTP verbs such as GET, POST, PUT and DELETE to perform CRUD operations against the service. The payload format used by the service is controllable by the application, but all options are simple, open formats such as JSON and Atom/APP.

This article will help you get quick Hands on experience on ADO.NET entity framework using VS 2008 and LINQ to SQL classes.

  1. Create a new ASP.NET web site / ASP.NET web application and name it AdventureWorksServices. (I am going to use the AdventureWorks sample database provided by MS for creating the ADO.NET Data service.
  2. Add a new LINQ to SQL class and name it AdventureWorksORM.dbml to the project. Drag and drop the respective tables from the AdventureWorks database for the entities.

  3. Add a new ADO.NET Data Service class and name it AdventureWorkService.svc. Change the class definition for the AdventureWorkService class to add the AdventureWorksORMDataContext class as given below

    public class AdventureWorksService : WebDataService<AdventureWorksORMDataContext>

    {

    public static void InitializeService(IWebDataServiceConfiguration config)

    {

    config.SetResourceContainerAccessRule("*", ResourceContainerRights.AllRead);

  4. Run the web site/ web application and you can start querying the database from the URI as given below.

  5. For getting the row 1 value for Product.

Web Service/ WCF callouts and async page support in ASP.NET 2.0

With asynchronous page support in ASP.NET, we can register one or more tasks with the RegisterAsyncTask method for the runtime to execute asynchronously. In this article I will explain how to use the async page support in ASP.NET for calling web services/ WCF services to get the data for processing.

WEB SERVICE

We will create a simple Web Service method as:

public class CustomerService : System.Web.Services.WebService

{

[WebMethod]

public Collection<Customer> GetCustomers()

{

return new Collection<Customer>

{

new Customer { CustomerID = 1, CustomerName = "Prajeesh" },

new Customer { CustomerID = 2, CustomerName = "Sachin" },

new Customer { CustomerID = 3, CustomerName = "Rahul" },

new Customer { CustomerID = 4, CustomerName = "Varun" },

new Customer { CustomerID = 5, CustomerName = "Rachel" },

new Customer { CustomerID = 6, CustomerName = "Monica" },

new Customer { CustomerID = 7, CustomerName = "Micheal" },

new Customer { CustomerID = 8, CustomerName = "John" }

};

}

}

[Serializable]

public class Customer

{

public Int32 CustomerID { get; set; }

public String CustomerName { get; set; }

}

And the proxy class generated for this web service has the Asynchronous methods generated for the GetCustomers method.

Now Compared to VS 2005 proxy generation one major difference in VS 2008 is the missing of BeginXXX and EndXXX methods. All we get for asnyc processing is:

<>

public event GetCustomersCompletedEventHandler GetCustomersCompleted;

public void GetCustomersAsync() {

this.GetCustomersAsync(null);

}

public delegate void GetCustomersCompletedEventHandler(object sender, GetCustomersCompletedEventArgs e);

public partial class GetCustomersCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs {

private object[] results;

internal GetCustomersCompletedEventArgs(object[] results, System.Exception exception, bool cancelled, object userState) :

base(exception, cancelled, userState) {

this.results = results;

}

public Customer[] Result {

get {

this.RaiseExceptionIfNecessary();

return ((Customer[])(this.results[0]));

}

}

}

Now in the asp.net code behind file to access this method we need to add few lines like:

public partial class _Default : System.Web.UI.Page

{

CollectionCustomer> customers;

CustomerServiceHost.CustomerService service;

protected void Page_Load(object sender, EventArgs e)

{

if (!Page.IsPostBack)

{

PreRenderComplete += new EventHandler(

delegate

{

_customerGridView.DataSource = customers;

_customerGridView.DataBind();

_resultLabel.Text = "Sucess";

});

service = new AsyncWebApplication.CustomerServiceHost.CustomerService();

service.GetCustomersCompleted +=

new AsyncWebApplication.CustomerServiceHost.GetCustomersCompletedEventHandler(

delegate(Object s, AsyncWebApplication.CustomerServiceHost.GetCustomersCompletedEventArgs ce)

{

customers = new CollectionCustomer>(ce.Result.ToList());

});

service.GetCustomersAsync();

}

}

public override void Dispose()

{

if (service != null) service.Dispose();

base.Dispose();

}

}

WCF SERVICE

Our WCF Service consists of a ServiceContract and a DataContract like:

[ServiceContract]

public interface ICustomerWCFService

{

[OperationContract]

Collection<WCFCustomer> GetCustomers();

}

[DataContract]

public class WCFCustomer

{

[DataMember]

public Int32 CustomerID { get; set; }

[DataMember]

public String CustomerName { get; set; }

}

And the Service implementation is like

public class CustomerWCFService : ICustomerWCFService

{

public Collection<WCFCustomer> GetCustomers()

{

return new Collection<WCFCustomer>

{

new WCFCustomer { CustomerID = 1, CustomerName = "Prajeesh" },

new WCFCustomer { CustomerID = 2, CustomerName = "Sachin" },

new WCFCustomer { CustomerID = 3, CustomerName = "Rahul" },

new WCFCustomer { CustomerID = 4, CustomerName = "Varun" },

new WCFCustomer { CustomerID = 5, CustomerName = "Rachel" },

new WCFCustomer { CustomerID = 6, CustomerName = "Monica" },

new WCFCustomer { CustomerID = 7, CustomerName = "Micheal" },

new WCFCustomer { CustomerID = 8, CustomerName = "John" }

};

}

}

Now the proxy class generated using the SVCUTIL with Asynchronous options gives you the BeginXXX and EndXXX methods on which we can use the PageAsyncTask method for asp.net asnyc processing.

Code:

public partial class _Default : System.Web.UI.Page

{

PageAsyncTask customerGetAsyncTask;

ListWCFCustomer> wcfCustomers;

CustomerWCFServiceProxy.ICustomerWCFService wcfService;

protected void Page_Load(object sender, EventArgs e)

{

if (!Page.IsPostBack)

{

PreRenderComplete += new EventHandler(

delegate

{

_customerGridView.DataSource = wcfCustomers;

_customerGridView.DataBind();

_resultLabel.Text = "Sucess";

});

wcfService = new CustomerWCFServiceProxy.CustomerWCFServiceClient();

}

customerGetAsyncTask = new PageAsyncTask(

delegate(Object beginSender, EventArgs beginArgs, AsyncCallback asynCb, Object asyncState)

{

return wcfService.BeginGetCustomers(asynCb, asyncState);

},

delegate(IAsyncResult asynEndResult)

{

wcfCustomers = wcfService.EndGetCustomers(asynEndResult).ToList();

},

delegate(IAsyncResult asynTimeOutResult)

{

_resultLabel.Text = "Timeout!!!";

},

null);

RegisterAsyncTask(customerGetAsyncTask);

}

}

Note: You can also implement parallel processing for asynchronous methods using the PageAsyncTask methods by passing true as parameter in the overloaded method.