simplified command pattern (non-emit) AOP: anonymous delegate and generics
simplified command pattern (non-emit) AOP: anonymous delegate and generics
In my previous blogs, I mentioned that with C# 2.0, we finally can do similar things like java's anonymous inner class for object-adapter pattern. This will simplify command pattern AOP.
Basically, it is an easier and better way of strict inheritance of template method pattern.
Generics here is just a helper, it makes the "template" more like, well, a template -- you do not really need it, a lot of times, if your template does not need to be general.
Note that this is still too expensive, so, it can only be used for facade-level methods; so, it cannot replace real (i.e. emit-based) AOP that can be used for entity get-set methods.
I know, I used too much "patterns" here, so, here are code -- copied/pasted.
-----------------------------------------
-----------------------------------------
public static EmployeeDetails FindEmployee(int empId)
{
WindowsIdentity id = HttpContext.Current.User.Identity as WindowsIdentity;
WindowsImpersonationContext wic = id.Impersonate();
try
{
BrowseOrganizationServiceWse bosw = new BrowseOrganizationServiceWse();
bosw.SetPolicy("kerberos");
FindEmployeeResult fer = bosw.FindEmployee(empId);
EmployeeDetails ed = fer.Item as EmployeeDetails;
return ed;
}
finally
{
wic.Undo();
}
}
--------------------------------templeate ThreadStart deledagate is simply return void
private static void RunMethodImpersonating(ThreadStart method)
{
WindowsIdentity id = HttpContext.Current.User.Identity as WindowsIdentity;
WindowsImpersonationContext wic = id.Impersonate();
try
{
method();
}
finally
{
wic.Undo();
}
}
---------------------------
public static EmployeeDetails FindEmployee(int empId)
{
EmployeeDetails ed = null;
RunMethodImpersonating(
delegate
{
BrowseOrganizationServiceWse bosw = new BrowseOrganizationServiceWse();
bosw.SetPolicy("kerberos");
FindEmployeeResult fer = bosw.FindEmployee(empId.ToString());
ed = fer.Item as EmployeeDetails;
}
);
return ed;
}
-----------------------------
-----------------------------
-----------------------------
-----------------------------
try
{
// cmd is from IDbCommand (SqlCommand, OracleCommand, OleDbCommend etc) type.
// This is the command which we want to run against our database.
using (IDbConnection conn = ProviderFactory.CreateConnection())
{
cmd.Connection = conn;
conn.Open();
// use the cmd object.
} //"using" will close the connection even in case of exception.
}
catch (Exception e)
{
// 1. Trace ?
// 2. Rollback transaction ?
// 3. Throw a wrapper exception with some more information ?
}
--------------------------
--------------------------
--------------------------
public delegate T CommandHandler
--------------------------
--------------------------template
--------------------------
///
/// Simple command executer "design pattern".
///
///
/// The command
/// The handler which will receive the open command and handle it (as required)
///
public static T ExecuteCommand
{
try
{
using (IDbConnection conn = ProviderFactory.CreateConnection()) //*2
{
cmd.Connection = conn;
// Trace the query & parameters.
DatabaseTracer.WriteToTrace(TraceLevel.Verbose, cmd, "Data Access Layer - Query profiler"); //*3
conn.Open();
return handler(cmd); //*4
} //"using" will close the connection even in case of exception.
}
catch (Exception e)
{
// Trace the exception into the same log.
Tracer.WriteToTrace(TraceLevel.Error, e, "Data Access Layer - Exception"); //*5
throw WrapException(e); //*6
}
}
-----------------------------------
-----------------------------------
public delegate T ReaderHandler
-----------------------------------
-----------------------------------
///
/// Execute the db command as reader and parse it via the given handler.
///
///
/// The command to execute
/// The handler which will parse the reader
///
public static T ExecuteReader
{
return ExecuteCommand
delegate(IDbCommand liveCommand) //*1
{
// This is the anonymous delegate handler.
// REMINDER: The original template sends the live command as parameter.
IDataReader r = liveCommand.ExecuteReader();
return handler(r);
});
}
-----------------------------------
-----------------------------------
///
/// Retrieve the persons according to the specified command.
///
///
public static List
{
IDbCommand cmd = ProviderFactory.CreateCommand();
cmd.CommandText = "SELECT Name,Age,Email FROM Persons";
cmd.CommandType = CommandType.Text;
return DalServices.ExecuteReader
- >(cmd,
delegate(IDataReader r)
{
List
while (r.Read())
{
// Create a Person object, fill it by the reader and add it to the "persons" list.
Person person = new Person(r["Name"].ToString(), Convert.ToInt32(r["Age"]), r["Email"].ToString());
persons.Add(person);
}
return persons;
});
}
----------------------------------
----------------------------------
----------------------------------
///
/// Retrieve the persons xml according to the specified command.
///
///
public static string GetPersonsXml()
{
IDbCommand cmd = ProviderFactory.CreateCommand();
cmd.CommandText = "SELECT Name,Age,Email FROM Persons";
cmd.CommandType = CommandType.Text;
return DalServices.ExecuteReader
delegate(IDataReader r)
{
StringBuilder builder = new StringBuilder(500);
builder.Append("
while (r.Read())
{
// Create a Person object, fill it by the reader and add it to the "persons" list.
Person person = new Person(r["Name"].ToString(), Convert.ToInt32(r["Age"]), r["Email"].ToString());
builder.Append(person.ToXml());
}
builder.Append("
return builder.ToString();
});
}
///
/// Execute the db command in "NonQuery mode".
///
/// The command to parse
///
public static int ExecuteNonQuery(IDbCommand cmd)
{
return ExecuteCommand
delegate(IDbCommand liveCommand)
{
return liveCommand.ExecuteNonQuery();
});
}
///
/// Execute the db command in "Scalar mode".
///
///
/// The command to execute
///
public static T ExecuteScalar
{
return ExecuteCommand
delegate(IDbCommand liveCommand)
{
return (T)liveCommand.ExecuteScalar();
});
}