I am working hard to standardize one single way of Layered/n-Tiered design of my all applications.
I am trying to make all my applications 5 tiered.
Code:
--------------------
| UI |
--------------------
|
--------------------
| Business Object |
--------------------
|
--------------------
| OR-Mapper |
--------------------
|
--------------------
| Data Access |
--------------------
|
--------------------
| RDBMS |
--------------------
Suppose I am developing an application with a log-in/log-out capability for users. I am creating 4 projects under a VS2005 solution. Each project is for one of the upper 4 layers.
I am designing my Business Object class as follows:-
Code:
public class User
{
private string _usercode;
public string UserCode
{
get { return _usercode; }
set { _usercode = value; }
}
private DateTime _regDate;
public DateTime RegDate
{
get { return _regDate; }
set { _regDate = value; }
}
private string _username;
public string Username
{
get { return _username; }
set { _username = value; }
}
private string _password;
public string Password
{
get { return _password; }
set { _password = value; }
}
private string _userTypeCode;
public string UserTypeCode
{
get { return _userTypeCode; }
set { _userTypeCode = value; }
}
private string _loginMessage;
public string LoginMessage
{
get { return _loginMessage; }
}
public User()
{
}
public bool LogIn(String username, String password)
{
bool success = false;
if (RegisteredUserMapper.UsernameExists(username))
{
success = RegisteredUserMapper.UsernamePasswordExists(username, password);
if (success)
{
_loginMessage = "Login was successful!";
}
else
{
_loginMessage = "Password mismatch!";
}
}
else
{
_loginMessage = "Username does not exist!";
}
return success;
}
public bool LogOut()
{
bool success;
//----some logic
return success;
}
public static User GetUserByUsername(string username)
{
return UserMapper.GetUserByUsername(username);
}
public static UserCollection GetByUserTypeCode(string code)
{
return UserMapper.GetByUserTypeCode(code);
}
}
This is how I am giving my objects some functionality that matches the real-world scenario. Here GetByUsername() and GetByUserTypeCode() are getter functions. These functions does't match a real-world logic. Coz, in real-world, a User never "Gets by Username" or "Gets by UserTypeCode". So these functions are kept static.
My class for O-R Mapper layer is as follows:-
Code:
public static class UserMapper
{
public static bool UsernameExists(String username)
{
bool exists = false;
if (UserDA.CountUsername(username) == 1)
{
exists = true;
}
return exists;
}
public static bool UsernamePasswordExists(String username, String password)
{
bool exists = false;
if (UserDA.CountUsernameAndPassword(username, password) == 1)
{
exists = true;
}
return exists;
}
public static User GetUserByUsername(string username)
{
User user = null;
SqlDataReader dataReader = UserDA.GetUserByUsername(username);
if (dataReader != null)
{
if (user == null)
{
user = new User();
}
while (dataReader.Read())
{
user.Username = dataReader.GetString(1);
user.UserCode = dataReader.GetString(2);
user.Password = dataReader.GetString(3);
user.RegDate = dataReader.GetDateTime(4);
user.UserTypeCode = dataReader.GetString(5);
}
dataReader.Close();
}
return user;
}
public static UserCollection GetByUserTypeCode(string code)
{
UserCollection users = null;
SqlDataReader dataReader = UserDA.GetUserByUserTypeCode(code);
if (dataReader != null)
{
if (users == null)
{
users = new UserCollection();
}
User user = null;
while (dataReader.Read())
{
user = new User();
user.Username = dataReader.GetString(1);
user.UserCode = dataReader.GetString(2);
user.Password = dataReader.GetString(3);
user.RegDate = dataReader.GetDateTime(4);
user.UserTypeCode = dataReader.GetString(5);
users.AddItem(user);
}
dataReader.Close();
}
return users;
}
}
And finally, the DA class is as follows:-
Code:
public static class UserDA
{
#region Checks the availability of a Username
public static int CountUsername(string username)
{
int count = -1;
SqlConnection conn = DBConn.Connection;
if (conn != null)
{
try
{
SqlCommand command = new SqlCommand();
command.Connection = conn;
command.CommandText = @"SELECT COUNT(*)
FROM User
WHERE User_name = @User_name";
command.Parameters.AddWithValue("@User_name", username);
command.Connection.Open();
object idRaw = command.ExecuteScalar();
command.Connection.Close();
if (idRaw == DBNull.Value)
{
count = 0;
}
else
{
count = (int)idRaw;
}
}
catch (Exception ex)
{
count = -1;
}
}
return count;
}
#endregion Checks the availability of a Username
#region Checks the availability of a Username OR Code/Roll
public static int CountUsernameOrCodeRoll(string username, string codeOrRoll)
{
int count = -1;
SqlConnection conn = DBConn.Connection;
if (conn != null)
{
try
{
SqlCommand command = new SqlCommand();
command.Connection = conn;
command.CommandText = @"SELECT COUNT(*)
FROM User
WHERE User_name = @User_name
OR ActualCodeOrRoll = @ActualCodeOrRoll";
command.Parameters.AddWithValue("@User_name", username);
command.Parameters.AddWithValue("@ActualCodeOrRoll", codeOrRoll);
command.Connection.Open();
object idRaw = command.ExecuteScalar();
command.Connection.Close();
if (idRaw == DBNull.Value)
{
count = 0;
}
else
{
count = (int)idRaw;
}
}
catch (Exception ex)
{
count = -1;
}
}
return count;
}
#endregion Checks the availability of a Username OR Code/Roll
public static int CountUsernameAndPassword(string username, string password)
{
int count = 0;
SqlConnection conn = DBConn.Connection;
if (conn != null)
{
try
{
SqlCommand command = new SqlCommand();
command.Connection = conn;
command.CommandText = @"SELECT COUNT(*)
FROM User
WHERE User_name = @User_name AND Pass_word = @Pass_word";
command.Parameters.AddWithValue("@User_name", username);
command.Parameters.AddWithValue("@Pass_word", password);
command.Connection.Open();
object idRaw = command.ExecuteScalar();
command.Connection.Close();
if (idRaw == DBNull.Value)
{
count = 0;
}
else
{
count = (int)idRaw;
}
}
catch (Exception ex)
{
count = 0;
}
}
return count;
}
public static int InsertUser(params object[] objects)
{
int count = -1;
SqlConnection conn = DBConn.Connection;
if (conn != null)
{
try
{
SqlCommand command = new SqlCommand();
command.Connection = conn;
command.CommandText = @"INSERT INTO User(ID, User_name, Pass_word, RegDate, UserTypeCode, ActualCodeOrRoll)
VALUES(@ID, @User_name, @Pass_word, @RegDate, @UserTypeCode, @ActualCodeOrRoll)";
command.Parameters.AddWithValue("@ID", objects[0]);
command.Parameters.AddWithValue("@User_name", objects[1]);
command.Parameters.AddWithValue("@Pass_word", objects[2]);
command.Parameters.AddWithValue("@RegDate", objects[3]);
command.Parameters.AddWithValue("@UserTypeCode", objects[4]);
command.Parameters.AddWithValue("@ActualCodeOrRoll", objects[5]);
command.Connection.Open();
count = command.ExecuteNonQuery();
command.Connection.Close();
}
catch (Exception ex)
{
count = -1;
}
}
return count;
}
public static SqlDataReader GetUserByUsername(string username)
{
SqlDataReader dataReader = null;
SqlConnection conn = DBConn.Connection;
if (conn != null)
{
try
{
SqlCommand command = new SqlCommand();
command.Connection = conn;
command.CommandText = @"SELECT * FROM User WHERE User_name = @User_name";
command.Parameters.AddWithValue("@User_name", username);
command.Connection.Open();
dataReader = command.ExecuteReader(CommandBehavior.CloseConnection);
}
catch (Exception ex)
{
dataReader.Close();
dataReader.Dispose();
}
}
return dataReader;
}
public static SqlDataReader GetUserByUserTypeCode(string userTypeCode)
{
SqlDataReader dataReader = null;
SqlConnection conn = DBConn.Connection;
if (conn != null)
{
try
{
SqlCommand command = new SqlCommand();
command.Connection = conn;
command.CommandText = @"SELECT * FROM User WHERE UserTypeCode = @UserTypeCode";
command.Parameters.AddWithValue("@UserTypeCode", userTypeCode);
command.Connection.Open();
dataReader = command.ExecuteReader(CommandBehavior.CloseConnection);
}
catch (Exception ex)
{
dataReader.Close();
dataReader.Dispose();
}
}
return dataReader;
}
}
If anyone closely examine the these classes he can understand that, O-R Mapper layer needs the reference of BusinessObject-layer. BusinessObject- layer also needs a reference of O-R Mapper-layer.
This should create a circular dependency.
How can I avoid this problem?
N.B. I don't know whether it is an ideal 5-tiered application or not, I have found some logic in favor of this design.