I am working hard to standardize one of the Layered / n-Tiered ways of all my applications.
I am trying to make all my applications 5 levels.
code:
| UI |
|
| Business object |
|
| OR-Mapper |
|
| Data Access |
|
| RDBMS |
Suppose I’m developing an entry / exit application for users. I am creating 4 projects in accordance with VS2005 solution. Each project is designed for one of the top 4 layers. I design my business object class as follows: -
public class User { private string _username; public string Username { get { return _username; } set { _username = value; } } private string _password; public string Password { get { return _password; } set { _password = value; } } public User() { } public bool LogIn(String username, String password) { bool success = false; if (UserMapper.UsernameExists(username)) { success = UserMapper.UsernamePasswordExists(username, password); } else {
This is how I provide my objects with some functionality that matches the actual scenario. Here GetByUsername () and GetByUserTypeCode () are getter functions. These functions do not correspond to real logic. Coz, in the real world, a user never "gets by name" or "gets by UserTypeCode". Thus, these functions remain static.
My class for the OR Mapper layer is as follows: -
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; } }
And finally, the DA class is as follows: -
public static class UserDA { 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; } 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 someone carefully examines these classes, they can understand that a link to the BusinessObject layer is required for the OR Mapper layer. The BusinessObject-layer layer also requires a reference to the OR marker layer.
This should create a circular dependency.
How can I avoid this problem?
Someone suggested using simple data transfer objects (DTOs). But, as far as I know, according to OOP, the attributes and functionality of a real-world object should be grouped together as a class. If I use DTO, how can I encapsulate functionality in a class? Moreover, I am creating another class without an attribute (BO). For me, this is a violation of OOP in both directions. If I do, then what is OOP in this world? The same answer can be applied to the "UserManager" classes.
I found a blog .
It discusses the implementation of interfaces. Define a separate interface, implement it in your data class in BusinessObject, and create a program against your interface in BusinessObject and in the OR-Mapper layer.
But I could not do it.
Can someone show me this with a practical example?