Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Go Back   Wrox Programmer Forums > .NET > .NET 2.0 and Visual Studio. 2005 > Visual Studio 2005
Password Reminder
Register
Register | FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
Visual Studio 2005 For discussing Visual Studio 2005. Please post code questions about a specific language (C#, VB, ASP.NET, etc) in the correct language forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the Visual Studio 2005 section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developersí questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
Reply
 
Thread Tools Display Modes
  #1 (permalink)  
Old April 30th, 2009, 07:18 PM
edurazee
Guest
 
Posts: n/a
Default Layered/Tiered Architecture complexity...

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.
Reply With Quote
  #2 (permalink)  
Old May 3rd, 2009, 04:48 AM
Imar's Avatar
Wrox Author
Points: 71,804, Level: 100
Points: 71,804, Level: 100 Points: 71,804, Level: 100 Points: 71,804, Level: 100
Activity: 100%
Activity: 100% Activity: 100% Activity: 100%
 
Join Date: Jun 2003
Location: Utrecht, Netherlands.
Posts: 17,046
Thanks: 80
Thanked 1,580 Times in 1,557 Posts
Default

Hi there,

One solution is to create yet another layer with plain data objects, also called DTOs or Data Transfer Objects.

You could put them in a BusinessEntities namespace and they would contain just the data, like this:

Code:
 
public class Person
{
  public string FirstName { get; set; }
  public string LastName { get; set; }        
}
Then both your BusinessObject Layer and the OR-Mapper layer can have a reference to this separate project, removing the circular reference.

You can do pretty much the same using 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.

Take a look here for some ideas on implementing this: http://imar.spaanjaars.com/QuickDocId.aspx?quickdoc=476

Hope this helps,

Imar
__________________
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Follow me on Twitter

Author of Beginning ASP.NET 4.5 : in C# and VB, Beginning ASP.NET Web Pages with WebMatrix
and Beginning ASP.NET 4 : in C# and VB.
Did this post help you? Click the button below this post to show your appreciation!
Reply With Quote
The Following User Says Thank You to Imar For This Useful Post:
  #3 (permalink)  
Old May 3rd, 2009, 11:21 AM
edurazee
Guest
 
Posts: n/a
Default Layered/Tiered Architecture complexity (AGAIN...)

Quote:
Originally Posted by Imar View Post
Hi there,

One solution is to create yet another layer with plain data objects, also called DTOs or Data Transfer Objects.

You could put them in a BusinessEntities namespace and they would contain just the data, like this:

Code:
 
public class Person
{
  public string FirstName { get; set; }
  public string LastName { get; set; }        
}
Then both your BusinessObject Layer and the OR-Mapper layer can have a reference to this separate project, removing the circular reference.

You can do pretty much the same using 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.

Take a look here for some ideas on implementing this: http://imar.spaanjaars.com/QuickDocId.aspx?quickdoc=476

Hope this helps,

Imar
Thanks a lot for your reply.

Personally I don't like the idea of Data Transfer Objects(DTO). Coz this technique don't match OOP fundamentals at all.

But can you please tell me something about my view of Object-Orientation in my design (plz. recall the placing of two static methods in the "User"-class)?

Can you suggest any better way of placing those to "Getter" methods in the application?

Thanks again.
Reply With Quote
  #4 (permalink)  
Old May 3rd, 2009, 11:26 AM
Imar's Avatar
Wrox Author
Points: 71,804, Level: 100
Points: 71,804, Level: 100 Points: 71,804, Level: 100 Points: 71,804, Level: 100
Activity: 100%
Activity: 100% Activity: 100% Activity: 100%
 
Join Date: Jun 2003
Location: Utrecht, Netherlands.
Posts: 17,046
Thanks: 80
Thanked 1,580 Times in 1,557 Posts
Default

I don't agree that DTOs "don't match OOP fundamentals at all". Why would that be the case?

Your question was about avoiding the circular references. DTOs or Interfaces can remove them. Is that an option for you?

The static methods in your class make sense to me. Why wouldn't they? The provide easy access to common functionality without the need for an instance.

Did you check the article series I linked to? It shows a way to move the static methods from the User class into a UserManager class.

Cheers,

Imar
__________________
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Follow me on Twitter

Author of Beginning ASP.NET 4.5 : in C# and VB, Beginning ASP.NET Web Pages with WebMatrix
and Beginning ASP.NET 4 : in C# and VB.
Did this post help you? Click the button below this post to show your appreciation!
Reply With Quote
The Following User Says Thank You to Imar For This Useful Post:
  #5 (permalink)  
Old May 3rd, 2009, 11:50 AM
edurazee
Guest
 
Posts: n/a
Default Layered/Tiered Architecture complexity (AGAIN-2)

Quote:
Originally Posted by Imar View Post
I don't agree that DTOs "don't match OOP fundamentals at all". Why would that be the case?

Your question was about avoiding the circular references. DTOs or Interfaces can remove them. Is that an option for you?

The static methods in your class make sense to me. Why wouldn't they? The provide easy access to common functionality without the need for an instance.

Did you check the article series I linked to? It shows a way to move the static methods from the User class into a UserManager class.

Cheers,

Imar
Thanks for your opinion.

I like the idea of Interfaces. It makes much sense to me.

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.
Reply With Quote
  #6 (permalink)  
Old May 3rd, 2009, 12:00 PM
Imar's Avatar
Wrox Author
Points: 71,804, Level: 100
Points: 71,804, Level: 100 Points: 71,804, Level: 100 Points: 71,804, Level: 100
Activity: 100%
Activity: 100% Activity: 100% Activity: 100%
 
Join Date: Jun 2003
Location: Utrecht, Netherlands.
Posts: 17,046
Thanks: 80
Thanked 1,580 Times in 1,557 Posts
Default

I am not so rigid about that. You can have DTOs and Manager classes and still use all good things from OO including inheritance, polymorhism, encapsulation and so on.

In your case, I think interfaces are the way to handle it.

BTW: can you please not quote the entire message when replying? That adds a lot of unnecessary noise to the thread.

Cheers,

Imar
__________________
Imar Spaanjaars
http://Imar.Spaanjaars.Com
Follow me on Twitter

Author of Beginning ASP.NET 4.5 : in C# and VB, Beginning ASP.NET Web Pages with WebMatrix
and Beginning ASP.NET 4 : in C# and VB.
Did this post help you? Click the button below this post to show your appreciation!
Reply With Quote
The Following User Says Thank You to Imar For This Useful Post:
  #7 (permalink)  
Old May 3rd, 2009, 12:04 PM
edurazee
Guest
 
Posts: n/a
Default

Thanks a lot.

Hoping for more problem-solving answers in future.
Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off

Similar Threads
Thread Thread Starter Forum Replies Last Post
Removal of same tiered nodes, based on text overcast XSLT 2 August 19th, 2008 01:43 PM
Kindly help for datagrid complexity avixorld ASP.NET 2.0 Professional 0 July 30th, 2007 03:09 AM
Layered Applications Muhammad Zeeshan General .NET 5 July 25th, 2007 05:08 AM
Two tiered collections (user-defined) sholom VB How-To 0 November 26th, 2003 02:51 PM



All times are GMT -4. The time now is 10:35 PM.


Powered by vBulletin®
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.