Я прилагаю все усилия, чтобы стандартизировать единый подход ко всем приложениям Layered / n-Tiered.
Я пытаюсь сделать все свои приложения 5-уровневыми.
Код:
| UI |
|
| Бизнес-объект |
|
| OR-Mapper |
|
| Доступ к данным |
|
| СУРБД |
Предположим, я разрабатываю приложение с возможностью входа / выхода для пользователей. Я создаю 4 проекта в рамках решения VS2005. Каждый проект для одного из верхних 4 слоев.
Я проектирую свой класс Business Object следующим образом: -
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
{
//do nothing
}
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);
}
}
Вот как я даю своим объектам некоторую функциональность, соответствующую реальному сценарию. Здесь GetByUsername () и GetByUserTypeCode () являются функциями получения. Эти функции не соответствуют реальной логике. Ведь в реальности пользователь никогда не «получает по имени пользователя» или «получает по типу пользователя». Таким образом, эти функции остаются неизменными.
Мой класс для слоя O-R Mapper выглядит следующим образом: -
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;
}
}
И, наконец, класс DA выглядит следующим образом: -
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;
}
}
Если кто-нибудь внимательно изучит эти классы, он поймет, что для слоя O-R Mapper необходима ссылка на слой BusinessObject. BusinessObject-layer также требуется ссылка на O-R Mapper-layer.
Это должно создать круговую зависимость.
Как мне избежать этой проблемы?
Кто-то предложил использовать простые объекты передачи данных (DTO). Но, насколько я знаю, согласно ООП атрибуты и функциональные возможности объекта реального мира должны быть сгруппированы как класс. Если я использую DTO, то как я могу инкапсулировать функциональность в класс? Более того, я создаю другой класс без каких-либо атрибутов (BO). Для меня это нарушение ООП в обоих направлениях. Если я это сделаю, то для чего ООП в этом мире? Тот же ответ может быть применен к классам "UserManager".
Я нашел блог .
В нем обсуждается реализация интерфейсов. Определите отдельный интерфейс, внедрите его в свой класс данных в BusinessObject и запрограммируйте на свой интерфейс в BusinessObject и на уровне OR-Mapper.
Но я не смог этого сделать.
Может кто-нибудь показать мне это на практическом примере?