65.9K
CodeProject 正在变化。 阅读更多。
Home

不使用向导编写 N 层应用程序 - 第一部分

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.71/5 (68投票s)

2008年3月25日

CPOL

8分钟阅读

viewsIcon

252944

downloadIcon

4257

面向初学者到中级开发人员的 N 层开发。

引言

之前,我写了一篇关于使用 VB.NET 构建 n 层应用程序的文章。如果你查看那篇文章,你会发现当时我处于一定的编程水平,并且使用了 Visual Studio 中包含的向导,这让我根本无法理解向导在后台做了什么。我发现自己又被 VS 提供的这个向导的局限性卡住了。不要误会我的意思,它们很有帮助,但仅限于初学者开发。试想一下,如果你刚接触 .NET,并且使用数据适配器向导从两个表使用 JOIN 获取数据。向导不会报错,但到了更新的时候,你就会遇到问题。向导无法帮助你,这时你就必须自己编写数据层。试想一下,告诉一个刚入门的程序员他必须使用数据层。除非你一步一步地解释什么是数据层,否则他不会理解。现在,在这篇文章中,我们将创建一个不使用任何向导的 n 层应用程序。我们将在下一篇文章之前只集中精力进行插入操作。这是下一篇文章的介绍。

这是完成后的分层结构

tier.jpg

背景

这是一个 n 层应用程序,其物理层是分开的。当然,我们必须设计用户界面并回到 DAL,但我认为在表示层中有大量文本不是一种好的编程实践。不要误会我的意思,这不会影响系统的任何功能,但如果你查看你的表示层(窗体),Visual Studio 会生成它的代码,相信我,在其中编写如此大的注释看起来并不好。好了,让我们回到主题。

我喜欢 n 层应用程序,它们易于维护,易于设计。所以整个项目由层组成,PL(表示层)是你的用户界面,ASP.NET 页面;BLL(业务逻辑层)是负责格式化你的数据、验证数据以及我们将在项目中稍后进行的其它工作的类;DAL(数据访问层)是我们在其中向 SQL 数据库、Access 或 Oracle 运行 SQL 查询的地方。请注意,在此层,我们不处理错误。

Using the Code

我们将在文章中使用多处注释,并且将使用 C# 作为我们的语言。

关注点

在使用向导和原始 SQL 语句编写 n 层应用程序代码的过程中,我学到了很多东西,这些都促使我写了这篇文章。我曾经答应 Dave 永远不使用向导来做数据库工作,而且这对我来说是有效的,并且感谢 CodeProject,我再次学会了使用 VB.NET 和 C#。

历史

我想借此机会将这篇文章献给 the careways group。我去了一次面试,这次面试给了我编写 C# 代码的勇气,相信我,它工作起来非常容易。在学术上,我过去在 Linux 环境下使用 C++ 和 Java 进行开发。C++ 或 Java 中的 OOP 思想是一次很棒的经历,我一直渴望与之一起工作。我更喜欢 C++ 而不是 Java,因为它支持多重继承,但现在 C# 必须像 Java 一样实现接口。但我仍然非常感激我回到了那个世界。感谢 careways group 团队,Ian Roets 和他的团队,我想说我愿意和你们一起工作,你们让我回到了那个世界,而 VB.NET 用它用户友好的 IDE 束缚了我。最后,各位,不要笑,这不是玩笑,我一直用“//”来注释我的 VB.NET 代码,你们看到了你们做了什么吗?我喜欢它,因为我可以互换语言,但现在我将坚持使用 C#。

Start

现在在我们开始编码之前,让我们构建我们的项目并理解它。打开 Visual Studio 并选择一个新项目,选择 C# Windows 项目并将其命名为“client”。这将是我们的表示层,添加一些文本框,让它看起来像这样

pl_presentation_layer_d.jpg

完成后,转到文件,添加一个新项目并选择一个类库,将其命名为“clsbll”。完成之后,请注意,在你的 clsbll 项目中,会有一个“class1”。将其更改为“bll”,然后再次添加一个项目并将其命名为“clsdal”,并执行相同的操作,将 class1 重命名为“DAL”,完成后,你的解决方案资源管理器将看起来像这样

project_explorerd.jpg

现在,这个解决方案中有三个项目。对于本文的这一部分,我们将只关注插入操作。我们已经看到了我们的表示层,现在是时候进行数据库工作了。让我们创建一个数据库和表,我们甚至可以在本文的第二部分中使用它们。

数据库工作 (SQL)

就我而言,我同时拥有 SQL 2000 和 SQL 2005。你可以选择任何一个。如果你使用 2000,请转到查询分析器并开始编码。记住,即使你有 SQL 2005,我们也不会使用向导来完成数据库工作。让我们创建一个数据库,如下所示

create database valrollclients

