|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionRecently, Microsoft released the source code for the built-in Providers that ship with the .NET 2.0 framework. While I never downloaded them, this release caught my attention, so I started to study this new feature of ASP.NET. I was really impressed by the power of the build providers (I don't know why they didn't get my attention until now), so I decided to share my weekend experience with you. Declarative type creationI started this process by creating an XML file in the App_Code, trying to dynamically create a type from it. Here is the file's content: <types>
<class name="User" namespace="Mapper.Core">
<property name="ID" type="System.Int32" />
<property name="FirstName" type="System.String" />
<property name="LastName" type="System.String" />
<property name="Username" type="System.String" />
<property name="Password" type="System.String" />
<property name="Email" type="System.String" />
</class>
</types>
After reading some articles about custom build providers (one of them being Javier Lozano's great article which can be found here) and others about CodeDom, I started to build my test provider. I created the public override void GenerateCode(AssemblyBuilder assemblyBuilder)
{
string fileName = base.VirtualPath;
CodeCompileUnit generatedUnit = GenerateUnit(fileName);
assemblyBuilder.AddCodeCompileUnit(this, generatedUnit);
}
The <compilation debug="true">
<buildProviders>
<add extension=".xml"
type="ObjectMapper.EntityBuildProvider, ObjectMapper"/>
</buildProviders>
</compilation>
And the result is a generated type with full intellisense support in Visual Studio 2005:
Using some string utility methods, I've added support for PascalCase/camelCase code generation, and the ASP.NET engine creates my namespace Mapper.Core
{
public class User
{
private System.Int32 _id;
private System.String _firstName;
private System.String _lastName;
private System.String _username;
private System.String _password;
private System.String _email;
public User()
{
}
public System.Int32 ID
{
get { return _id; }
set { _id = value; }
}
public System.String FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public System.String LastName
{
get { return _lastName; }
set { _lastName = value; }
}
public System.String Username
{
get { return _username; }
set { _username = value; }
}
public System.String Password
{
get { return _password; }
set { _password = value; }
}
public System.String Email
{
get { return _email; }
set { _email = value; }
}
}
}
After adding some additional properties for my <types>
<class name="User" namespace="Mapper.Core">
<property name="ID" type="System.Int32" />
<property name="FirstName" type="System.String" />
<property name="LastName" type="System.String" />
<property name="Username" type="System.String" />
<property name="Password" type="System.String" />
<property name="Email" type="System.String" />
<property name="Enabled" type="System.Boolean" />
<property name="Phone" type="System.String" />
<property name="Address1" type="System.String" />
<property name="Address2" type="System.String" />
</class>
</types>
Extending my custom languageSeeing that my custom build provider allows me to use all the power of the .NET framework to implement a custom programming language (even if one of my favorite CodeProject member wrote a great article about the limitations), I started to add more features: For the
For the
I've also created some additional string utility methods for getting the plural/singular for a name, also the generated properties have some basic XML comments. Because the purpose of this article is to create custom build providers, we'll not discuss about the CodeDom implementation process. However, if you're interested in this namespace, you can try reading an excellent article which can be found here. The final XML file would look like this: <?xml version="1.0" encoding="utf-8" ?>
<types>
<class name="User" table="Users" namespace="Mapper.Core"
SqlStoredProceduresPrefix="DB_"
GenerateStoredProcedures="true"
ExportLocation="E:\GeneratedFiles\">
<property name="ID" column="ID" IsIdentity="true"
IsPrimaryKey="true"
type="System.Int32" SqlType="int" />
<property name="FirstName" column="FirstName"
type="System.String" SqlType="nvarchar"
SqlLength="50" />
<property name="LastName" column="LastName"
type="System.String" SqlType="nvarchar"
SqlLength="50" />
<property name="Username" column="Username"
type="System.String" SqlType="nvarchar"
SqlLength="50" />
<property name="Password" column="Password"
type="System.String" SqlType="nvarchar"
SqlLength="50" />
<property name="Enabled" column="Enabled"
type="System.Boolean" SqlType="bit" />
<property name="Email" column="Email"
type="System.String" SqlType="nvarchar"
SqlLength="50" />
<property name="Phone" column="Phone"
type="System.String" SqlType="nvarchar"
SqlLength="50" />
<property name="Address1" column="Address1"
type="System.String" SqlType="nvarchar"
SqlLength="50" />
<property name="Address2" column="Address2"
type="System.String" SqlType="nvarchar"
SqlLength="50" />
</class>
</types>
And the generated code by the custom provider: namespace Mapper.Core {
[System.Serializable()]
public class User : ObjectMapper.Utils.Entity {
private int _id;
private string _firstName;
private string _lastName;
private string _username;
private string _password;
private bool _enabled;
private string _email;
private string _phone;
private string _address1;
private string _address2;
/// <summary>
/// Gets or sets the user's identifier.
/// </summary>
public int ID {
get {
return _id;
}
set {
if ((value != this._id)) {
this._id = value;
base.MarkDirty();
}
}
}
/// <summary>
/// Gets or sets the user's first name.
/// </summary>
public string FirstName {
get {
return _firstName;
}
set {
if ((value != this._firstName)) {
this._firstName = value;
base.MarkDirty();
}
}
}
// ----------------------------------------
// All other properties go here
// ----------------------------------------
public static Mapper.Core.User GetUser(int id) {
System.Data.SqlClient.SqlCommand selectCommand;
selectCommand =
ObjectMapperUtils.DataUtility.CreateCommand(
"DB_Users_Select");
selectCommand.Parameters.AddWithValue("@ID", id);
System.Collections.Generic.List<Mapper.Core.User> users;
users = Mapper.Core.User.UserListFromReader(
ObjectMapperUtils.DataUtility.ExecuteReader(
selectCommand));
if ((users.Count > 0)) {
return users[0];
}
return null;
}
public override void Insert() {
System.Data.SqlClient.SqlCommand insertCommand;
insertCommand =
ObjectMapperUtils.DataUtility.CreateCommand(
"DB_Users_Insert");
insertCommand.Parameters.AddWithValue("@FirstName", this.FirstName);
insertCommand.Parameters.AddWithValue("@LastName", this.LastName);
insertCommand.Parameters.AddWithValue("@Username", this.Username);
insertCommand.Parameters.AddWithValue("@Password", this.Password);
insertCommand.Parameters.AddWithValue("@Enabled", this.Enabled);
insertCommand.Parameters.AddWithValue("@Email", this.Email);
insertCommand.Parameters.AddWithValue("@Phone", this.Phone);
insertCommand.Parameters.AddWithValue("@Address1", this.Address1);
insertCommand.Parameters.AddWithValue("@Address2", this.Address2);
System.Data.IDataReader reader;
reader =
ObjectMapperUtils.DataUtility.ExecuteReader(insertCommand);
if ((reader.Read() == true)) {
this.ID = System.Convert.ToInt32(reader[0]);
}
if ((reader.IsClosed != true)) {
reader.Close();
}
base.MarkOld();
}
public override void Update() {
System.Data.SqlClient.SqlCommand updateCommand;
updateCommand =
ObjectMapperUtils.DataUtility.CreateCommand("DB_Users_Update");
updateCommand.Parameters.AddWithValue("@ID", this.ID);
updateCommand.Parameters.AddWithValue("@FirstName", this.FirstName);
updateCommand.Parameters.AddWithValue("@LastName", this.LastName);
updateCommand.Parameters.AddWithValue("@Username", this.Username);
updateCommand.Parameters.AddWithValue("@Password", this.Password);
updateCommand.Parameters.AddWithValue("@Enabled", this.Enabled);
updateCommand.Parameters.AddWithValue("@Email", this.Email);
updateCommand.Parameters.AddWithValue("@Phone", this.Phone);
updateCommand.Parameters.AddWithValue("@Address1", this.Address1);
updateCommand.Parameters.AddWithValue("@Address2", this.Address2);
ObjectMapperUtils.DataUtility.ExecuteNonQuery(updateCommand);
base.MarkOld();
}
public override void Delete() {
System.Data.SqlClient.SqlCommand deleteCommand;
deleteCommand =
ObjectMapperUtils.DataUtility.CreateCommand("DB_Users_Delete");
deleteCommand.Parameters.AddWithValue("@ID", this.ID);
ObjectMapperUtils.DataUtility.ExecuteNonQuery(deleteCommand);
base.MarkNew();
}
internal static void Fetch(Mapper.Core.User user,
System.Data.IDataReader reader) {
Mapper.Core.User.Fetch(user, reader, 0);
}
internal static void Fetch(Mapper.Core.User user,
System.Data.IDataReader reader, int startIndex) {
user.ID = reader.GetInt32((0 + startIndex));
user.FirstName = reader.GetString((1 + startIndex));
user.LastName = reader.GetString((2 + startIndex));
user.Username = reader.GetString((3 + startIndex));
user.Password = reader.GetString((4 + startIndex));
user.Enabled = reader.GetBoolean((5 + startIndex));
user.Email = reader.GetString((6 + startIndex));
user.Phone = reader.GetString((7 + startIndex));
user.Address1 = reader.GetString((8 + startIndex));
user.Address2 = reader.GetString((9 + startIndex));
}
internal static System.Collections.Generic.List<Mapper.Core.User>
UserListFromReader(System.Data.IDataReader reader) {
return Mapper.Core.User.UserListFromReader(reader, 0);
}
internal static System.Collections.Generic.List<Mapper.Core.User>
UserListFromReader(System.Data.IDataReader reader,
int startIndex) {
System.Collections.Generic.List<Mapper.Core.User> users;
users = new
System.Collections.Generic.List<Mapper.Core.User>();
for (
; (reader.Read() == true);
) {
Mapper.Core.User user;
user = new Mapper.Core.User();
Mapper.Core.User.Fetch(user, reader, startIndex);
user.MarkOld();
users.Add(user);
}
if ((reader.IsClosed != true)) {
reader.Close();
}
return users;
}
public static
System.Collections.Generic.List<Mapper.Core.User> GetUsers() {
System.Data.SqlClient.SqlCommand selectAllCommand;
selectAllCommand =
ObjectMapperUtils.DataUtility.CreateCommand(
"DB_Users_SelectAll");
return Mapper.Core.User.UserListFromReader(
ObjectMapperUtils.DataUtility.ExecuteReader(
selectAllCommand));
}
}
}
The final screen shows the intellisense support with the generated XML comments as well.
Using the codeFor testing the code in a separate project, you should add references to the Future ideas
Points of InterestBuild providers are a powerful feature of ASP.NET 2.0, which can increase our productivity pretty much. I hope I was able to show you the advantages we can have if we're using them.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||