Loopback between assemblies in C # and Visual Studio 2005

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 { //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); } } 

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?

+4
source share
4 answers

I think there are a few things you can do that together can help with your design. I also think that you can read the Injection of Dependency as the best design sample possible for what you want to do.

Assuming you just want to do what you have:

  • First, remove the static methods from your User class, as they "create" users, and so it's best to leave them on UserMapper .

  • After that, there will still be a number of methods that can use the UserMapper functionality from the User class. Create an IUserLookup interface (or something else) that supports the UserNameExists and UserNamePasswordExists methods; put this interface in the same project as the User class.

  • IUserLookup into the UserMapper class, and then add it to instances of the User class that it creates using static methods through the constructor, so basically, since UserMapper creates the User , it gives them a reference to the IUserLookup interface that it implements itself.

Thus, User only uses methods on IUserLookup that are in one solution, so no link is required. And UserMapper refers to this solution, so it can create User objects and implement the IUserLookup interface.

+4
source

If OR Mapper actually executes OR, then probably it does not need a reference to BL - it just needs to know Type (s), which (is) involved. But this is a side question ...

The main answer to this question is “Inversion of control” / “Injection of dependencies”, presumably trimming everything under BL - therefore BL depends only on the interface (defined in the base assembly), but does not know about a specific OR / DA / RDBMS (they are supplied by IoC / DI).

This is a big topic, so I'm intentionally vague. Personally, I like StructureMap, but there are many IoC / DI tools available.

Please note that from a technical point of view, it is possible to create backlinks for the assembly; this is a bad idea indeed , and - the tools will (intentionally) fight you at every turn.

+3
source

In the code you indicated above, there are no signs of circular dependence.

When your call moves from the upper layers to the lower layers ... your object is converted to a specialization corresponding to each layer (however, in your case, you are dealing with primitives at each level ... at least in the code presented) ... And when your call comes back, it should be from specialization to generalization ....

It can be the other way around, and there is no problem with cyclic dependence if one path is observed in this way. However, if in any layer you are trying to implement the Specilization script for the side information path, then we have a problem, since each layer will depend on and requires a reference to its covering level.

But in your code there is no such evidence of circular dependence. However, if such a case can be avoided by implementing the interface layer or adapter template (interface level - adapter template).

For example, we have Layer InformationTravel (IT) ... (OK, I understand this is not too good)

| User interface | | | InformationTravel | ** | | Business object | | | OR-Mapper | | | Data Access | | | RDBMS |

What do you do for your custom business object, declare the IUser interface and implement it in the user business object ....

Then BO has a link to IT. Creating an object should only be in the BO layer, which implements the interface from IT. This is completely normal. When you need to transfer this object to ORM, you transfer it by cutting it to the interface implemented in IT, and when you receive it, you will return the same object again after making the necessary changes.

But this again suggests that you cannot create your BO at several levels, since only BO has an interface implementation. However, if you really cannot avoid this, you need to provide implementation in several places or use an adapter template (encapsulate your object in another object expected by the client).

0
source

To prevent circular reference between assemblies, you should use interfaces. For example, if your OR-Mapper needs to call some BL members, you must recognize these elements and put them in one or more interfaces and put them either in the assembly (Interfaces for example) or in your OR-Mapper assembly, and let your BL objects implement them, then there will be no need to refer to your BL in your OR-Mapper assembly.

0
source

All Articles