好的,现在是时候创建我们的客户表了

create table client_details
(
client_id int identity(100,01) primary key not null,
client_name char(12) null ,
client_lastname char(15) null, 
client_age int null
)

这样,我们的数据库和表就准备好了。我们需要一个存储过程来使用。让我们定义我们的存储过程

set nocount on
create procedure prcinsert_client
(
@client_name char(12) ,
@client_lastname char(15) ,
@client_age int 
)
as 
insert into client_details
values(@client_name,@client_lastname,@client_age)

现在我们的数据库工作在 SQL 中完成了。让我们回到 Visual Studio。

DAL(数据访问层)

现在我们的表示层已完成,但还没有编写代码,现在我们需要从后往前开始。我们将从编写 DAL 代码开始。打开 DAL 类。我们需要添加一些不是自动可用的命名空间。让我们导入这些命名空间。

using system.data.sqlclient;
using system.data;

public class dal
    {   /*declare a string variable and assign a connection string to it*/
        string strcon  = @"user id  = sde ; password = passrd;
        server =dfgserver;database =valrollclients";
        
         //in this line, we are declaring a command object for inserting
         sqlcommand cmdinserted;
         sqlconnection con; //declaring a connection object 
        
        /*The following method is going to insert client details into the client_details
        table. our presentation layer will need us to save the data into our database,
        the main objective of this project is to show how we do n-tier application
        and from this example, we are only going to add data into the database. Now
        the following method is going to save data that is valid into the database,
        the data is accepted from the bll, that means the data has passed the
        business rules that we have set and some calculations have been done if
        needed. This method accepts 3 parameters, that means bll has to supply 
        3 valid parameters to the dal to insert the data into the database, and we
        are going to use a stored procedure to avoid SQL injection and besides
        that using stored procedure is easier than using naked SQL statements, if
        you want information on SQL injection follow the link
        http://msdn2.microsoft.com/en-us/library/ms161953.aspx
        Now the SQL procedure is already defined as we have seen.*/

        public void insert_clients(string client_name, string client_lastname,
           int client_age)
        {
            /* instantiating the inserted command object that we have declared earlier
            if you look at this code, it can be written in one line, but for the
            purpose of understanding, we have to break it into parts */
            cmdinserted = new sqlcommand();

            /*now here we are passing the storedprocedure name in the command text
            the command text carries the SQL statement, if we decided to use naked
            SQL statement, we would have had something like
            "select * from table", but we used a storedprocedure instead. */
            cmdinserted.commandtext = "[dbo].[prcinsert_client]";

            /*in this line, we want to avoid the time out exceptions by setting the
            number to infinite, but Microsoft recommended to use a number. */
            cmdinserted.commandtimeout = 0;

            //we are telling the command object that we are going to use a
            //stored procedure
            cmdinserted.commandtype = commandtype.storedprocedure; 

            //we are initializing the connection object and pass
            //the connection string in the constructor
            con = new sqlconnection(strcon); 

            //we are telling the connection object what connection we are going to use
            //below we are adding parameters to the command object to insert, as
            //we have seen that our stored procedure will require some parameters.
            //ok let's explain this statement in detail 

            cmdinserted.connection = con; 
            cmdinserted.parameters.add("@client_name",
               sqldbtype.varchar, 12).value = client_name;

            cmdinserted.parameters.add("@client_lastname",
               sqldbtype.varchar, 15).value = client_lastname;

            cmdinserted.parameters.add("@client_age ",
               sqldbtype.int, 4).value = client_age;
           /*from the command object inserted we add a parameter and name it
           client_age, remember that the parameter should be in quotes and the 
           datatype and the dimension must be the same as the one defined in the
           table, or else you will encounter the problems and another thing, the order 
           * of variable declaration in your procedure should be the same as the
           * order in your VB or C# code.
           * now the last part of the code, we are assigning the value to the parameter
           * from the string that has been accepted from BLL in our method. I hope
           * the explanation is clear*/
            try
            {
                con.open(); //open connection

                cmdinserted.executenonquery(); //execute the stored procedure

                con.close();//close connection
            }
            catch (sqlexception) //catch an error
            {
                throw; //throw it back to the calling method 
            }
        }
    }

现在,这是我们的 DAL 类,它只有一个方法。请记住,客户端不应直接访问我们的 DAL,这意味着我们的 BLL 应该是 DAL 和客户端之间的中间人。这意味着我们需要在 BLL 项目中添加对 DAL 项目的引用,而在客户端项目中,我们将只添加对 BLL 的引用。

BLL(业务逻辑层)

