在C#和Visual Studio 2005年大会之间循环引用 [英] Circular reference between Assemblies in C# and Visual Studio 2005
问题描述
我正在努力使所有应用程序的单层分层/ n层设计标准化。
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.
代码:
假设我正在为用户开发具有登录/注销功能的应用程序。我在VS2005解决方案下创建4个项目。每个项目是上4层之一。
我设计我的业务对象类如下: -
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:-
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()是getter函数。这些函数不匹配真实世界的逻辑。 Coz,在现实世界中,用户从不按用户名获取或通过UserTypeCode获取。因此,这些函数保持静态。
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.
我的类为OR Mapper图层如下: -
My class for O-R 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;
}
}
最后,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;
}
}
如果任何人仔细检查这些类,他可以理解,OR映射器层需要BusinessObject-layer的引用。 BusinessObject- layer还需要一个OR Mapper-layer的引用。
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.
如何避免此问题?
有人建议使用纯数据传输对象(DTO)。但是,据我所知,根据OOP,现实世界对象的属性和功能应该被分组在一起作为一个类。如果我使用DTO,那么如何将功能封装到类中?此外,我创建另一个类没有任何属性(BO)。对我来说,这是违反OOP在两个方面。如果我这样做,那么在这个世界是什么是OOP?相同的答案可以应用于UserManager类。
Someone suggested using plain Data Transfer Objects (DTO). But, as far as I know, according to OOP, attributes and functionality of a real-world object should be grouped together as a class. If I use DTO then how can I encapsulate functionality into a class? Moreover I am creating another class without any attribute (BO). To me that is breach of OOP in both ways. If I do so, then what is OOP for in this world? The same answer can be applied for "UserManager" classes.
我发现了一个博客。
它讨论了实现接口。定义一个单独的接口,在BusinessObject中的数据类中实现它,并在BusinessObject和OR-Mapper层中的接口上进行编程。
It discusses about implementing interfaces. Define a separate interface, implement it in your data class in the BusinessObject and program against your interface in the BusinessObject and in the OR-Mapper layer.
但我不能
任何人都可以用一个实际例子告诉我。
Can anyone show me that with a practical example?
推荐答案
p>我认为有几件事情你可以做,一起可以帮助您的设计。我也认为您可能想阅读依赖注入,或许可以提供更好的设计
I think there are a few things you can do that together could help with your design. I also think that you might want to read up on Dependency Injection as perhaps providing a better design pattern for what you want to do.
-
首先,从
User
类中删除static
方法,创建'用户,因此最好只保留在UserMapper
。
First, remove the
static
methods from yourUser
class, since they 'create' users, and therefore are best just left on theUserMapper
.
仍然有许多方法可能使用 User
类中的 UserMapper
功能。创建支持 UserNameExists
和 UserNamePasswordExists $ c>的接口
IUserLookup
$ c> methods;将此界面置于与 User
类相同的项目中。
After that, there will still be a number of methods potentially that use UserMapper
functionality from the User
class. Create an interface IUserLookup
(or something) that supports the UserNameExists
and UserNamePasswordExists
methods; put this interface in the same project as the User
class.
实现 IUserLookup
在 UserMapper
类中,然后将它注入到 User
通过构造函数使用静态方法创建的实例,所以基本上,如 UserMapper
创建用户
对象,
Implement the IUserLookup
on the UserMapper
class, and then 'inject' it into the User
class instances it creates with the static methods through a constructor, so basically, as the UserMapper
creates User
objects, it gives them a reference to the IUserLookup
interface that it implements itself.
这样, User
只使用在同一个解决方案中的 IUserLookup
上的方法,因此不需要引用。 UserMapper
引用此解决方案,因此可以创建 User
对象并实现 IUserLookup
接口。
In this way, User
only uses methods on IUserLookup
, which is in the same solution, so no reference needed. And UserMapper
references this solution, so it can create User
objects and implement the IUserLookup
interface.
这篇关于在C#和Visual Studio 2005年大会之间循环引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!