|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionHave you ever had to create a data access layer in your projects? It is most probable that you have created a class or classes having several methods to call stored procedures or execute SQL expressions. It is very boredom work especially if the database contains more than a dozen tables. And the worst part of this is that the methods have almost the same steps (create a command object, fill its properties, execute and return a result) and rarely contains some other logic. You have two ways: write the source code manually or generate it. In both ways, there will be a lot of source code. I tried both ways before and I thought that there had to be something easier. The article about AutoSproc Tool gave me an idea and I developed my A simple exampleLet's write two classes to work with the database NorthWind. One class public class Orders1
{
private SqlConnection m_connection = null;
public SqlConnection Connection
{
get{return m_connection;}
set{m_connection = value;}
}
public DataSet CustOrdersDetail(int OrderID)
{
SqlCommand cmd = new SqlCommand("CustOrdersDetail", m_connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@OrderID", SqlDbType.Int);
cmd.Parameters["@OrderID"].Value = OrderID;
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
return ds;
}
public int CountByEmployee(int EmployeeID)
{
SqlCommand cmd = new SqlCommand(
"select count(*) from Orders where EmployeeID=@EmployeeID",
m_connection);
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("@EmployeeID", SqlDbType.Int);
cmd.Parameters["@EmployeeID"].Value = EmployeeID;
int count = (int)cmd.ExecuteScalar();
return count;
}
}
and the other class public abstract class Orders2 : SqlWrapperBase
{
public abstract DataSet CustOrdersDetail(int OrderID);
[SWCommand("select count(*) from Orders where EmployeeID=@EmployeeID")]
public abstract int CountByEmployee(int EmployeeID);
}
Now let's see how these classes are used: SqlConnection cnn = new SqlConnection(
ConfigurationSettings.AppSettings["ConnectionString"]);
cnn.Open();
// working with the ordinary class
Orders1 orders1 = new Orders1();
orders1.Connection = cnn;
DataSet ds1 = orders1.CustOrdersDetail(10248);
int count1 = orders1.CountByEmployee(6);
// working with the wrapped class
Orders2 orders2 = (Orders2)WrapFactory.Create(typeof(Orders2));
orders2.Connection = cnn;
DataSet ds2 = orders2.CustOrdersDetail(10248);
int count2 = orders2.CountByEmployee(6);
The difference is obvious. The class How it worksIn order to create a wrapper class, you have to inherit your class from [SWCommand("select count(*) from Orders where EmployeeID=@EmployeeID")]
public abstract int CountByEmployee(int EmployeeID);
will be implemented like this: [SWCommand("select count(*) from Orders where EmployeeID=@EmployeeID")]
public int CountByEmployee(int EmployeeID)
{
MethodInfo method = (MethodInfo)MethodBase.GetCurrentMethod();
object[] values = new object[1];
values[0] = EmployeeID;
object obj = SWExecutor.ExecuteMethodAndGetResult(
m_connection,
m_transaction,
method,
values,
m_autoCloseConnection);
return (int)obj;
}
The main job is done in the method
Some of these information is taken from the method signature, the other from optional methods and method parameter attributes. The following diagram shows how the
SqlWrapperBase
You can use protected fields in concrete methods of your wrapper class. SWCommandAttributeThis is an optional method attribute and contains the following properties:
All the properties except Important: If this attribute is omitted then the method name is used for SWParameterAttributeThis is an optional parameter attribute and contains the following properties:
All the properties are optional.
InsertUpdate commandsAs SQL
These are examples of
As you can see, in the first example the variable Creating data access layer in your application
public YourWrapperClass YourPropertyName
{
get
{
return (YourWrapperClass)GetWrapped();
}
}
And this is all. You can also add any other members if you need. This is an example of a DAL class that can be created ( public class MyDAL : DataAccessLayerBase
{
public UserClass1 UserClass1{get{return (UserClass1)GetWrapped();}}
public Orders2 Orders2{get{return (Orders2)GetWrapped();}}
}
And this is a class diagram of your DAL class:
And here is an example of how your DAL class can be used: MyDAL dal = new MyDAL();
dal.Init(cnn, true, true);
int c = dal.Orders2.CountByEmployee(6);
DataTable dt = dal.UserClass1.Method1();
Before using an object of your DAL class, you have to call one of the overloaded public void Init(SqlConnection connection, bool autoCloseConnection,
bool ownsConnection);
public void Init(string connectionString, bool autoCloseConnection);
These methods are very important because besides setting of connection properties, they call a private method private void GenerateAllWrapped()
{
MethodInfo[] mis = this.GetType().GetMethods();
for(int i = 0; i < mis.Length; ++i)
{
Type type = mis[i].ReturnType;
if(type.GetInterface(typeof(ISqlWrapperBase).FullName) ==
typeof(ISqlWrapperBase))
{
if(mis[i].Name.StartsWith("get_"))
{
if(!m_swTypes.ContainsKey(mis[i].Name))
{
ISqlWrapperBase sw = WrapFactory.Create(type);
m_swTypes[mis[i].Name] = sw;
}
}
}
}
}
Properties that you have declared call the protected method protected ISqlWrapperBase GetWrapped()
{
MethodInfo mi = (MethodInfo)(new StackTrace().GetFrame(1).GetMethod());
ISqlWrapperBase res = (ISqlWrapperBase)m_swTypes[mi.Name];
if(res == null)
{
throw new SqlWrapperException("The object is not initialized.");
}
return res;
}
Three methods of
In addition, Other languagesI have tested Public MustInherit Class Orders2
Inherits SqlWrapperBase
Public MustOverride Function _
CustOrdersDetail(ByVal OrderID As Integer) As DataSet
<SWCommand("select count(*) from Orders where EmployeeID=@EmployeeID")> _
Public MustOverride Function _
CountByEmployee(ByVal EmployeeID As Integer) As Integer
End Class
and Public Class MyDAL
Inherits DataAccessLayerBase
Public ReadOnly Property UserClass1() As UserClass1
Get
Return GetWrapped()
End Get
End Property
Public ReadOnly Property Orders2() As Orders2
Get
Return GetWrapped()
End Get
End Property
End Class
What is planned to do
History
Reference | ||||||||||||||||||||