现在在我们的 BLL 中,我们将处理所有业务规则和错误。正如你在我们的 DAL 类中所见,我们只捕获了异常,但从未从那里显示它们,我们只将它们抛给了调用方法,而该方法只会来自 BLL,BLL 会将消息发送给客户端。这意味着 BLL 旨在控制层之间数据的有效性和一致性,从 DAL 进入的数据已准备好供客户端展示,而从客户端应用程序(表示层)进入的数据将经过有效性检查并传递给 DAL。所以我们要做的第一件事是在 BLL 中添加对 DAL 项目的引用,以便我们可以调用 save 方法。完成之后,让我们去编写 BLL 代码。

public class bll
{
    public int save_client(string clientname, string clientlastname, string clientage)
    {
        boolean bopassed = true; //declare a boolean variable
        /*assigning the boolean results from the check_rules function to 
         * the variable bopassed, that means the data is valid*/
        bopassed = check_rules(clientname, clientlastname, clientage);
        //creating an object of a class and instantiating it
        clsdal.dal obj = new clsdal.dal();
        int Res = 0;
        try
        {
            if (bopassed == true) //if the rules are passed then 
            {
                //save the records 
                obj.insert_clients
		(clientname, clientlastname, convert.toint32(clientage));
                Res = 1;
            }
            else //else 
            {
                //the rules are not passed send the user a notification that something 
                //is wrong
                Res = 0;
            }
        }
        catch (SqlException)
        {
            throw;
        }

        return Res;
    }

        /* let's check the rules, this is just an example of what kind of things should
         * be present in the bll, it can be calculations and the data after the
         * calculations \ should be brought back to correct datatypes that the dal
         * can understand and they should be valid..*/
 
        private boolean  check_rules(string client_name,string clientlastname,
            string clientage)
        { /*we are accepting the input that was accepted from the client and they are
           validated for empty string later the age will be converted to an integer*/

            boolean bolres = true; //declaring a boolean variable
            if (client_name == "")  //testing for empty string
            {
                bolres = false;  //if its empty set the bolres variable to false 
             }
                if (clientlastname == "")
                {
                    bolres = false;
                }

                 if (clientage =="")
                 {
                        bolres = false;
                 }
               
            
           /*return a boolean value based on the test, if one of the fields say "false"
           then the function will return false, because remember the SQL
           stored procedure will require all the fields to present.*/
           return bolres;
                }
    } 

现在我们的 BLL 已完成,并添加了注释,让我们回到客户端完成工作。

PL(表示层)

这是我们的日常用户看到和使用的部分。现在我们必须设置引用,以便我们可以使用 BLL 的函数和方法,在完成设置引用后,双击保存按钮开始编写代码。

clsbll.bll obj = new clsbll.bll();
string clientname = txtclientname.text ;
string clientlastname = txtclientlastname.text;
string clientage = txtclientage.text;
int Res = 0;
        try 
        {
        Res = obj.save_client(clientname,clientlastname,clientage); 
        }
        catch(SqlException ex)
        {
            MessageBox.Show(ex.Message);
        }
        if(res  == 1)  
        {   
            MessageBox.Show("Data Saved");
        }
        else
        {
        MessageBox.Show("Data not Saved");
        }

完成后,请记住有一个清除按钮,这只是一个额外的东西,与 n 层无关。有一个清除按钮。双击它以添加代码。

txtclientname.clear();
txtclientlastname.clear() ;
txtclientage.clear();

按下此代码将清除文本框。现在,如果我们像这样测试我们的应用程序,按键盘上的 F5,你将看到你的 PL(表示层)。如果你尝试在没有任何输入的情况下单击保存按钮,你将收到一个在 BLL 中被捕获的错误消息。这表明 BLL 是好的,并且负责消息和错误处理。这意味着 check_rules 函数返回了 false ,并且再次意味着规则已被违反。

err_blld.jpg

如果你添加输入并再次尝试,你将收到一条确认规则未被违反的消息。

record_savedd.jpg

要确认记录已保存,而无需依赖我们在 BLL 中显示的消息,你可以在 SQL 数据库中运行 select * 语句,你将看到你输入的记录。

sql_reocrdd.jpg

结论

我们编写了此应用程序而没有向导的帮助,但目前我们只添加到了表中。在本文的第二部分,我们将更新、删除和搜索 datagrid 。数据将来自 join ,这意味着 datagrid 将显示来自两个表的数据,就好像它们来自一个表一样。现在,向导无法处理的棘手部分是 update delete 。我将解释如何使用存储过程在 n 层应用程序中设计我们的 DAL 和 BLL,而无需向导。我感谢所有人的支持,并且不要因为我没有整齐地格式化这篇文章而惩罚我。我自行编写了标签,这很痛苦,我在 CodeProject 中找不到工具栏,但 CodeProject 的 Sean 试图为我提供工具栏,我感谢他的帮助。我相信昨天被删除的文章会比这篇更好。我解释得很清楚,如果我能有机会完成它,对初学者来说会很好。

© . All rights reserved.