Sunday, September 16, 2007

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(IDbCommand cmd);

--------------------------
--------------------------template
--------------------------
///
/// Simple command executer "design pattern".
///

/// The type to return
/// The command
/// The handler which will receive the open command and handle it (as required)
/// A generic defined result, according to the handler choice
public static T ExecuteCommand(IDbCommand cmd, CommandHandler handler) //*1
{
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(IDataReader reader);

-----------------------------------
-----------------------------------
///
/// Execute the db command as reader and parse it via the given handler.
///

/// The type to return after parsing the reader.
/// The command to execute
/// The handler which will parse the reader
/// A generic defined result, according to the handler choice
public static T ExecuteReader(IDbCommand cmd, ReaderHandler handler)
{
return ExecuteCommand(cmd,
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.
///

/// Typed collection of person.
public static List GetPersonsList()
{
IDbCommand cmd = ProviderFactory.CreateCommand();
cmd.CommandText = "SELECT Name,Age,Email FROM Persons";
cmd.CommandType = CommandType.Text;

return DalServices.ExecuteReader>(cmd,
delegate(IDataReader r)
{
List persons = new 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.
///

/// Xml representation of the persons.
public static string GetPersonsXml()
{
IDbCommand cmd = ProviderFactory.CreateCommand();
cmd.CommandText = "SELECT Name,Age,Email FROM Persons";
cmd.CommandType = CommandType.Text;

return DalServices.ExecuteReader(cmd,
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
/// Affected rows number
public static int ExecuteNonQuery(IDbCommand cmd)
{
return ExecuteCommand(cmd,
delegate(IDbCommand liveCommand)
{
return liveCommand.ExecuteNonQuery();
});
}

///
/// Execute the db command in "Scalar mode".
///

/// The type to return after parsing the reader.
/// The command to execute
/// A generic defined result, according to the handler choice
public static T ExecuteScalar(IDbCommand cmd)
{
return ExecuteCommand(cmd,
delegate(IDbCommand liveCommand)
{
return (T)liveCommand.ExecuteScalar();
});
}

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home