1.前言

前言

我是从01前开始就接触.net,那时.net还是1.0时代,很多东西都没有.
后来.net出了2.0版本.从vs2005开始就使用Webform做网站.当时感觉.net能够拖来拖去,很厉害.
参加工作后,又学了很多其它语言,包括php,python,ruby等.这时才感受到.net多么的封闭.
那时接触了很多MVC的思想,主要来自于brophp,后来微软出了自己的MVC,包括Razor等.
通过对这些新技术的接触和使用我发现微软的MVC并不是那么的好用:
1.Razor引擎虽然使得前端和后台进一步分离，但是其混编方式导致前端人员不能自由的修改模板文件, 并且也没有了页面的及时预览功能.
2.MVC的在压力测试下和传统的Webform相比运行速度不足Webfrom的七分之一.(vNext引擎之后速度大大提升)
3.微软的Entility framework的效率真的不高,ORM框架甚至不如第三方的Dapper.
4.微软每出一种新技术(包括MVC),都要重新学习,不像其它语言有一个平滑过度的过程.

我想要一个MVC框架,不需要什么学习过程,10分钟就能使用,
半个小时就能掌握,一天的时间就能灵活使用,最好以前学的Webform的知识也能用得上.
而且我这个人比较极端,尤其是在程序运行速度方面,.net的MVC让我很纠结.
因此有了NFinal框架.
声明：此框架与JFinal与任何关系，完全是另一套编程模式，仅是名称相似。


2.为什么要用NFinal

为什么要用NFinal?

NFinalWeb框架是基于模板生成创建的框架.
本框架有两大特点.
1.所有框架里ORM最简单的，会写sql语句就会用。（ORM采用sql原生语法且带sql自动提示功能）.
2.所有框架里运行速度最快的，比ashx还要快。（无反射,无dynamic,无模板解析,纯手工sql代码生成）.

相关介绍
1.单服务器及单数据库下，运行速度比任何php和java以及.net框架要快.
2.安装简单,自带配置程序，dll的引入，类文件及目录的生成，包括web.config的配置可一键完成。
3.视图带有自动提示和预览功能,这点是php和java以及.net mvc的razor引擎都做不到的.
4.NFinal的ORM框架简单到只要会sql就会使用,且运行速度比dapper更高.
5.小优化.包括html零损耗压缩,cookie，session代码自动提示,参数自动转换等等.
6.通过在线编译功能,项目可在线修改，并发布.
7.学习资料丰富,我们会提供相关的视频教程.
http://bbs.nfinal.com/thread-3
8.更多支持.此框架开源，而且非常小.每周至少会更新一次。
https://git.oschina.net/LucasDot/NFinal.git
svn://git.oschina.net/LucasDot/NFinal
http://bbs.nfinal.com/read-18


3.查看源码

请尽量使用vs2015及以上版本，支持sql语句提示，及模版提示
vs2015社区中文版下载 》强烈建议更新，只要注册微软帐号即可永久使用。《

加入QQ群:459156828
NFinal开发版本：
开源项目
http://git.oschina.net/LucasDot/NFinal

帐号:nfinal@126.com 密码:nfinal
为了方便,此帐号具有修改提交的权限.

各种项目管理工具的URL:
https://git.oschina.net/LucasDot/NFinal.git

git@git.oschina.net]git@git.oschina.net:LucasDot/NFinal.git

svn://git.oschina.net/LucasDot/NFinal


4.安装与配置

请尽量使用vs2015及以上版本，支持sql语句提示，及模版提示
NFinal框架不仅仅是一些引入，还包含了生成器部分。
1.在Web应用程序的引用上点右键，选择管理Nuget程序包。
（应用程序必须为.net framework 4.0及以上版本）

(可选)当微软官方的nuget服务器被墙时，可通过以下方式进行安装
(可选)a.找到Nuget管理按钮

(可选)b.添加NFinal Nuget服务器,名称:NFinal,源:http://api.nfinal.com/nuget

(可选)c.选择添加的Nuget服务器

2.搜索NFinal.Web安装包

3.安装NFinal.Web

4.运行NFinal文件夹下的AutoConfig.exe.

5.输入想要增加的功能模块的名字(比如App,Admin,Shop,Forum等【名称必须全为字母】),回车.

6.VS会自动检测项目文件被修改,我们选择放弃即可,如果没有放弃选择重新加载.

7.浏览新添加文件夹下的Web.Compiler.aspx
8.这时安装已经完成,找到【模块】/Web/IndexController/Index.html文件,右键选择在浏览器中查看.


许可协议

NFinal遵循Apache2开源协议发布。Apache Licence是著名的非盈利开源组织Apache采用的协议。该协议和BSD类似，鼓励代码共享和尊重原作者的著作权，同样允许代码修改，再作为开源或商业软件发布。需要满足的条件:

1． 需要给代码的用户一份Apache Licence ；
2． 如果你修改了代码，需要在被修改的文件中说明；
3． 在延伸的代码中（修改和有源代码衍生的代码中）需要带有原来代码中的协议，商标，专利声明和其他原来作者规定需要包含的说明；
4． 如果再发布的产品中包含一个Notice文件，则在Notice文件中需要带有Apache Licence。你可以在Notice中增加自己的许可，但不可以表现为对Apache Licence构成更改。

具体的协议参考：http://www.apache.org/licenses/LICENSE-2.0。
http://bbs.nfinal.com/
QQ群:459156828


目录结构

引用文件

网站目录

模块目录

NFinal项目文件,其中不仅包含了传统的三层架构,也包含了MVC架构.
其中WebCompiler.aspx用于代码自动生成.


命名规则

1.项目名称,文件夹名称,类名,变量名均不能使用中文.
2.数据库表名,列名不能与csharp中的关键字重名.


关于var声明变量

1.数据库ORM函数和foreach循环可以使用var变量.
2.除了以上情况外,其它地方皆不可使用var变量声明


开发流程

1.模块下的Web表现层与DAL数据库访问层的代码,
是通过运行WebCompiler.aspx自动生成的.
2.当你编辑完Controller层或Views层或BLL层时
需要重新运行WebCompiler.aspx生成Web层代码.
3.注意,第一次生成完成后,VS可能会提示找不到相应的类,
这时必须显示所有文件,再手动把相应的类添加进项目中即可.


1.0.User表的增删改

1.控制器的定义

当安装完NFinal后,在Controllers目录下加入users类，内容为：
    public class users:Controller
    {
        //分页显示，p为页码数
        public void show(int p)
        {
            var con = Models.Common.OpenConnection();
            //创建Page对象,变量p为当前页数，5为每页记录数
            NFinal.Page pg = new NFinal.Page(p, 5);
            //计算URL所用到的函数，变量pi为当前页数
            pg.GetUrlFunction = (pi) => { return Url.DDD_users_show(pi); };
            //分页语句获取分页对象
            var us = con.Page("select * from users",pg);
            con.Close();
            View("show.aspx");
        }
        //编辑
        public void edit(int id,string name,string pwd)
        {
            var con = Models.Common.OpenConnection();
            var count = con.Update("update users set name=@name,pwd=@pwd where id=@id");
            con.Close();
            AjaxReturn(count > 0);
        }
        //插入
        public void insert(int id, string name, string pwd)
        {
            var con = Models.Common.OpenConnection();
            var count = con.Insert("insert into users(name,pwd) values(@name,@pwd)");
            con.Close();
            AjaxReturn(count > 0);
        }
        //删除
        public void delete(int id)
        {
            var con = Models.Common.OpenConnection();
            var count = con.Delete("delete from users where id=@id");
            con.Close();
            AjaxReturn(count > 0);
        }
    }

2.然后添加视图：

        <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="show.aspx.cs" Inherits="WebApplication1.DDD.Views.Default.users.show" %>
        <%@ Register Src="~/DDD/Views/Default/Common/Public/Navigator.ascx" TagPrefix="uc1" TagName="Navigator" %>
        <!DOCTYPE html>

        <html xmlns="http://www.w3.org/1999/xhtml">
        <head runat="server">
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            <title></title>
            <script src="/Scripts/jquery-1.11.2.min.js"></script>
            <script src="/Scripts/Url.js"></script>
        </head>
        <body>
            <form id="form1" runat="server">
                <div>
                    <table>

                        <foreach enumerator="<%var u = ViewBag.us.GetEnumerator(); %>">
                            <tr>
                                <td>

                                    <input id="id_<%=u.Current.id %>" type="text" value="<%=u.Current.id %>" readonly="readonly" />
                                </td>
                                <td>

                                    <input id="name_<%=u.Current.id %>" type="text" value="<%=u.Current.name %>" />
                                </td>
                                <td>

                                    <input id="pwd_<%=u.Current.id %>" type="text" value="<%=u.Current.pwd %>" />
                                </td>
                                <td>

                                    <button onclick="save(<%=u.Current.id %>);" type="button">save</button>

                                    <button onclick="del(<%=u.Current.id %>);" type="button">delete</button>
                                </td>
                            </tr>
                        </foreach>
                    </table>

                    <uc1:Navigator runat="server" id="Navigator" db="<%#ViewBag.pg %>" /><br />
                    插入
                    <input id="name" type="text" value="" />
                    <input id="pwd" type="text" value="" />
                    <input type="button" onclick="insert();" value="insert" />
                </div>
            </form>
        </body>
        </html>
        <script>
            //删除
            function del(id)
            {
                $.post(Url.DDD_users_delete(id), {}, function (data) {
                    if (data) {
                        alert("删除成功");
                    }
                    else {
                        alert("删除失败");
                    }
                });
            }
            //插入
            function insert()
            {
                var id = $("#id").val();
                var name = $("#name").val();
                var pwd = $("#pwd").val();
                $.post(Url.DDD_users_insert(id, name, pwd), {}, function (data) {
                    if (data) {
                        alert("添加成功");
                    }
                    else {
                        alert("添加失败");
                    }
                });
            }
            //保存更改
            function save(ext)
            {
                var id = $("#id_" + ext).val();
                var name = $("#name_" + ext).val();
                var pwd = $("#pwd_" + ext).val();
                $.post(Url.DDD_users_edit(id, name, pwd), {}, function (data) {
                    if (data) {
                        alert("保存成功");
                    }
                    else {
                        alert("保存失败");
                    }
                });
            }
        </script>
2.右键点击WebCompiler.aspx.选择在浏览器中查看.

生成开始
生成结束

3.找到解决方案资源管理器，展开Web文件夹下的Default文件夹。
点击显示所有文件按钮，使其激活。则可看到users文件夹，
其下有Show.cs和Show.html等文件
如果未看到，请选择该目录并点击刷新按钮。

右键点击users文件夹,选择包括在项目中,其中show.html的代码如下.
        ??<!DOCTYPE html>
        <html xmlns="http://www.w3.org/1999/xhtml">
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                <title></title>
                <meta http-equiv="Cache-Control" content="no-cache">
                <script src="/Scripts/jquery-1.11.2.min.js"></script>
        </head>
        <body>
                <script>
                $.getScript("/Scripts/Url.js?_" + Math.random(), function () {
                    var p=1;
                    //生成URL所用的js函数是由NFinal自动生成
                    window.location.href = Url.DDD_users_show(p);
                });
            </script>
        </body>
        </html>
Show.cs中的代码如下:
        public void show(int p)
        {
            var con = new System.Data.SQLite.SQLiteConnection(NFinal.Config.ConfigurationManager.ConnectionStrings["Common"].ConnectionString);
            con.Open();
            NFinal.Page pg = new NFinal.Page(p, 5);
            pg.GetUrlFunction = (pi) => { return Url.DDD_users_show(pi); };
            #region	var us;分页
            var __show_us_command__ = new System.Data.SQLite.SQLiteCommand("select count(*) from users", con);
            pg.recordCount =System.Convert.ToInt32(__show_us_command__.ExecuteScalar());
            __show_us_command__.Dispose();
            pg.count = (pg.recordCount % pg.size==0)? pg.recordCount/pg.size:pg.recordCount/pg.size+1;
            //传页码时用的变量名
            if (pg.index > pg.count)
            {
                pg.index = pg.count;
            }
            if (pg.index < 1)
            {
                pg.index = 1;
            }
            var us = new NFinal.List<__show_us__>();
            //计算得到SQL语句
            __show_us_command__ = new System.Data.SQLite.SQLiteCommand(string.Format("select * from users Limit {0} Offset {1}",pg.size,(pg.index-1)*pg.size), con);
            var __show_us_reader__= __show_us_command__.ExecuteReader();
            if (__show_us_reader__.HasRows)
            {
                while (__show_us_reader__.Read())
                {
                    var __show_us_temp__ = new __show_us__();
                    __show_us_temp__.id = __show_us_reader__.GetInt64(0);
                    __show_us_temp__.name =__show_us_reader__.IsDBNull(1)?null: __show_us_reader__.GetString(1);
                    __show_us_temp__.pwd =__show_us_reader__.IsDBNull(2)?null: __show_us_reader__.GetString(2);
                    us.Add(__show_us_temp__);
                }
            }
            __show_us_reader__.Dispose();
            __show_us_command__.Dispose();
            #endregion

            con.Close();

            Write("<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\"><head runat=\"server\"><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /><title></title><script src=\"/Scripts/jquery-1.11.2.min.js\"><<script><script src=\"/Scripts/Url.js\"></script></head><body><form id=\"form1\" runat=\"server\"><div><table>");
            var u= us.GetEnumerator(); while(u.MoveNext()){
                Write("<tr><td><input id=\"id_");
                Write(u.Current.id);
                Write("\" type=\"text\" value=\"");
                Write(u.Current.id);
                Write("\" readonly=\"readonly\" /></td><td><input id=\"name_");
                Write(u.Current.id);
                Write("\" type=\"text\" value=\"");
                Write(u.Current.name);
                Write("\" /></td><td><input id=\"pwd_");
                Write(u.Current.id);
                Write("\" type=\"text\" value=\"");
                Write(u.Current.pwd);
                Write("\" /></td><td><button onclick=\"save(");
                Write(u.Current.id);
                Write(");\" type=\"button\">save</button><button onclick=\"del(");
                Write(u.Current.id);
                Write(");\" type=\"button\">delete</button></td></tr>");
            }
            Write("</table>");
            pg.__render__ = ()=>{
                Write("?");
                Write("当前页:");
                Write(pg.index);
                Write("/");
                Write(pg.count);
                Write("lt;a href=\"");
                          Write(pg.GetUrlFunction(1));
                          Write("\">首页</a><a href=\"");
                                             Write(pg.index <= 1 ? "#" : pg.GetUrlFunction(pg.index - 1));
                Write("\">上一页</a>");
                for(int i = ((pg.index - 1) / pg.navigatorSize) * pg.navigatorSize + 1;i <= pg.count && i <= ((pg.index - 1) / pg.navigatorSize + 1) * pg.navigatorSize;i++){
                    if(i == pg.index){
                        Write("<a style=\"color:red;\" href=\"");
                                  Write(pg.GetUrlFunction(i));
                                  Write("\">");
                        Write(i);
                        Write("</a>");
                    }else{
                        Write("<a href=\"");
                                  Write(pg.GetUrlFunction(i));
                                  Write("\">");
                        Write(i);
                        Write("</a>");
                    }
                }
                Write("<a href=\"");
                          Write(pg.index>= pg.count ? "#" : pg.GetUrlFunction(pg.index + 1));
                Write("\">下一页</a><a href=\"");
                                    Write(pg.GetUrlFunction(pg.count));
                                    Write("\">末页</a>");
            };pg.__render__();
            Write("<br /> 插入 <input id=\"name\" type=\"text\" value=\"\" /><input id=\"pwd\" type=\"text\" value=\"\" /><input type=\"button\" onclick=\"insert();\" value=\"insert\" /></div></form></body></html><script> function del(id) { $.post(Url.DDD_users_delete(id), {}, function (data) { if (data) { alert(\"删除成功\"); } else { alert(\"删除失败\"); } }); } function insert() { var id = $(\"#id\").val(); var name = $(\"#name\").val(); var pwd = $(\"#pwd\").val(); $.post(Url.DDD_users_insert(id, name, pwd), {}, function (data) { if (data) { alert(\"添加成功\"); } else { alert(\"添加失败\"); } }); } function save(ext) { var id = $(\"#id_\" + ext).val(); var name = $(\"#name_\" + ext).val(); var pwd = $(\"#pwd_\" + ext).val(); $.post(Url.DDD_users_edit(id, name, pwd), {}, function (data) { if (data) { alert(\"保存成功\"); } else { alert(\"保存失败\"); } }); } </script>");
        }

show中的代码只是跳转并执行users下的Show方法.

4.右键点击show.html并选择在浏览器中查看,则可看到最终分页效果.


1.0.登陆

1.控制器的定义

当安装完NFinal后,在Controllers目录下加入login类，内容为：
    public class login:Controller
    {
        public void show()
        {
            View("show.aspx");
        }
        [GetHtml("login/log")]
        public void log(string name,string pwd)
        {
            var con = Models.Common.OpenConnection();
            var count = con.QueryObject("select count(*) from users where name=@name and pwd=@pwd").ToInt32();
            con.Close();
            if (count>0)
            {
                //保留用户状态，1.使用cookie加密，2.使用redis缓存。
                //NFinal的vNext版本不支持session,因为IIS在高并发访问时，IIS内存占用太大的话容易假死。
                this.Success("登陆成功",Url.DDD_IndexController_Index(), 3);
            }
            else
            {
                this.Error("登陆失败",Url.DDD_login_show(), 3);
            }
        }
        public void reg(string name,string pwd)
        {
            var con = Models.Common.OpenConnection();
            var id = con.Insert("insert into users(name,pwd) values(@name,@pwd)");
            con.Close();
            AjaxReturn(id>0);
        }
    }

2.然后添加视图：

        <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="show.aspx.cs" Inherits="WebApplication1.DDD.Views.Default.login.show" %>
        <!DOCTYPE html>

        <html xmlns="http://www.w3.org/1999/xhtml">
        <head runat="server">
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                <title></title>
                <script src="/Scripts/jquery-1.11.2.min.js"></script>
                <script src="/Scripts/Url.js">/script>
        </head>
        <body>
            <form id="form1" runat="server" method="post" action="<%=Url.DDD_login_log() %>">
                <div>
                    <input name="name" type="text" value="123" /><br />
                    <input name="pwd" type="password" value="123" /><br />
                    <input type="submit" value="login" /><br />
                </div>
            </form>
        </body>
        </html>
2.右键点击WebCompiler.aspx.选择在浏览器中查看.

生成开始
生成结束

3.找到解决方案资源管理器，展开Web文件夹下的Default文件夹。
点击显示所有文件按钮，使其激活。则可看到login文件夹，
其下有show.cs和show.html等文件
如果未看到，请选择该目录并点击刷新按钮。

右键点击login文件夹,选择包括在项目中,其中show.html的代码如下.
        <!DOCTYPE html>
        <html xmlns="http://www.w3.org/1999/xhtml">
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                <title></title>
                <meta http-equiv="Cache-Control" content="no-cache">
                <script src="/Scripts/jquery-1.11.2.min.js"></script>
        </head>
        <body>
                <script>
                $.getScript("/Scripts/Url.js?_" + Math.random(), function () {
                    //生成URL所用的js函数是由NFinal自动生成
                    window.location.href = Url.DDD_login_show();
                });
            </script>
        </body>
        </html>
show.cs中的代码如下:
        public void show()
        {
            Write("<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\"><head runat=\"server\"><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /><title></title><script src=\"/Scripts/jquery-1.11.2.min.js\"></script><script src=\"/Scripts/Url.js\"></script></head><body><form id=\"form1\" runat=\"server\" method=\"post\" action=\"");
            Write(Url.DDD_login_log());
            Write("\"><div><input name=\"name\" type=\"text\" value=\"123\" /><br /><input name=\"pwd\" type=\"password\" value=\"123\" /><br /><input type=\"submit\" value=\"login\" /><br /></div></form></body></html>");
        }

show中的代码只是跳转并执行login下的show方法.

4.右键点击show.html并选择在浏览器中查看,则可看到登陆效果.

登陆成功时显示如下，3秒后跳转到首页

登陆失败时显示如下，3秒后跳转到登陆页


1.0.登陆

1.控制器的定义

当安装完NFinal后,在Controllers目录下加入upload类，内容为：
        public class upload:Controller
        {
            public void show()
            {
                View("show.aspx");
            }
        }

2.然后添加视图：

        <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="show.aspx.cs" Inherits="WebApplication1.DDD.Views.Default.upload.show" %>
        <%@ Register Src="~/DDD/Views/Default/Common/Public/KindEditorLibrary.ascx" TagPrefix="uc1" TagName="KindEditorLibrary" %>
        <!DOCTYPE html>

        <html xmlns="http://www.w3.org/1999/xhtml">
        <head runat="server">
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                <title></title>
                <uc1:KindEditorLibrary runat="server" id="KindEditorLibrary" />
        </head>
        <body>
            <form id="form1" runat="server">
            <div>
                <img id="img" src="#" width="200" height="200" />
                <input id="imgsrc" name="imgsrc" type="hidden" />
                <input type="button" data-type="upload-img" data-img="img" data-input="imgsrc" value="upload" />
            </div>
            </form>
        </body>
        </html>
2.右键点击WebCompiler.aspx.选择在浏览器中查看.

生成开始
生成结束

3.找到解决方案资源管理器，展开Web文件夹下的Default文件夹。
点击显示所有文件按钮，使其激活。则可看到upload文件夹，
其下有show.cs和show.html等文件
如果未看到，请选择该目录并点击刷新按钮。

右键点击upload文件夹,选择包括在项目中,其中show.html的代码如下.
        ??<!DOCTYPE html>
        <html xmlns="http://www.w3.org/1999/xhtml">
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                <title></title>
                <meta http-equiv="Cache-Control" content="no-cache">
                <script src="/Scripts/jquery-1.11.2.min.js"></script>
        </head>
        <body>
                <script>
                $.getScript("/Scripts/Url.js?_" + Math.random(), function () {
                    //生成URL所用的js函数是由NFinal自动生成
                    window.location.href = Url.DDD_upload_show();
                });
            </script>
        </body>
        </html>
Show.cs中的代码如下:
        public void show()
        {
            Write("<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\"><head runat=\"server\"><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /><title></title> ?");
            Write("<link rel=\"stylesheet\" href=\"");
            Write(_app);
            Write("/Content/kindeditor/themes/default/default.css\" /><link rel=\"stylesheet\" href=\"");
            Write(_app);
            Write("/Content/kindeditor/plugins/code/prettify.css\" /><script charset=\"utf-8\" src=\"");
            Write(_app);
            Write("/Content/kindeditor/kindeditor.js\"></script><script charset=\"utf-8\" src=\"");
            Write(_app);
            Write("/Content/kindeditor/lang/zh_CN.js\"></script><script charset=\"utf-8\" src=\"");
            Write(_app);
            Write("/Content/kindeditor/plugins/code/prettify.js\"></script><script>KindEditor.ready(function (K) { K.create(\"textarea[data-type=\'kindeditor\']\", { cssPath: \'");
            Write(_app);
            Write("/Content/kindeditor/plugins/code/prettify.css\', uploadJson: \'");
            Write(_app);
            Write("/Content/kindeditor/asp.net/upload_json.ashx\', fileManagerJson: \'");
            Write(_app);
            Write("/Content/kindeditor/asp.net/file_manager_json.ashx\', allowFileManager: true }); prettyPrint(); K(\"input[data-type=\'upload-img\']\").each(function () { var upload = K(this); var img = K(this).attr(\'data-img\'); var input = K(this).attr(\'data-input\'); var uploadbutton = K.uploadbutton({ button: upload, fieldName: \'imgFile\', url: \'");
            Write(_app);
            Write("/Content/kindeditor/asp.net/upload_json.ashx\', afterUpload: function (data) { if (data.error === 0) { if (img != \"\") { K(\'#\' + img).attr(\'src\', data.url); } if (input != \"\") { K(\'#\' + input).val(data.url); } } else { alert(data.message); } } }); uploadbutton.fileBox.change(function (e) { uploadbutton.submit(); }); }); }); </script></head><body><form id=\"form1\" runat=\"server\"><div><img id=\"img\" src=\"#\" width=\"200\" height=\"200\" /><input id=\"imgsrc\" name=\"imgsrc\" type=\"hidden\" /><input type=\"button\" data-type=\"upload-img\" data-img=\"img\" data-input=\"imgsrc\" value=\"upload\" /></div></form></body></html>");
        }

show中的代码只是跳转并执行upload下的show方法.

4.右键点击show.html并选择在浏览器中查看,点击上传按钮上传图片.


1.Web.config全局配置

模块配置

1.web.config是固定配置，无需修改。
所有的相关配置都在模块根目录下的config.json中。

    <?xml version="1.0" encoding="utf-8"?>
    <!--
      有关如何配置 ASP.NET 应用程序的详细信息，请访问
      http://go.microsoft.com/fwlink/?LinkId=169433
      -->
    <configuration>
      <!--禁用VS Browser Link功能，防止js post bug-->
      <appSettings>
        <add key="vs:EnableBrowserLink" value="false" />
      </appSettings>
      <system.web>
        <!--删除http协议中无用的version头-->
        <httpRuntime enableVersionHeader="false" />
        <!--session重命名-->
        <sessionState cookieName="session_id" mode="InProc">
        </sessionState>
        <compilation debug="true" />
        <!--http handler重写-->
        <httpHandlers>
          <add verb="*" path="*.nf" type="NFinal.Handler" validate="false" />
        </httpHandlers>
        <!--http module重写-->
        <httpModules>
          <add name="NFinalMoudle" type="NFinal.Module" />
        </httpModules>
      </system.web>
      <system.webServer>
        <!--不检测system.web配置节下的配置，以加快网站运效率-->
        <validation validateIntegratedModeConfiguration="false" />
        <!--http handler重写，并设置新的管道模式-->
        <handlers>
          <add name="NFinalHandler" verb="*" path="*.nf" type="NFinal.Handler" preCondition="integratedMode" />
        </handlers>
        <!--http module重写，并设置新的管道模式-->
        <modules>
          <add name="NFinalMoudle" type="NFinal.Module" preCondition="integratedMode" />
        </modules>
        <!--删除无用的http头-->
        <httpProtocol>
          <customHeaders>
            <remove name="X-Powered-By" />
          </customHeaders>
        </httpProtocol>
      </system.webServer>
    </configuration>


2.config.json模块配置

模块配置

1.在模块根目录下config.json文件

    {
      //本模块所有的URL前缀
      "urlPrefix": "/App",
      //本模块所有的Cookie前缀
      "cookiePrefix": "App_",
      //本模块所有的Session前缀
      "sessionPrefix": "App_",
      //默认样式
      "defaultStyle": "Default",
      //默认后缀
      "urlExtension": ".htm",
      //URL模式
      "urlMode": 1,
      //是否压缩HTML
      "compressHTML": true,
      //数据库配置
      "connectionStrings": [
        {
          "name": "Common",
          "connectionString": "Data Source=|ModuleDataDirectory|Common.db;Pooling=true;FailIfMissing=false",
          "providerName": "System.Data.SQLite"
        },
        {
          "name": "SqlServer",
          "connectionString": "Server=.\\SQLEXPRESS;Database=test;Uid=sa;Pwd=hayiaf;",
          "providerName": "System.Data.SqlClient"
        },
        {
          "name": "MySql",
          "connectionString": "Server=localhost;Database=test;Uid=root;Pwd=root;",
          "providerName": "MySql.Data.MySqlClient"
        },
        {
          "name": "PostgreSql",
          "connectionString": "Server=localhost;DataBase=postgres;Uid=postgres;Pwd=hayiaf;",
          "providerName": "Npgsql"
        }
        ,
        {
          "name": "Oracle",
          "connectionString": "Data Source==(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=ORCL)));User Id=c##sa;Password=hayiaf;",
          "providerName": "Oracle.ManagedDataAccess.Client"
        }
      ],
      //静态路由重写配置
      //目录重写
      "rewriteDirectory": [
      ],
      //文件重写
      "rewriteFile": [
        {"from": "/Index.html","to": "/App/IndexController/Index.html"}
      ],
      //缓存服务器
      "redisServers": [
        {
          "ip": "127.0.0.1",
          "port": 6379
        }
      ]
    }


3.模块配置详解

数据库配置

1.urlPrefix
为所有的URL加上自定义前缀，默认与模块名相同。

2.cookiePrefix
为所有的Cookie加上自定义前缀，默认与模块名相同.
用于区别不同模块下的cookie.

3.sessionPrefix
为所有的Session加上自定义前缀，默认与模块名相同.
用于区别不同模块下的Session.

4.defaultStyle
当前模块下的Views目录下使用哪个样式目录下的模板文件进行渲染，默认为Defalut.

5.urlExtension
为所有的URL加上默认后缀，默认为.html，也可以写.jsp,.php,.asp,.aspx等等。

6.urlMode
预留URL路由模式，未来用于多站点URL模式设置，本版本暂时不起作用。

7.compressHTML
是否压缩HTML(此压缩能提高网站运行效率，节省cpu及内存消耗。)
当设置为true时，输出的html中多余的空格都会被去掉。
此压缩引擎只能压缩标准的html及javascript.
如果html标签没写全或javascript没写分号等，
都会出现压缩bug.

8.connectionStrings
连接字符串，参数与原asp.net参数写法一致。
对于sqlite等文件型数据库。
|ModuleDataDirectory|指模块下的Data文件夹路径
|DataDirectory|指网站根目录下的App_Data文件夹路径

9.rewriteDirectory
url静态路由重写

10.redisServers
缓存服务器
在NFinal目录下有一个redis文件夹。
里面是redis分布式缓存服务端，有32位和64位两种版本。
启动redis-server.exe即可以使用缓存。 在代码中使用缓存：
ServiceStack.Redis.IRedisClient redisClient=NFinal.Cache.GetRedisClient();
redisClient.Set("city","zhengzhou");
Write(redisClient.Get("city"));


4.数据库配置

数据库配置

1.配置Web.Config中的ConnectionString

mysql数据库:
    "connectionStrings": [
        {
            "name": "MySql",
            "connectionString": "Server=localhost;Database=test;Uid=root;Pwd=root;",
            "providerName": "MySql.Data.MySqlClient"
        }]
sqlite数据库:
    "connectionStrings": [
        {
            "name": "Sqlite",
            "connectionString": "Data Source=|ModuleDataDirectory|Common.db;Pooling=true;FailIfMissing=false",
            "providerName": "System.Data.SQLite"
        }]
sqlserver数据库:
    "connectionStrings": [
        {
            "name": "SqlServer",
            "connectionString": "Server=.\\SQLEXPRESS;Database=test;Uid=sa;Pwd=123456;",
            "providerName": "System.Data.SqlClient"
        }]
oracle数据库:
    "connectionStrings": [
        {
            "name": "Oracle",
            "connectionString": "Data Source==(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=ORCL)));User Id=c##sa;Password=123456;",
            "providerName": "Oracle.ManagedDataAccess.Client"
        }]
postgresql数据库:
    "connectionStrings": [
        {
            "name": "PostgreSql",
            "connectionString": "Server=localhost;DataBase=postgres;Uid=postgres;Pwd=123456;",
            "providerName": "Npgsql"
        }]

2.运行WebCompile.aspx生成数据库操作类.

3.打开Models目录,显示所有文件,把生成的数据库操作类添加进项目中.


5.数据库注释管理

注释管理

当你配置完数据库之后，数据库中会出现一个叫nfinal_note的表
此表有三列，table_name,field_name,field_note;对应为表名，列名，列注释。
前二个字段已经填充完毕，只需把注释加入即可。
此注释管理的好处在于
1.添加修改注释方便简单。

2.生成器在生成Entity实体时，字段会自动加上注释。

3.在使用模板时，模板中关于数据库的实体也会有注释的自动提示。


6.Cookie配置

Cookie配置

1.在[模块名]/Code/Data/CookieInfo.cs中定义你的Cookie变量
        public class CookieInfo
        {
            public string name="";
            public bool islogin=false;
            public int age=0;
        }
2.运行WebCompiler.aspx生成[模块名]/Code/Data/CookieManager.cs类
3.在Controllers中读取和使用cookie:
        public void Index()
        {
            //让Cookie操作变得更简单.
            Cookie.name="NFinal";
            Write(_cookies.age);
        }

此方式的好处有3：
1.便于Cookie统一管理。
2.方便使用，不用去特意记忆Cookie名称，只需按.就会有自动提示。
3.当调用Web层下的Action类生成静态页时，只需把cookie作为参数填进去即可。


7.Session配置

Session配置

1.在[模块名]/Code/Data/SessionInfo.cs中定义你的Session变量
        public class SessionInfo
        {
            public id userId=0;
        }
2.运行WebCompiler.aspx生成[模块名]/Code/Data/SessionManager.cs类
3.在Controllers中读取和使用Session:
        public void Index()
        {
            //让Session操作变得更简单.
            Session.userId=2;
            Write(Session.userId);
        }

此方式的好处有3：
1.便于Session统一管理。
2.方便使用，不用去特意记忆Session名称，只需按.就会有自动提示。
3.当调用Web层下的Action类生成静态页时，只需把Session作为参数填进去即可。


8.URL重写配置

URL重写

config.json中的rewriteDirectory,rewriteFile

目录重写,把/1开始的url全改为以/news（文章）开头
    "rewriteDirectory": [
        {"from":"/news/","to":"/1/"}
    ]
文件重写,把/1/1.html重写为/1/index.html
    "rewriteFile": [
        { "from":"/1/1.html", "to":"/1/index.html" }
      ]

经过上面两个规则，请求的路径
/news/index.html被重写为/1/1.html
/news/2.html被重写为/1/2.html
/news/3.html被重写为/1/3.html
....
/news/n.html被重写为/1/n.html


1.0.控制器的定义

1.控制器的定义

当安装完NFinal后,Controllers目录下会有一个Index.cs的控制器
        public class IndexController: Controller
        {
            public void Index()
            {
                View("Index.aspx");
            }
        }

控制器定义有以下原则：
1.控制器必须在Controllers目录下.
2.命名空间必须采用与文件夹一致的命名空间,即默认命名空间.
且该类必须继承自Controller类
3.其函数返回值类型必须为void.可访问修饰符必须为public.

简单示例: 1.在Controllers下新建SampleController.cs
        public class SampleController:Controller
        {
            public void Show()
            {
                Write("Hello World.");
            }
        }
2.右键点击WebCompiler.aspx.选择在浏览器中查看.

生成开始
生成结束

3.找到解决方案资源管理器，展开Web文件夹下的Default文件夹。
点击显示所有文件按钮，使其激活。则可看到SampleController文件夹，
其下有Show.cs和Show.html文件
如果未看到，请选择该目录并点击刷新按钮。

右键点击SampleController文件夹,选择包括在项目中,其中Show.cs的代码如下.
        public class ShowAction  : Controller
        {
            public ShowAction(System.IO.TextWriter tw):base(tw){}
            public ShowAction(string fileName) : base(fileName) {}
            public void Show()
            {
                Write("Hello World.");
            }
        }
Show.html中的代码如下:
        <!DOCTYPE html>
        <html xmlns="http://www.w3.org/1999/xhtml">
        <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            <title></title>
            <script src="Url.js"></script>
        </head>
        <body>
            <script>
                function jump()
                {
                    window.location.href=Url.App_SampleController_Show();
                }
                jump();
            </script>
        </body>
        </html>

show中的代码只是跳转并执行SampleController下的Show方法.

4.右键点击Show.html并选择在浏览器中查看.可以看到浏览器输出Hello World.


1.1.控制器常用变量

1.get请求参数

        public class IndexController: Controller
        {
            public void Index()
            {
                //get变量存储了从url获取的变量以及get与post获取的变量。类似于asp.net中的request["name"];
                string name = this._get["name"];
            }
        }

2.tw页面输出

        public class IndexController: Controller
        {
            public void Index()
            {
                //tw变量继承自TextWriter接口，用于向浏览器输出字符。
                this._tw.Write("hello!");
                //通常情况下使用Write()函数
                Write("hello!");
            }
        }

3._subdomain二级域名

        public class IndexController: Controller
        {
            public void Index()
            {
                int city=0;
                //subdomain存储了二级域名，默认情况下为"www"
                switch(this._subdomain)
                {
                    //如果是beijing.xxx.com
                    case "beijing":city=1;break;
                    //如果是henan.xxx.com
                    case "henan":city=2;break;
                    //如果是shanghai.xxx.com
                    case "shanghai":city=3;break;
                    //....
                    default:city=0;break;
                }
                //然后根据不同的城市显示不同的内容。此字段用于做分站模式。
            }
        }

3._methodType请求类型

        public enum MethodType
        {
            NONE = 0,
            POST = 1,
            GET = 2,
            PUT = 3,
            DELETE = 4,
            AJAX = 5
        }
        public class IndexController: Controller
        {
            public void Index()
            {
                //判断请求类型。
                if(this._methodType==NFinal.MethodType.AJAX)
                {
                    Write("ajax请求");
                }
                else
                {
                    Write("请求出错");
                }
            }
        }

3.isMobile判断不同的设备

        public class IndexController: Controller
        {
            public void Index()
            {
                //判断请求类型。
                if(this.isMobile)
                {
                    Write("您在使用手机进行浏览");
                }
                else
                {
                    Write("您在使用PC进行浏览");
                }
            }
        }

3._context对象,用于其它情况下使用（尽量不要使用，一旦使用了就不能利用Web层进行静态页面的生成了。）

        public class IndexController: Controller
        {
            public void Index()
            {
                //输出
                this._context.Response.Write("Hello!");
            }
        }


2.0.URL默认规则

路由配置

路由前缀，后缀
在模块目录下的config.json文件中。

        //URL前缀
        "urlPrefix": "/App",
        //URL后缀
        "urlExtension": ".html",

默认路由规则

不带参数
URL=URL前缀（config.json中的urlPrefix项）
+"/"+类名+"/"+方法名
+URL后缀(config.json中的urlExtension项)
        //如果此类的模块名为App
        namespace WebSite.App.Controllers
        {
            public class IndexController
            {
                public void Index()
                {
                    Write("Hello!");
                }
            }
        }
        //URL前缀为：/App
        //类名为:IndexController
        //方法名:Index
        //URL后缀为:.html
        //对应的URL为:http://localhost/App/IndexController/Index.html
带参数
URL=URL前缀（config.json中的urlPrefix项）
+"/"+类名+"/"+方法名
+"/"+参数1+"/"+参数2+"/"+参数3....
+两位数字(两位数字代表urlPrefix+类名+方法名的长度)
+URL后缀(config.json中的urlExtension项)
        //如果此类的模块名为App
        namespace WebSite.App.Controllers
        {
            public class IndexController
            {
                public void Index(int id)
                {
                    Write("Hello!");
                }
            }
        }
        //URL前缀为：/App
        //类名为:User
        //方法名:Index
        //数字:15
        //参数1为：3
        //URL后缀为:.html
        //对应的URL为:http://localhost/App/User/Index/315.html
后台生成
        //Url.cs类位于网站根目录下。
        string url=Url.IndexController_Index(int id);
        //模板调用时
        //<%=Urls.IndexController_Index(3);%>
前端生成
        //需要引入网站根目录下的Url.js文件
        var id="1";
        var url=Url.IndexController_Index(int id);


2.1.URL生成

Url生成

Url采用函数生成（URL规则一旦改变，代码可保持不变）

后端生成
        public void Index()
        {
            //生成URL
            string url=Url.App_IndexController_Index();
        }
        //对应的Url类位于根目录下的Url.cs文件中，
        //此文件由生成器自动生成。
        //模块名：App
        //类名为:IndexController
        //方法名:Index
前端生成
        //需要引入网站根目录下的Url.js文件
        var url=Url.App_IndexController_Index();
        //对应的Url类位于根目录下的Url.js文件中，
        //此文件由生成器自动生成。
        //模块名：App
        //类名为:IndexController
        //方法名:Index

如果Url中有参数，则Url对应的生成函数也会有相应的参数。
例：
string url=Url.App_IndexController_News(int page);


2.2.Attribute路由

特性路由函数

使用时请先引入NFinal.Advanced命名空间。
特性函数如下：
FormHtml,Html,GetHtml,PostHtml
Redirect,GetRedirect,PostRedirect
Json,GetJson,PostJson
JavaScript,GetJavaScript,PostJavaScript
Css,GetCss,PostCss
File,GetFile,PostFile
Jpeg,GetJpeg,PostJpeg
Gif,GetGif,PostGif
Png,GetPng,PostPng
Xml,GetXml,PostXml
Text,GetText,PostText
Empty,GetEmpty,PostEmpty
在输出不同内容时使用不同的特性。
一方面程序会更清晰，另一方面系统也会自动加上不同的contypeType头。

特性路由规则

默认

URL=URL前缀+控制器类名+控制器方法名+(自定义参数[00])+URL后缀
Url=UrlPrefix+BaseUrl+ActionUrl+UrlExtesion;

        //urlPrefix="/App";
        namespace WebSite.App.Controllers
        {
            //BaseUrl="/User";
            [BaseUrl("/User")]
            public class IndexController
            {
                //ActionUrl="/Index.html";
                [GetHtml("/Index.html")]
                public void Index()
                {
                    Write("Hello!");
                }
            }
        }
        string url="/App"+ "/User" + "/Index.html";
        对应的URL为:http://localhost/App/User/Index.html

        //BaseUrl中带有~/,表示路径从根目录开始，但不能忽略URL前缀
        [BaseUrl("~/User")]
        public class IndexController
        {
            [GetHtml("/Index.html")]
            public void Index()
            {
                Write("Hello!");
            }
        }
        对应的URL为:http://localhost/App/User/Index.html

        public class IndexController
        {
            //ActionUrl中带有~/,表示路径从根目录开始
            [GetHtml("~/Index.html")]
            public void Index()
            {
                Write("Hello!");
            }
        }
        对应的URL为:http://localhost/App/Index.html

参数绑定:
URL参数类型仅为基本类型或基本类型对应的数组类型
ActionUrl规则:1.{函数参数名}
2.{函数参数名:正则表达式(或数据类型)}
3.{函数参数名:正则表达式:字符串格式化参数(与string.Format用法一致)}

        //如果此类的模块名为App
        //则ModuleUrl="/App"
        namespace WebSite.App.Controllers
        {
            //BaseUrl="/IndexController"
            public class IndexController
            {
                //ActionUrl="/Index.htm"
                //首页
                [GetHtml("~/ShouYe/{name}.htm")]
                public void Index(string name)
                {
                    Write("Hello!");
                }
            }
        }
        //通过以上公式得出URL:http://localhost/App/ShouYe/NFinal.htm
        //输出Hello!NFinal

参数

规则:string url=Url.App_Controller_Action(type1 p1,type2 p2,....);//默认生成URL


2.3.AttributeURL重写

Url重写

重写目录

        //urlPrefix="/App";
        namespace WebSite.App.Controllers
        {
            public class NewsController
            {
                [RewriteDirectory("/News/","/1/")]
                [RewriteDirectory("/Shop/","/2/")]
                [RewriteDirectory("/Contact/","/3/")]
                [GetHtml("~/{cateId}/{page}.html")]
                public void Index(int cateId,int page)
                {
                    Write("Hello!");
                }
            }
        }
        //把/News/1.html重写为/1/1.html
        //把/Shop/1.html重写为/2/1.html
        //把/Contact/1.html重写为/3/1.html

重写页面

        //urlPrefix="/App";
        namespace WebSite.App.Controllers
        {
            public class NewsController
            {
                [RewriteDirectory("/News/","/1/")]
                [RewriteDirectory("/Shop/","/2/")]
                [RewriteDirectory("/Contact/","/3/")]
                [RewriteFile("/1/Index.html","/1/1.html")]
                [RewriteFile("/2/Index.html","/2/1.html")]
                [RewriteFile("/3/Index.html","/3/1.html")]
                [GetHtml("~/{cateId}/{page}.html")]
                public void Index(int cateId,int page)
                {
                    Write("Hello!");
                }
            }
        }
        //把/News/1.html重写为/News/Index.html
        //把/Shop/1.html重写为/Shop/Index.html
        //把/Contact/1.html重写为/Contact/Index.html

config.json重写规则与此规则一致，请参考 URL重写配置


3.0.获取URL参数

获取URL参数

在控制器的方法中加入一些参数,例如name,然后输出.
        using System;
        using System.Collections.Generic;
        using System.Web;

        namespace WebMvc.App.Controllers
        {
            public class SampleController:Controller
            {
                public void Show(string name)
                {
                    Write(string.Format("Hello {0}.",name));
                }
            }
        }

运行WebCompiler.aspx重新生成.
然后把Web/Default/SampleControler文件夹包括在项目中.
其中Show.cs代码如下

        using System;
        using System.Collections.Generic;
        using System.Web;

        namespace WebMvc.App.Web.Default.SampleController
        {
            public class ShowAction  : Controller
            {
                public ShowAction(System.IO.TextWriter tw):base(tw){}
                public ShowAction(string fileName) : base(fileName) {}
                public void Show(string name)
                {
                    Write(string.Format("Hello {0}.",name));
                }
            }
        }

Sample.html中的代码如下(此文件只有在不存在的情况下才会重新生成，
如果你的controller参数或Url改变，则需删除该文件然后重新生成)

        ?<!DOCTYPE html>
        ?<html xmlns="http://www.w3.org/1999/xhtml">
        ?<head>
        ?<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            ?<title>
            ?<script src="/Url.js">
        ?</head>
        ?<body>
            ?<script>
                function jump()
                {
                    var name="";
                    window.location.href = Url.App_SampleController_Index(name);
                }
                jump();
            ?</script>
        ?</body>
        ?</html>

修改name变量

        ?<!DOCTYPE html>
        ?<html xmlns="http://www.w3.org/1999/xhtml">
        ?<head>
        ?<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            ?<title>
            ?<script src="/Url.js">
        ?</head>
        ?<body>
            ?<script>
                function jump()
                {
                    var name="NFinal";
                    window.location.href = Url.App_SampleController_Index(name);
                }
                jump();
            ?</script>
        ?</body>
        ?</html>

用浏览器查看Show.html.则浏览器输出Hello NFinal.

参数说明

NFinal仅支持基础参数类型，以及基础参数类型一维数组.
如果定义为数组，则传参时使用,(逗号)分隔。


3.1.利用Attribute路由获取参数

获取参数

当Action中有Attribute路由时。参数分为两种，
一种参数位于URL中（类似于Get传参），一种不在URL中（POST传参）。
在URL中的参数可以通过生成URL的函数作为参数直接传递，
不在URL中的参数可以通过POST传参。
        using System;
        using System.Collections.Generic;
        using System.Web;

        namespace WebMvc.App.Controllers
        {
            public class SampleController:Controller
            {
                [GetHtml("~/App/{cateId}.html")]
                //cateId为URL参数，p为post参数
                public void Show(int cateId,int p)
                {
                    Write(string.Format("分类：{0}。分页：{1}.",cateId,p));
                }
            }
        }

运行WebCompiler.aspx重新生成.
然后把Web/Default/SampleControler文件夹包括在项目中.
其中Show.cs代码如下

        using System;
        using System.Collections.Generic;
        using System.Web;

        namespace WebMvc.App.Web.Default.SampleController
        {
            public class ShowAction  : Controller
            {
                public ShowAction(System.IO.TextWriter tw):base(tw){}
                public ShowAction(string fileName) : base(fileName) {}
                [GetHtml("~/App/{cateId}.html")]
                public void Show(int cateId,int p)
                {
                    Write(string.Format("分类：{0}。分页：{1}.",cateId,p));
                }
            }
        }

Sample.html中的代码如下(此文件只有在不存在的情况下才会重新生成，
如果你的controller参数或Url改变，则需删除该文件然后重新生成)
运行此文件，输出始终为:"分类：2。分页：0."因为服务端获取不到参数p.

        ?<!DOCTYPE html>
        ?<html xmlns="http://www.w3.org/1999/xhtml">
        ?<head>
        ?<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            ?<title>
            ?<script src="/Url.js">
        ?</head>
        ?<body>
            ?<script>
                function jump()
                {
                    var cateId="2";
                    window.location.href = Url.App_SampleController_Index(cateId);
                }
                jump();
            ?</script>
        ?</body>
        ?</html>

如何传递参数p?

        ?<!DOCTYPE html>
        ?<html xmlns="http://www.w3.org/1999/xhtml">
        ?<head>
        ?<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
            ?<title>
            ?<script src="/Url.js">
        ?</head>
        ?<body>

            ?
            <script>
                var cateId=2
                //URL传参及POST传参
                $.post(Url.App_SampleController_Index(cateId),{"p":1},function(data){
                    alert(data);
                });
                jump();
            ?</script>
        ?</body>
        ?</html>

用浏览器查看Show.html.则浏览器弹出消息："分类：2。分页：1." 当点击提交按钮时页面会输出："分类：2。分页：1."

参数说明

NFinal仅支持基础参数类型，以及基础参数类型一维数组.
如果定义为数组，则传参时使用,(逗号)分隔。


3.2.Attribute参数验证

参数验证

html5前端验证

        <input name="name" type="text" required maxLength="12" minLength="3"/>

HTML5服务端验证

        public void Index(
        //引入using NFinal.Advanced;
        //使用时,只需加html5的属性即可
        [type(type.text), required(), maxLength(12), minLength(3)]
        string name
        )
        {
            Write("姓名长度必须在3到12之间，且为必填字段");
        }

验证属性包括:type,maxLength,minLength,max,min,required,pattern等.

html5属性 | NFinal特性 | 说明
type="text" | [type("text")] | 可以为任意字符
type="email" | [type("email")] | 必须为emial邮箱
type="url" | [type("url")] | 必须为URL路径
type="number" | [type("number")] | 必须为数字
type="range",min="0",max="100",step=1 | [type("range"),min(0),max(100),step(1)] | 必须为数字，必须写上min,max,step属性
type="date" | [type("date")] | 必须为日期
type="month" | [type("month")] | 必须为月份
type="week" | [type("week")] | 必须为星期
type="time" | [type("time")] | 必须为时间
type="datetime-local" | [type("datetime-local")] | 必须为本地化时间
type="search" | [type("search")] | 可以为任意字符
type="color" | [type("color")] | 必须为颜色
minLength="3" | [minLength(3)] | 输入的最小长度为3
maxLength="7" | [maxLength("7")] | 输入的最大长度为7
max="100" | [max("100")] | 最大值为100，必须和type="range"一起使用
min="1" | [min("1")] | 最小值为1，必须和type="range"一起使用
step="1" | [step("1")] | 最小增值为1，必须和type="range"一起使用
placeholder="请输入姓名" | [placeholder("请输入姓名")] | 不作任何验证，起提示作用。
required="required" | [required()] | 不能为空值
pattern="\d+" | [pattern("\d+")] | 必须符合正则表达式


3.3.参数绑定

参数绑定

在控制器函数内,NFinal框架支持默认参数. 也就是说,当该参数值不存在的情况下.就使用其默认值.

        using System;
        using System.Collections.Generic;
        using System.Web;

        namespace WebMvc.App.Controllers
        {
            public class SampleController : Controller
            {
                //在这里使用了默认参数
                public void Show(int id=5)
                {
                    if (id == 5)
                    {
                        Write("Success");
                    }
                    else
                    {
                        Write("Error");
                    }
                }
            }
        }

运行WebCompiler.aspx生成Web层.

修改Show.html，把id值改为空字符串或null;
然后再用浏览器查看Show.html.
浏览器返回Success.说明参数绑定成功


4.AJAX返回

AJAX返回

NFinal默认ajax结构{"code":1,"msg":"添加成功!","data":null}

我们经常会用到ajax,当我们想返回以上json时,需要这样写.

        context.Response.ContentType="application/json";
        context.Response.Write("{");
        context.Response.Write("\"code\":0,");
        context.Response.Write("\"msg\":\"添加成功!\"");
        context.Response.Write("\"data\"","null");
        context.Response.Write("}")
        context.Response.End();

这样做很不方便.我们可以直接在控制器中这样写.

        //返回{"code":1,"msg":"","data":{"id":1}}
        this.AjaxReturn("{\"id\":1}");
        //返回{"code":2,"msg":"","data":{"id":1}}
        this.AjaxReturn("{\"id\":1}", 2);
        //返回{"code":1,"msg":"添加成功","data":{"id":1}}
        this.AjaxReturn("{\"id\":1}",1,"添加成功");
        //返回{"code":1,"msg":"添加成功","data":null}
        this.AjaxReturn(1, "添加成功");
        //当然也可以使用这个,其中obj为任意类型
        //返回{"code":2,"msg":"","data":【obj序列化的json字符串】}
        this.AjaxReturn(obj);

另外json的组装也很麻烦.NFinal包含了LitJson框架,用于处理Json. LitJson的详细用法,可以百度一下.这里只是给一个小例子:

由于LitJson速度远远超过Newtonsoft.Json，
建议使用JsonData来解析或生成json:
        LitJson.JsonData data = new LitJson.JsonData();
        data["name"] = "peiandsky";
        data["age"] = 28;
        data["sex"] ="male";
        //{"name":"peiandsky","age":28,"sex":"male"}
        string json= data.ToJson();


5.页面跳转

页面跳转

Extesion.cs类提供了两个函数,Success和Error 我们可以在控制器中直接调用.如下:

        if(isLogin)
        {
            //参数1：显示信息，参数2为跳转后的URL，参数3为跳转所需的秒数
            this.Success("登陆成功", "/App/AdminController/Index.htm", 3);
        }
        else
        {
            //参数1：显示信息，参数2为跳转后的URL，参数3为跳转所需的秒数
            this.Error("登陆失败","/App/UserController/Login.htm",3);
        }

对应的模板位于Views/Default/Common/Public/下 分别是Success.aspx和Error.aspx,用户可以重新修改其样式.


6.页面重定向

页面重定向

与Success()和Error()两个函数的使用类似 我们可以在控制器中直接调用.如下:

        if(isLogin)
        {
            this.Redirect("/App/AdminController/Index.htm");
        }
        else
        {
            this.Redirect("/App/UserController/Login.htm");
        }

对应的模板位于Views/Default/Common/Public/下 分别是Index.aspx和Login.aspx,用户可以重新修改其样式.


7.Attribute压缩

Gzip压缩

        //urlPrefix="/App";
        namespace WebSite.App.Controllers
        {
            public class NewsController
            {
                //调用带有Zip后缀的Attribute路由，即实现Gzip压缩功能。
                [GetHtmlZip("~/Index.html")]
                public void Index()
                {
                    Write("Hello world!");
                }
            }
        }

Deflate压缩

        //urlPrefix="/App";
        namespace WebSite.App.Controllers
        {
            public class NewsController
            {
                //调用带有Deflate后缀的Attribute路由，即实现Deflate压缩功能。
                [GetHtmlDeflate("~/Index.html")]
                public void Index()
                {
                    Write("Hello world!");
                }
            }
        }


8.0.Attribute标准缓存

Attribute缓存

使用缓存时，请先引入NFinal.Advanced命名空间
缓存函数为：

缓存特性 | 说明
[Cache(Server server,Browser browser,int second)] | 使用缓存， 参数1为服务器缓存设置， (NoCache,FileDependency,AbsoluteExpiration,SlidingExpiration) 参数2为浏览器缓存设置， (NoStore,NotModify,Expires,NoExpires) 参数3为缓存的时间
[Cache(Standard standard,int second)] | 使用标准缓存， 参数1为一般缓存设置 (其中有File和Nomal两种，一种是文件缓存，一种是内存缓存)， 参数2为缓存的时间
[CacheFile(int second)] | 使用文件缓存，参数为缓存的时间
[CacheNormal(int second)] | 使用内存缓存，参数为缓存的时间

文件缓存
        //urlPrefix="/App";
        namespace WebSite.App.Controllers
        {
            public class NewsController
            {
                [CacheFile(6000)]
                public void Index()
                {
                    Write("Hello!");
                }
            }
        }
        //缓存100分钟，缓存文件将会存储于模块文件夹下的HTML中。
        //当该文件改变时缓存将会失效。
内存缓存
        //urlPrefix="/App";
        namespace WebSite.App.Controllers
        {
            public class NewsController
            {
                [CacheNormal(6000)]
                public void Index()
                {
                    Write("Hello!");
                }
            }
        }
        //缓存100分钟，缓存文件将会存储于内存中。
        //当重启IIS时缓存将会失效。


8.1.Http头Cache-Control详解

Http客户端缓存

http协议，Cache-Control(缓存控制)说明

1.不需要缓存的页面（即一般的动态页面）的Http报头中包含以下字段：
Cache-Control:private
Date:Tue,03 Jan 2012 10:48:28 GMT

其中private代表此页面在浏览器端不缓存。
Date表示远程文件的更新时间

2.缓存的页面报头中包含以下字段：
Cache-Control:public,max-age=10
Date:Wed,04 Jan 2012 14:02:07 GMT
Expires:Wed,04 Jan 2012 14:02:17 GMT
Last-Modified:Wed,04 Jan 2012 14:02:07 GMT

其中public代表此页面在浏览器端需缓存
max-age表示该页面要缓存多少秒。
Expires表示该页面在浏览器端的缓存在什么时间会失效。
其中Date（更新时间）+max-age（缓存时间）=Expires（过期时间）
Last-Modified表示该页面的最后修改时间。
在缓存未失效时，浏览器不会向服务器发送任何新的请求。
但如果用户使用f5进行强制刷新时，浏览器还是会发送请求。
3.为了防止用户恶意刷新所实现的304应答过程。

当用户第一次发送请求时，服务器返回Last-Modified头。
当用户再次浏览该页面或强制刷新时，请求的http头中就会出现If-Modified-Since头。
此头对应的值是Last-Modified的值。
服务端会获取该文件的最后修改时间与包头中If-Modified-Since中提取的时间，
If-Modified-Since的时间比文件的最后修改时间要小，则服务器返回304错误。
304表示文件未修改。这时浏览器会继续取用缓存。即实现了304应答。
当然服务器也可以选择直接返回页面，即第二种情况。


8.2.Attribute客户端缓存

Attribute客户端缓存

使用缓存之前，必须先引入NFinal.Advanced;命名空间

根据http头Cache-Control详解中的三种情况，浏览器端缓存分为四种情况。

Browser对应的枚举值 | 说明
NoStore | 表示永不缓存，即第一种情况
Expires | 一定时间内访问缓存,即第二种情况
NotModify | 文件未修改就一直使用缓存，即第三种情况
NoExpires | 表示永远缓存，一般jquery,css样式及静态文件采用此选项

        //urlPrefix="/App";
        namespace WebSite.App.Controllers
        {
            public class IndexController
            {
                //Server.NoCache表示服务端不缓存
                //Browser.NoStore表示浏览器缓存，缓存时间为100分钟。
                [Cache(Server.NoCache,Browser.Expires,6000)]
                public void Index()
                {
                    Write("Hello!");
                }
            }
        }


8.3.Attribute服务端缓存

Attribute服务端缓存

使用缓存之前，必须先引入NFinal.Advanced;命名空间

服务端缓存分为四种情况。

Server对应的枚举值 | 说明
NoCache | 表示不缓存，动态页面默认值
FileDependency | 指访问的文件如果不修改或未超时就一直缓存。
AbsoluteExpiration | 绝对超时，指过了指定缓存时间后，缓存自动失效。
SlidingExpiration | 滑动超时，指最后一次访问时间与上一次访问时间的时间间隔如果大于某个时间则缓存失效。

        //urlPrefix="/App";
        namespace WebSite.App.Controllers
        {
            public class IndexController
            {
                //Server.NoCache表示服务端使用滑动超时，时间间隔为100分钟。
                //Browser.NoStore表示浏览器不缓存。
                [Cache(Server.SlidingExpiration,Browser.NoStore,6000)]
                public void Index()
                {
                    Write("Hello!");
                }
            }
        }


9.前置,后置操作

前置后置操作类

每个控制器继承自Controller方法.
其中有两个函数,Before,After,一个在函数之前执行,一个在函数之后执行.
有什么用呢?例:我在Common中添加了一个类UserAction,继承自Controller方法.
重写了Before函数.并声明了一个变量,用于判断用户是否登陆.

        using System;
        using System.Collections.Generic;
        using System.Web;

        namespace WebMvc.App.Common
        {
            public class UserAction:Controller
            {
                //初始化函数,固定格式.
                public UserAction() : base() { }
                public UserAction(System.IO.TextWriter tw):base(tw){}
                public UserAction(string fileName) : base(fileName) { }
                //用于判断用户是否登陆成功
                public bool isLogion = false;
                public override void Before()
                {
                    //判断是否登陆成功
                    isLogion = true;
                    base.Before();
                }
            }
        }

然后在Controler增加一个控制器,使其继承自以上类.如下:

        using System;
        using System.Collections.Generic;
        using System.Web;

        namespace WebMvc.App.Controllers
        {
            public class SampleController:App.Common.UserAction
            {
                public void Show()
                {
                    //如果没有登陆则返回
                    if (isLogion)
                    {
                        Write("登陆成功");
                    }
                    else
                    {
                        Write("登陆失败");
                    }
                }
            }
        }

我们可以把所有需要用户操作权限的控制器全都继承自该类,以达到权限控制的目地.
这样做的好处是,不同的权限可以用不同的类进行控制,而且修改起来也简单.

用浏览器查看WebCompiler.aspx重新生成Web层.

然后再用浏览器查看Show.html,则会显示登陆成功的字样.


10.控制器区域划分

控制器区域划分

        NFinal默认支持控制器分文件夹放，但控制器的类名不能重复。


本章讲解 SELECT 和 SELECT * 语句

    SQL SELECT 语句
    SELECT 语句用于从表中选取数据。
    结果被存储在一个结果表中（称为结果集）。
    SQL SELECT 语法
    SELECT 列名称 FROM 表名称
    以及：
    SELECT * FROM 表名称
    注释：SQL 语句对大小写不敏感。SELECT 等效于 select。
    SQL SELECT 实例
    如需获取名为 "LastName" 和 "FirstName" 的列的内容（从名为 "Persons" 的数据库表），请使用类似这样的 SELECT 语句：
    SELECT LastName,FirstName FROM Persons
    "Persons" 表:
    Id	LastName	FirstName	Address	City
    1	Adams	John	Oxford Street	London
    2	Bush	George	Fifth Avenue	New York
    3	Carter	Thomas	Changan Street	Beijing
    结果：
    LastName	FirstName
    Adams	John
    Bush	George
    Carter	Thomas
    SQL SELECT * 实例
    现在我们希望从 "Persons" 表中选取所有的列。
    请使用符号 * 取代列的名称，就像这样：
    SELECT * FROM Persons
    提示：星号（*）是选取所有列的快捷方式。
    结果：
    Id	LastName	FirstName	Address	City
    1	Adams	John	Oxford Street	London
    2	Bush	George	Fifth Avenue	New York
    3	Carter	Thomas	Changan Street	Beijing


Update 语句

Update 语句用于修改表中的数据。

        语法：
        UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
        Person:
        LastName	FirstName	Address	City
        Gates	Bill	Xuanwumen 10	Beijing
        Wilson	 	Champs-Elysees
        更新某一行中的一个列
        我们为 lastname 是 "Wilson" 的人添加 firstname：
        UPDATE Person SET FirstName = 'Fred' WHERE LastName = 'Wilson'
        结果：
        LastName	FirstName	Address	City
        Gates	Bill	Xuanwumen 10	Beijing
        Wilson	Fred	Champs-Elysees
        更新某一行中的若干列
        我们会修改地址（address），并添加城市名称（city）：
        UPDATE Person SET Address = 'Zhongshan 23', City = 'Nanjing'
        WHERE LastName = 'Wilson'
        结果：
        LastName	FirstName	Address	City
        Gates	Bill	Xuanwumen 10	Beijing
        Wilson	Fred	Zhongshan 23	Nanjing


DELETE 语句

DELETE 语句用于删除表中的行。

    语法
    DELETE FROM 表名称 WHERE 列名称 = 值
    Person:
    LastName	FirstName	Address	City
    Gates	Bill	Xuanwumen 10	Beijing
    Wilson	Fred	Zhongshan 23	Nanjing
    删除某行
    "Fred Wilson" 会被删除：
    DELETE FROM Person WHERE LastName = 'Wilson'
    结果:
    LastName	FirstName	Address	City
    Gates	Bill	Xuanwumen 10	Beijing
    删除所有行
    可以在不删除表的情况下删除所有的行。这意味着表的结构、属性和索引都是完整的：
    DELETE FROM table_name
    或者：
    DELETE * FROM table_name


INSERT INTO 语句

INSERT INTO 语句用于向表格中插入新的行。

    语法
    INSERT INTO 表名称 VALUES (值1, 值2,....)
    我们也可以指定所要插入数据的列：
    INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....)
    插入新的行
    "Persons" 表：
    LastName	FirstName	Address	City
    Carter	Thomas	Changan Street	Beijing
    SQL 语句：
    INSERT INTO Persons VALUES ('Gates', 'Bill', 'Xuanwumen 10', 'Beijing')
    结果：
    LastName	FirstName	Address	City
    Carter	Thomas	Changan Street	Beijing
    Gates	Bill	Xuanwumen 10	Beijing
    在指定的列中插入数据
    "Persons" 表：
    LastName	FirstName	Address	City
    Carter	Thomas	Changan Street	Beijing
    Gates	Bill	Xuanwumen 10	Beijing
    SQL 语句：
    INSERT INTO Persons (LastName, Address) VALUES ('Wilson', 'Champs-Elysees')
    结果：
    LastName	FirstName	Address	City
    Carter	Thomas	Changan Street	Beijing
    Gates	Bill	Xuanwumen 10	Beijing
    Wilson	 	Champs-Elysees


SELECT DISTINCT 语句

    在表中，可能会包含重复值。这并不成问题，不过，有时您也许希望仅仅列出不同（distinct）的值。
    关键词 DISTINCT 用于返回唯一不同的值。
    语法：
    SELECT DISTINCT 列名称 FROM 表名称
    使用 DISTINCT 关键词
    如果要从 "Company" 列中选取所有的值，我们需要使用 SELECT 语句：
    SELECT Company FROM Orders
    "Orders"表：
    Company	OrderNumber
    IBM	3532
    W3School	2356
    Apple	4698
    W3School	6953
    结果：
    Company
    IBM
    W3School
    Apple
    W3School
    请注意，在结果集中，W3School 被列出了两次。
    如需从 Company" 列中仅选取唯一不同的值，我们需要使用 SELECT DISTINCT 语句：
    SELECT DISTINCT Company FROM Orders
    结果：
    Company
    IBM
    W3School
    Apple


WHERE 子句

WHERE 子句用于规定选择的标准

    如需有条件地从表中选取数据，可将 WHERE 子句添加到 SELECT 语句。

    语法
    SELECT 列名称 FROM 表名称 WHERE 列 运算符 值
    下面的运算符可在 WHERE 子句中使用：
    操作符	描述
    =	等于
    <>	不等于
    >	大于
    <	小于
    >=	大于等于
    <=	小于等于
    BETWEEN	在某个范围内
    LIKE	搜索某种模式
    注释：在某些版本的 SQL 中，操作符 <> 可以写为 !=。

    使用 WHERE 子句
    如果只希望选取居住在城市 "Beijing" 中的人，我们需要向 SELECT 语句添加 WHERE 子句：
    SELECT * FROM Persons WHERE City='Beijing'
    "Persons" 表
    LastName	FirstName	Address	City	Year
    Adams	John	Oxford Street	London	1970
    Bush	George	Fifth Avenue	New York	1975
    Carter	Thomas	Changan Street	Beijing	1980
    Gates	Bill	Xuanwumen 10	Beijing	1985
    结果：
    LastName	FirstName	Address	City	Year
    Carter	Thomas	Changan Street	Beijing	1980
    Gates	Bill	Xuanwumen 10	Beijing	1985

    引号的使用
    请注意，我们在例子中的条件值周围使用的是单引号。
    SQL 使用单引号来环绕文本值（大部分数据库系统也接受双引号）。如果是数值，请不要使用引号。
    文本值：
    这是正确的：
    SELECT * FROM Persons WHERE FirstName='Bush'

    这是错误的：
    SELECT * FROM Persons WHERE FirstName=Bush
    数值：
    这是正确的：
    SELECT * FROM Persons WHERE Year>1965

    这是错误的：
    SELECT * FROM Persons WHERE Year>'1965'


AND 和 OR 运算符

AND 和 OR 运算符用于基于一个以上的条件对记录进行过滤

    AND 和 OR 运算符
    AND 和 OR 可在 WHERE 子语句中把两个或多个条件结合起来。
    如果第一个条件和第二个条件都成立，则 AND 运算符显示一条记录。
    如果第一个条件和第二个条件中只要有一个成立，则 OR 运算符显示一条记录。
    原始的表 (用在例子中的)：
    LastName	FirstName	Address	City
    Adams	John	Oxford Street	London
    Bush	George	Fifth Avenue	New York
    Carter	Thomas	Changan Street	Beijing
    Carter	William	Xuanwumen 10	Beijing
    AND 运算符实例
    使用 AND 来显示所有姓为 "Carter" 并且名为 "Thomas" 的人：
    SELECT * FROM Persons WHERE FirstName='Thomas' AND LastName='Carter'
    结果：
    LastName	FirstName	Address	City
    Carter	Thomas	Changan Street	Beijing
    OR 运算符实例
    使用 OR 来显示所有姓为 "Carter" 或者名为 "Thomas" 的人：
    SELECT * FROM Persons WHERE firstname='Thomas' OR lastname='Carter'
    结果：
    LastName	FirstName	Address	City
    Carter	Thomas	Changan Street	Beijing
    Carter	William	Xuanwumen 10	Beijing
    结合 AND 和 OR 运算符
    我们也可以把 AND 和 OR 结合起来（使用圆括号来组成复杂的表达式）:
    SELECT * FROM Persons WHERE (FirstName='Thomas' OR FirstName='William')
    AND LastName='Carter'
    结果：
    LastName	FirstName	Address	City
    Carter	Thomas	Changan Street	Beijing
    Carter	William	Xuanwumen 10	Beijing


ORDER BY 语句

ORDER BY 语句用于对结果集进行排序

    ORDER BY 语句用于根据指定的列对结果集进行排序。
    ORDER BY 语句默认按照升序对记录进行排序。
    如果您希望按照降序对记录进行排序，可以使用 DESC 关键字。
    原始的表 (用在例子中的)：
    Orders 表:
    Company	OrderNumber
    IBM	3532
    W3School	2356
    Apple	4698
    W3School	6953
    实例 1
    以字母顺序显示公司名称：
    SELECT Company, OrderNumber FROM Orders ORDER BY Company
    结果：
    Company	OrderNumber
    Apple	4698
    IBM	3532
    W3School	6953
    W3School	2356
    实例 2
    以字母顺序显示公司名称（Company），并以数字顺序显示顺序号（OrderNumber）：
    SELECT Company, OrderNumber FROM Orders ORDER BY Company, OrderNumber
    结果：
    Company	OrderNumber
    Apple	4698
    IBM	3532
    W3School	2356
    W3School	6953
    实例 3
    以逆字母顺序显示公司名称：
    SELECT Company, OrderNumber FROM Orders ORDER BY Company DESC
    结果：
    Company	OrderNumber
    W3School	6953
    W3School	2356
    IBM	3532
    Apple	4698
    实例 4
    以逆字母顺序显示公司名称，并以数字顺序显示顺序号：
    SELECT Company, OrderNumber FROM Orders ORDER BY Company DESC, OrderNumber ASC
    结果：
    Company	OrderNumber
    W3School	2356
    W3School	6953
    IBM	3532
    Apple	4698
    注意：在以上的结果中有两个相等的公司名称 (W3School)。只有这一次，在第一列中有相同的值时，第二列是以升序排列的。如果第一列中有些值为 nulls 时，情况也是这样的。


IN 操作符

IN 操作符允许我们在 WHERE 子句中规定多个值。

    SQL IN 语法
    SELECT column_name(s)
    FROM table_name
    WHERE column_name IN (value1,value2,...)
    原始的表 (在实例中使用：)
    Persons 表:
    Id	LastName	FirstName	Address	City
    1	Adams	John	Oxford Street	London
    2	Bush	George	Fifth Avenue	New York
    3	Carter	Thomas	Changan Street	Beijing
    IN 操作符实例
    现在，我们希望从上表中选取姓氏为 Adams 和 Carter 的人：
    我们可以使用下面的 SELECT 语句：
    SELECT * FROM Persons
    WHERE LastName IN ('Adams','Carter')
    结果集：
    Id	LastName	FirstName	Address	City
    1	Adams	John	Oxford Street	London
    3	Carter	Thomas	Changan Street	Beijing


LIKE 操作符

    LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式。
    SQL LIKE 操作符语法
    SELECT column_name(s)
    FROM table_name
    WHERE column_name LIKE pattern
    原始的表 (用在例子中的)：
    Persons 表:
    Id	LastName	FirstName	Address	City
    1	Adams	John	Oxford Street	London
    2	Bush	George	Fifth Avenue	New York
    3	Carter	Thomas	Changan Street	Beijing
    LIKE 操作符实例
    例子 1
    现在，我们希望从上面的 "Persons" 表中选取居住在以 "N" 开始的城市里的人：
    我们可以使用下面的 SELECT 语句：
    SELECT * FROM Persons
    WHERE City LIKE 'N%'
    提示："%" 可用于定义通配符（模式中缺少的字母）。
    结果集：
    Id	LastName	FirstName	Address	City
    2	Bush	George	Fifth Avenue	New York
    例子 2
    接下来，我们希望从 "Persons" 表中选取居住在以 "g" 结尾的城市里的人：
    我们可以使用下面的 SELECT 语句：
    SELECT * FROM Persons
    WHERE City LIKE '%g'
    结果集：
    Id	LastName	FirstName	Address	City
    3	Carter	Thomas	Changan Street	Beijing
    例子 3
    接下来，我们希望从 "Persons" 表中选取居住在包含 "lon" 的城市里的人：
    我们可以使用下面的 SELECT 语句：
    SELECT * FROM Persons
    WHERE City LIKE '%lon%'
    结果集：
    Id	LastName	FirstName	Address	City
    1	Adams	John	Oxford Street	London
    例子 4
    通过使用 NOT 关键字，我们可以从 "Persons" 表中选取居住在不包含 "lon" 的城市里的人：
    我们可以使用下面的 SELECT 语句：
    SELECT * FROM Persons
    WHERE City NOT LIKE '%lon%'
    结果集：
    Id	LastName	FirstName	Address	City
    2	Bush	George	Fifth Avenue	New York
    3	Carter	Thomas	Changan Street	Beijing


BETWEEN 操作符

    操作符 BETWEEN ... AND 会选取介于两个值之间的数据范围。这些值可以是数值、文本或者日期。
    SQL BETWEEN 语法
    SELECT column_name(s)
    FROM table_name
    WHERE column_name
    BETWEEN value1 AND value2
    原始的表 (在实例中使用：)
    Persons 表:
    Id	LastName	FirstName	Address	City
    1	Adams	John	Oxford Street	London
    2	Bush	George	Fifth Avenue	New York
    3	Carter	Thomas	Changan Street	Beijing
    4	Gates	Bill	Xuanwumen 10	Beijing
    BETWEEN 操作符实例
    如需以字母顺序显示介于 "Adams"（包括）和 "Carter"（不包括）之间的人，请使用下面的 SQL：
    SELECT * FROM Persons
    WHERE LastName
    BETWEEN 'Adams' AND 'Carter'
    结果集：
    Id	LastName	FirstName	Address	City
    1	Adams	John	Oxford Street	London
    2	Bush	George	Fifth Avenue	New York
    重要事项：不同的数据库对 BETWEEN...AND 操作符的处理方式是有差异的。某些数据库会列出介于 "Adams" 和 "Carter" 之间的人，但不包括 "Adams" 和 "Carter" ；某些数据库会列出介于 "Adams" 和 "Carter" 之间并包括 "Adams" 和 "Carter" 的人；而另一些数据库会列出介于 "Adams" 和 "Carter" 之间的人，包括 "Adams" ，但不包括 "Carter" 。
    所以，请检查你的数据库是如何处理 BETWEEN....AND 操作符的！
    实例 2
    如需使用上面的例子显示范围之外的人，请使用 NOT 操作符：
    SELECT * FROM Persons
    WHERE LastName
    NOT BETWEEN 'Adams' AND 'Carter'
    结果集：
    Id	LastName	FirstName	Address	City
    3	Carter	Thomas	Changan Street	Beijing
    4	Gates	Bill	Xuanwumen 10	Beijing


SQL Alias

通过使用 SQL，可以为列名称和表名称指定别名（Alias）

    表的 SQL Alias 语法
    SELECT column_name(s)
    FROM table_name
    AS alias_name
    列的 SQL Alias 语法
    SELECT column_name AS alias_name
    FROM table_name
    Alias 实例: 使用表名称别名
    假设我们有两个表分别是："Persons" 和 "Product_Orders"。我们分别为它们指定别名 "p" 和 "po"。
    现在，我们希望列出 "John Adams" 的所有定单。
    我们可以使用下面的 SELECT 语句：
    SELECT po.OrderID, p.LastName, p.FirstName
    FROM Persons AS p, Product_Orders AS po
    WHERE p.LastName='Adams' AND p.FirstName='John'
    不使用别名的 SELECT 语句：
    SELECT Product_Orders.OrderID, Persons.LastName, Persons.FirstName
    FROM Persons, Product_Orders
    WHERE Persons.LastName='Adams' AND Persons.FirstName='John'
    从上面两条 SELECT 语句您可以看到，别名使查询程序更易阅读和书写。
    Alias 实例: 使用一个列名别名
    表 Persons:
    Id	LastName	FirstName	Address	City
    1	Adams	John	Oxford Street	London
    2	Bush	George	Fifth Avenue	New York
    3	Carter	Thomas	Changan Street	Beijing
    SQL:
    SELECT LastName AS Family, FirstName AS Name
    FROM Persons
    结果：
    Family	Name
    Adams	John
    Bush	George
    Carter	Thomas


1.ORM的支持及配置

数据库支持

NFinal框架支持Mysql,Sqlserver,sqlite,oracle,postgresql数据库.

在建立数据库时有以下原则:
1.表名与表字段不能与csharp的关键字重名.
2.select语句选取的结果中,列名不能相同,若有相同的列名,必须使用as重命名.

1.oracle在配置并生成后，会在Models目录生成相应的sql文件，此文件中的sql语句用于添加自增id.
2.postgreql在建表时，id字段选择serial类型，则会自动自增。
3.为了统一管理，所有数据库sql语句中插入的变量均采用@符号。

NFinal使用数据库操作类操作数据库,首先请先配置好Web.config中的连接字符串,
数据库的配置请参考这里


2.ORM生成原则及使用流程

数据库使用原则

1.所有的数据库语句只能写在Controlers文件夹或Models/BLL文件夹下的类中

数据库函数使用流程

1.函数中的代码如下:
        //打开关闭连接,必须以Models开头.其中YMC是Web.Config中连接字符串的名称.
        public void Index(int id)
        {
            //打开数据库连接
            var con=Models.YMC.OpenConnection();
            //查询
            //使用@变量名添加csharp中的变量到SQL中.
            var users=con.QueryAll("select * from users where id=@id");
            //关闭连接
            con.Close();
            //输出
            foreach(var user in users)
            {
                Write(user.id);
                Write(user.name);
                Write(user.pwd);
            }
        }
2.运行WebCompiler.aspx将在Web层或DAL层对应位置的类中生成以下代码:
        public void Index(int id)
        {
            var __QueryAll_Common_con__ = new System.Data.SQLite.SQLiteConnection(Models.ConnectionStrings.Common);
            __QueryAll_Common_con__.Open();
                #region	var users;选取所有记录
            var users = new NFinal.DB.NList<__queryall_users__>();

            var __QueryAll_users_command__ = new System.Data.SQLite.SQLiteCommand("select * from users where id=@id", __queryall_common_con__);
            var __QueryObject_count_parameters__=new System.Data.SQLite.SQLiteParameter[1];

            __QueryObject_count_parameters__[0] = new System.Data.SQLite.SQLiteParameter("@id",System.Data.DbType.Int64,8);
            __QueryObject_count_parameters__[0].Value = id;

            __QueryObject_count_command__.Parameters.AddRange(__QueryObject_count_parameters__);

            var __queryall_users_reader__=__QueryAll_users_command__.ExecuteReader();
            if (__queryall_users_reader__.hasrows)
            {
                while (__queryall_users_reader__.read())
                {
                    var __queryall_users_temp__=new __queryall_users__();
                    if(!__queryall_users_reader__.isdbnull(0)){__queryall_users_temp__.uid=__QueryAll_users_reader__.GetInt64(0);}
                    if(!__queryall_users_reader__.isdbnull(1)){__queryall_users_temp__.name=__QueryAll_users_reader__.GetString(1);}
                    if(!__queryall_users_reader__.isdbnull(2)){__queryall_users_temp__.pwd=__QueryAll_users_reader__.GetString(2);}
                    users.add(__queryall_users_temp__);
                }
            }
            __queryall_users_reader__.close();
            #endregion
            __queryall_common_con__.close();
            foreach (var user in users)
            {
                Write(user.id);
                Write(user.name);
                Write(user.pwd);
            }
        }
还生成了users所对应的实体类__queryall_users__
    public class __QueryAll_users__:NFinal.DB.Struct
    {
        public System.Int64 id;
        public System.String name;
        public System.String pwd;
        #region 写Json字符串
        public override void WriteJson(System.IO.TextWriter tw)
        {
            tw.Write("{");
            tw.Write("\"id\":");
            tw.Write(id.ToString());
            tw.Write(",");
            tw.Write("\"name\":");
            tw.Write("\"");
            tw.Write(name);
            tw.Write("\"");
            tw.Write(",");
            tw.Write("\"pwd\":");
            tw.Write("\"");
            tw.Write(pwd);
            tw.Write("\"");
            tw.Write("}");
        }
        #endregion
    }


3.ORM使用的几种方式

数据库操作

一般执行

        var con = Models.Common.OpenConnection();
        var us= con.QueryAll("select id,name,pwd from users");
        con.Close();

带参数的执行

        int vid=2;
        var con = Models.Common.OpenConnection();
        //在sql语句中直接写@变量名即可把变量值写入sql中。
        var us= con.QueryAll("select id,name,pwd from users where id>@vid");
        con.Close();

使用SQL语句提示（$号开头的字符串只有vs2005及以上版本才可使用）

        //使用sql自动提示类，找不到类的把Models/Tips目录包含进项目中来
        Models.Tips.Common.users users = new Models.Tips.Common.users();
        var con = Models.Common.OpenConnection();
        //Tips类不起任何作用代码方面的作用，只是起自动提示的效果。
        //NFinal引擎在解析时会把大括号{}去掉，当成sql语句进行进一步解析。
        //以下语句和var us= con.QueryAll("select users.id,users.name,users.pwd from users");相同。
        var us= con.QueryAll($"select {users.id},{users.name},{users.pwd} from {users}");
        con.Close();

使用SQL语句提示下的带参数执行

        //使用sql自动提示类，找不到类的把Models/Tips目录包含进项目中来
        Models.Tips.Common.users users = new Models.Tips.Common.users();
        int vid=2;
        var con = Models.Common.OpenConnection();
        //Tips类不起任何作用代码方面的作用，只是起自动提示的效果。
        //NFinal引擎在解析时会把大括号{}去掉，当成sql语句进行进一步解析。
        //以下语句和var us= con.QueryAll("select users.id,users.name,users.pwd from users where users.id >@vid");相同。
        var us= con.QueryAll($"select {users.id},{users.name},{users.pwd} from {users} where {users.id}>@vid");
        con.Close();

使用自定义的Entity类

        var con = Models.Common.OpenConnection();
        List<Models.Entity.Common.users> users = new List<Models.Entity.Common.users>();
        users= con.QueryAll<Models.Entity.Common.users>("select * from users");
        con.Close();

使用存储过程

        var con = Models.Common.OpenConnection();
        var trans = con.BeginTransaction();
        try
        {
            var users = trans.QueryAll("select * from users");
            AjaxReturn(users);
            trans.Rollback();
        }
        catch
        {
            trans.Rollback();
        }
        con.Close();

使用缓存,首先应启动NFinal\Redis\32bit或NFinal\Redis\64bit下的redis-server.exe服务

        var client = NFinal.Cache.GetRedisClient();
        var client2 = client.As<Models.Entity.Common.users>();
        var users = client2.GetAll();
        if (users.Count==0)
        {
            var con = Models.Common.OpenConnection();
            users = con.QueryAll<Models.Entity.Common.users>("select * from users");
            client2.StoreAll(users);
            con.Close();
        }
        client.Dispose();

使用ajax返回数据

        var con = Models.Common.OpenConnection();
        var us= con.QueryAll($"select {users.id},{users.name},{users.pwd} from {users}");
        con.Close();
        //所有的对象均可调用此函数返回json字符串，
        //若调用的是con返回的数据库对象，其速度是newtonsoft的1000倍以上。
        AjaxReturn(us);


4.1.QueryRandom选取随机行

QueryRandom方法

QueryRandom(string sql，int top) 有两个参数 sql:为sql查询语句 top:需要获得的行数
返回：行对象数组

1、无参数
    public void  Index()
    {
        //打开一个数据库链接
        var con= Models.YMC.OpenConnection();
        var rows= con.QueryRandom("SELECT * FROM user",3);
        //关闭数据库连接
        con.Close();
    }
2、有默认值的参数
    public void  Index(string pwd="123456")
    {
        //打开一个数据库链接
        var con = Models.YMC.OpenConnection();
        var row = con.QueryRandom("SELECT * FROM user WHERE pwd=@pwd"，3);
        //关闭数据库连接
        con.Close();
    }
3、vs2015支持的新语法，让SQL语句可以有自动提示
        Models.Tips.Common.users users = new Models.Tips.Common.users();
        public void queryRandom()
        {
            var con = Models.Common.OpenConnection();
            var us = con.QueryRandom($"select {users.id},{users.name},{users.pwd} from {users}",3);
            con.Close();
            AjaxReturn(us);
        }

模板会根据以上代码生成正确的执行代码，此处不再显示。可以参见QueryRandom中的模板生成的代码


4.2.QueryTop返回前几行

QueryTop方法

QueryTop(string sql，int top) 有两个参数 sql:为sql查询语句 top:需要获得的行数
返回：行对象数组

1、无参数
    public void  Index()
    {
        //打开一个数据库链接
        var con= Models.YMC.OpenConnection();
        var rows= Models.YMC.QueryTop("SELECT * FROM user",3);
        //关闭数据库连接
        con.Close();
    }

模板会根据以上代码生成正确的执行代码，此处不再显示。可以参见QueryRandom中的模板生成的代码

2、有默认值的参数
    public void  Index(string pwd="123456")
    {
        //打开一个数据库链接
        var con = Models.YMC.OpenConnection();
        var row = con.QueryTop("SELECT * FROM user WHERE pwd=@pwd"，3);
        //关闭数据库连接
        con.Close();
    }
3、vs2015支持的新语法，让SQL语句可以有自动提示
        Models.Tips.Common.users users = new Models.Tips.Common.users();
        public void queryTop(int top)
        {
            var con = Models.Common.OpenConnection();
            var us = con.QueryTop($"select {users.id},{users.name},{users.pwd} from {users}",top);
            con.Close();
            AjaxReturn(us);
        }

模板会根据以上代码生成正确的执行代码，此处不再显示。可以参见QueryRandom中的模板生成的代码


4.3.QueryAll选取所有行

QueryAll方法

QueryAll(string sql) 有一个参数 sql:为sql查询语句
返回：行对象数组

1、无参数：
    public void  Index()
    {
        //打开一个数据库链接
        var con = Models.YMC.OpenConnection();
        var rows = con.QueryAll("SELECT * FROM user");
        //关闭数据库连接
        con.Close();
    }

模板会根据以上代码生成正确的执行代码，此处不再显示。可以参见QueryRandom中的模板生成的代码

2、有默认值的参数
    public void  Index(string pwd="123456")
    {
        //打开一个数据库链接
        var con = Models.YMC.OpenConnection();
        var row= con.QueryAll("SELECT * FROM user WHERE pwd=@pwd");
        //关闭数据库连接
        con.Close();
    }
3、vs2015支持的新语法，让SQL语句可以有自动提示
        Models.Tips.Common.users users = new Models.Tips.Common.users();
        public void queryAll()
        {
            var con = Models.Common.OpenConnection();
            var us= con.QueryAll($"select {users.id},{users.name},{users.pwd} from {users}");
            con.Close();
            AjaxReturn(us);
        }

模板会根据以上代码生成正确的执行代码，此处不再显示。可以参见QueryRandom中的模板生成的代码


4.4.QueryRow返回行对象

QueryRow方法

QueryAll(string sql) 有一个参数 sql:为sql查询语句
返回：行对象

1、无参数：
    public void  Index()
    {
        //打开一个数据库链接
        var con = Models.YMC.OpenConnection();
        var row = con.QueryRow("SELECT * FROM user where id=23");
        //关闭数据库连接
        con.Close();
    }

模板会根据以上代码生成正确的执行代码，此处不再显示。可以参见QueryRandom中的模板生成的代码

2、有默认值的参数：
         public void Index(int id=23)
        {
            //打开一个数据库链接
            var con = Models.YMC.OpenConnection();
            var row = con.QueryRow("SELECT * FROM user where id=@id");
            //关闭数据库连接
            con.Close();
        }
3、vs2015支持的新语法，让SQL语句可以有自动提示
        Models.Tips.Common.users users = new Models.Tips.Common.users();
        public void queryRow(int id)
        {
            var con = Models.Common.OpenConnection();
            var user = con.QueryRow($"select {users.id},{users.name},{users.pwd} from {users} where {users.id}=@id");
            con.Close();
            AjaxReturn(user);
        }

模板会根据以上代码生成正确的执行代码，此处不再显示。可以参见QueryRandom中的模板生成的代码


4.5.QueryObject返回首行首列

QueryObject方法

QueryObject(string sql) 有一个参数 sql:为sql查询语句
返回：Object对象
（注：此方法返回Object对象所以必须用To类的转换函数来转换数据类型）
To类函数:ToInt()、ToByte()、ToDateTime()、ToDecimal()、ToDouble()、ToFloat()、ToLong()、ToString();

1、无参数
    public void  Index()
    {
        //打开一个数据库链接
        var con = Models.YMC.OpenConnection();
        var row = con.QueryObject("SELECT count(*) FROM user").ToInt();//用此方法后面必须跟To类的类型转换函数
        //关闭数据库连接
        con.Close();
    }

模板会根据以上代码生成正确的执行代码，此处不再显示。可以参见QueryRandom中的模板生成的代码

2、有默认值的参数
        public void Index(int id=23)
        {
            //打开一个数据库链接
            var con = Models.YMC.OpenConnection();
            var row = con.QueryObject("select id from user where id=@id").ToInt();
            //关闭数据库连接
            con.Close();
        }
3、vs2015支持的新语法，让SQL语句可以有自动提示
        Models.Tips.Common.users users = new Models.Tips.Common.users();
        public void queryObject()
        {
            var con = Models.Common.OpenConnection();
            var count = con.QueryObject($"select count(*) from {users}").ToInt32();
            con.Close();
            Write(count);
        }

模板会根据以上代码生成正确的执行代码，此处不再显示。可以参见QueryRandom中的模板生成的代码


5.NFinal.Page类实现分页

Page方法

Page(string sql,int ) 有两个参数 sql:sql查询语句 pageSize:每页显示行数
返回：行对象数组

1.1
    public void  Index(int p)
    {
        //打开一个数据库链接
        var con = Models.YMC.OpenConnection();
        //参数1为当前页数，参数2为每页大小
        NFinal.Page page=new NFinal.Page(p,5);
        var rows = con.Page("SELECT * FROM user",page);
        //关闭数据库连接
        con.Close();
        View("IndexController/Index.aspx");
    }
1.2、vs2015支持的新语法，让SQL语句可以有自动提示
        Models.Tips.Common.users users = new Models.Tips.Common.users();
        [GetJson("/page-{p}.json")]
        public void page(int p)
        {
            var con = Models.Common.OpenConnection();
            NFinal.Page page = new NFinal.Page(p, 2);
            var us = con.Page($"select {users.id},{users.name},{users.pwd} from {users}",page);
            con.Close();
            AjaxReturn(us);
        }
2.使用分页控件Views/Default/Common/Public/Navigator.ascx,将其拖入要使用的aspx页面即可
        <div>
            <uc1:navigator runat="server" id="navigator" db="<%ViewBag.page%>" />
        </div>

说明：db="Page对象"

3.模板会根据以上代码生成正确的执行代码，此处不再显示。


6.Insert,Update,Delete实现增删改

Insert方法

语法： Insert(string sql) 参数 sql:为sql插入语句
返回：插入数据的id(int类型)

    public void Insert(string name ,string pwd)
    {
        //打开数据库链接
        var con = Models.YMC.OpenConnection();
        //获取插入数据的id
        var id = con.Insert("insert into user(name,pwd) values(@name,@pwd)");
        //关闭数据库连接
        con.Close();
    }
使用vs2015新增语法，SQL语句有自动提示
    Models.Tips.Common.users users = new Models.Tips.Common.users();
    public void insert(string name, string pwd)
    {
        var con = Models.Common.OpenConnection();
        var id = con.Insert($"insert into {users}({users.name},{users.pwd}) values(@name,@pwd)");
        con.Close();
        Write(id);
    }

模板会根据以上代码生成正确的执行代码，此处不再显示。

Update方法

语法： Update(string sql) 参数 sql:为slq修改语句
返回：int 受影响行数

    public void Index(string name="大白",int id=23)
    {
        //打开一个数据库链接
        var con = Models.YMC.OpenConnection();
        var count = con.Update("Update user set name=@name where id=@id");
        //关闭数据库连接
        con.Close();
    }
使用vs2015新增语法，SQL语句有自动提示
    Models.Tips.Common.users users = new Models.Tips.Common.users();
    public void update(
        [maxLength(12),required()]
        string name,

        string pwd)
    {
        var con = Models.Common.OpenConnection();
        var count = con.Update($"update {users} set {users.name}=@name,{users.pwd}=@pwd where {users.id}=1");
        con.Close();
        Write(count);
    }

模板会根据以上代码生成正确的执行代码，此处不再显示。

Delete方法

语法： Delete(string sql) 参数 sql:为sql删除语句
返回：int 受影响行数

    public void Index(int id=25)
    {
        //打开一个数据库链接
        var con = Models.YMC.OpenConnection();
        var count = con.Delete("delete from user  where id=@id");
        //关闭数据库连接
        con.Close();
    }
使用vs2015新增语法，SQL语句有自动提示
    Models.Tips.Common.users users = new Models.Tips.Common.users();
    public void delete(int id)
    {
        var con = Models.Common.OpenConnection();
        var count = con.Delete($"delete from {users} where {users.id}=@id");
        con.Close();
        Write(count);
    }

模板会根据以上代码生成正确的执行代码，此处不再显示。可以参见QueryRandom中的模板生成的代码


7.父子表,多表关系

AddNewFiled方法

AddNewFiled(string filedName,type t) 有一个参数 filedName:所要添加的列名 type:所要添加列的类型（一般为typeof(List)）
返回：无返回值
为查询到的数据（表）添加一列数据（字段、子表）,实现数据的树形展示（层次化）

用例说明：查询所有班级表并且查询班级下的学生信息

    public void  Index()
    {
        //打开一个数据库链接
        var con = Models.YMC.OpenConnection();
        var classes = con.QueryAll("select id,name from classes");
        //为classes行对象数组的每一个行对象添加一个students行对象数组的属性
        classes.AddNewField("students", typeof(List));
        foreach( var cla in classes)
        {
            int cla_id = cla.id;
            var students = Models.YMC.QueryAll("select name,sex from stutent where cla_id=@cla_id");
            //为行对象cla的属性students赋值
            cla.students = students;
        }
        //关闭数据库连接
        con.Close();
        View("IndexController/Index.aspx");
    }

模板会根据以上代码生成以下代码

                public class __Index_classes__:NFinal.DB.Struct
        {

            public int id;

            public string name;

            public NFinal.DB.NList<__index_students__> students;

            #region 写Json字符串
            public override void WriteJson(System.IO.TextWriter tw)
            {
                tw.Write("{");

                        tw.Write("\"id\":");
                        tw.Write("\"");
                        WriteString(id==null?"null":id.ToString(),tw);
                        tw.Write("\"");

                        tw.Write(",");

                        tw.Write("\"name\":");
                        tw.Write("\"");
                        WriteString(name==null?"null":name.ToString(),tw);
                        tw.Write("\"");

                            tw.Write(",");
                            tw.Write("\"students\":");
                            students.WriteJson(students.GetEnumerator(),tw);

                tw.Write("}");
            }
            #endregion
        }
    #endregion
        #region __Index_students__声明
        public class __Index_students__:NFinal.DB.Struct
        {

            public string name;

            public string sex;

            #region 写Json字符串
            public override void WriteJson(System.IO.TextWriter tw)
            {
                tw.Write("{");

                        tw.Write("\"name\":");
                        tw.Write("\"");
                        WriteString(name==null?"null":name.ToString(),tw);
                        tw.Write("\"");

                        tw.Write(",");

                        tw.Write("\"sex\":");
                        tw.Write("\"");
                        WriteString(sex==null?"null":sex.ToString(),tw);
                        tw.Write("\"");

                tw.Write("}");
            }
            #endregion
        }
    #endregion
        public void Index()
        {
            //打开一个数据库链接
                        var __Index_YMC_con__ = new MySql.Data.MySqlClient.MySqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["YMC"].ConnectionString);
            __Index_YMC_con__.Open();
                #region	var classes;选取所有记录
            var classes = new NFinal.DB.NList<__index_classes__>();

            var __Index_classes_command__ = new MySql.Data.MySqlClient.MySqlCommand("select id,name from classes", __Index_YMC_con__);

            var __Index_classes_reader__= __Index_classes_command__.ExecuteReader();
            if (__Index_classes_reader__.HasRows)
            {
                while (__Index_classes_reader__.Read())
                {
                    var __Index_classes_temp__ = new __Index_classes__();

                    if(!__Index_classes_reader__.IsDBNull(1-1)){__Index_classes_temp__.id = __Index_classes_reader__.GetInt32(1-1);}

                    if(!__Index_classes_reader__.IsDBNull(2-1)){__Index_classes_temp__.name = __Index_classes_reader__.GetString(2-1);}
                    classes.Add(__Index_classes_temp__);
                }
            }
            __Index_classes_reader__.Close();
    #endregion

            /*field:classes.students*/
            foreach( var cla in classes)
            {
                int cla_id = cla.id;
                    #region	var students;选取所有记录
            var students = new NFinal.DB.NList<__index_students__>();

            var __Index_students_command__ = new MySql.Data.MySqlClient.MySqlCommand("select name,sex from stutent where cla_id=?cla_id", __Index_YMC_con__);

            var __Index_students_parameters__=new MySql.Data.MySqlClient.MySqlParameter[1];

            __Index_students_parameters__[1-1] = new MySql.Data.MySqlClient.MySqlParameter("?cla_id",MySql.Data.MySqlClient.MySqlDbType.Int32);
            __Index_students_parameters__[1-1].Value = cla_id;

            __Index_students_command__.Parameters.AddRange(__Index_students_parameters__);

            var __Index_students_reader__= __Index_students_command__.ExecuteReader();
            if (__Index_students_reader__.HasRows)
            {
                while (__Index_students_reader__.Read())
                {
                    var __Index_students_temp__ = new __Index_students__();

                    if(!__Index_students_reader__.IsDBNull(1-1)){__Index_students_temp__.name = __Index_students_reader__.GetString(1-1);}

                    if(!__Index_students_reader__.IsDBNull(2-1)){__Index_students_temp__.sex = __Index_students_reader__.GetString(2-1);}
                    students.Add(__Index_students_temp__);
                }
            }
            __Index_students_reader__.Close();
    #endregion

                cla.students = students;
            }
            //关闭数据库连接
           _Index_YMC_con__.Close();
       }

前台读取数据

        <%foreach (var classe in classes){%>

班级名称：<%=classe.name%>

            <%foreach(var stutent in classe.students){ %>
                <%=stutent.name %> <%=stutent.sex %>
        <%}} %>

展示效果


8.如何使用BLL与DAL层

如何使用BLL与DAL层

BLL逻辑操作层

        //声明一个类，并包含以下函数
        //其参数为con,即var con=Modles.Common.OpenConnection();中的con
        public List<dynamic> GetAll(System.Data.IDbConnection con)
        {
            var con1 = Models.Common.GetConnection(con);
            var users = con1.QueryAll("select * from users");
            //当返回的是多行时其返回值类型为List<dynamic>
            return users;
        }
        public dynamic GetRow(System.Data.IDbConnection con)
        {
            var con1 = Models.Common.GetConnection(con);
            var user = con1.QueryRow("select * from users where id=1");
            //当返回的是单行时其返回值类型为dynamic
            return user;
        }
        //其它情况下返回值是什么类型就写什么类型
        public List<Models.Entity.users> GetAllUsers(System.Data.IDbConnection con)
        {
            var con1 = Models.Common.GetConnection(con);
            var users = con1.QueryAll<Models.Entity.users>("select * from users");
            return users;
        }
        public Models.Entity.users GetRowUser(System.Data.IDbConnection con)
        {
            var con1 = Models.Common.GetConnection(con);
            var user = con1.QueryRow<Models.Common.users>("select * from users where id=1");
            return user;
        }

DAL层会生成如下代码

        //此处仅列一项以做说明
        public NFinal.List<__GetAll_users__> GetAll(System.Data.IDbConnection con)
        {
            var con1=(System.Data.SQLite.SQLiteConnection)con;
            #region	var users;选取所有记录
            var users = new NFinal.List<__GetAll_users__>();
            var __GetAll_users_command__ = new System.Data.SQLite.SQLiteCommand("select * from users", con1);
            var __GetAll_users_reader__= __GetAll_users_command__.ExecuteReader();
            if (__GetAll_users_reader__.HasRows)
            {
                while (__GetAll_users_reader__.Read())
                {
                    var __GetAll_users_temp__ = new __GetAll_users__();
                    __GetAll_users_temp__.id = __GetAll_users_reader__.GetInt64(0);
                    __GetAll_users_temp__.name =__GetAll_users_reader__.IsDBNull(1)?null: __GetAll_users_reader__.GetString(1);
                    __GetAll_users_temp__.pwd =__GetAll_users_reader__.IsDBNull(2)?null: __GetAll_users_reader__.GetString(2);
                    users.Add(__GetAll_users_temp__);
                }
            }
            __GetAll_users_reader__.Dispose();
            __GetAll_users_command__.Dispose();
            #endregion
            return users;
        }

Controller层的调用

        var con = Models.Common.OpenConnection();
        //DAL中的类声明
        Models.DAL.Sqlite sqlite = new Models.DAL.Sqlite();
        //使用由BLL生成的DAL类中的GetAll函数
        //数据库操作时，尽量减少数据库打开关闭的次数，
        //因为在.net中打开一次数据库的时间远远大于一般的查询操作。
        var users= sqlite.GetAll(con);
        con.Close();


View函数的使用

View函数只能在控制器中使用,
其作用为调用模板进行渲染,参数值为模板路径.
其使用方法为:

        //在Controllers/Admin/IndexController.cs 中的Index方法体内.
        //正常的写法为:
        View("IndexController/Index.aspx");

        //当调用Views/Default/Admin/IndexController/Show.aspx时,可简写为
        View("Show.aspx");
        //当调用Views/Default/Admin/IndexController/Index.aspx时,可以简写为
        View("");

        //前面加'/'符号代表相对于Views/Default/根目录的路径.
        View("/Admin/IndexController/Index.aspx");

生成完成后再编辑aspx时，就会有自动提示功能。
包括ViewBag的自提示，以及有模板标签的自提示。


1.基本视图语法

基本视图语法

        //1、<%%>中可直接写csharp代码.
        <% int a=0; %>
        //2、<%= %>输出
        <%=a %>
        //3、ViewBag.变量名(访问控制器中的变量)
        <%=ViewBag.title%>

在视图中可以直接输出控制器方法内的变量以及方法中的参数

    public class IndexController:Controller
    {
        public void Index(string name)
        {
            string word="hello ";
            View("Index.aspx");
        }
    }

则在视图中可以直接调用相关变量,如下:

        //当我调用Index/name/Lucas.htm时
        //输出hello Lucas
        <%=ViewBag.word%><%=ViewBag.name%>


2.高级视图语法—循环

高级视图语法—循环

        <%//Foreach循环 %>
        <foreach enumerator="<%var c = b.GetEnumerator();%>">
            <%=c.Current.ToString() %>
        </foreach>

        <%//For循环 %>
        <for start="<%int  i = 0; %>" condition="<%= i < b.Length %>" step="<%i++;%>">
            <%=b[i].ToString() %>
        </for>

        <%//While do循环 %>
        <%int k = 0; %>
        <while condition="<%=k < b.Length; %>">
            <%=b[k].ToString(); %>
            <%k++; %>
        </while>


3.高级视图语法—分支

高级视图语法—分支

        <%int a = 3; %>
        <%//if判断 %>
        <if condition="<%=a > 3 %>">
            true
        <else />
            false
        </if>

        <if condition="<%=a>1 %>">
            1
        <elseif condition="<%=a>2 %>" />
            2
        <else/>
            3
        </if>

        <%//switch判断 %>
        <switch variable="<%=a%>">
            <case value="<%=1 %>">
                1
            </case>
            <case value="<%=2 %>">
                2
            </case>
            <default>
                other
            </default>
        </switch>

        <%int[] b=new int[]{1,2,3,4}; %>


模板

创建模板

1.新建Header.ascx用户控件,此控件就是模板,修改内容如下:

                <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Header.ascx.cs" Inherits="WebMvc.App.Views.Default.Common.Public.Header" %>
                <header class="header">
                    我是头模板，
                    <%=ViewBag.message %>
                </header>
以下是为了让编译器不出现错误警告而增加的内容,可以忽略.
a.以上模版中,ViewBag.message变量是不存在的.为了让编译不出错,请在Header.ascx.cs文件中添加如下变量:
            public partial class Header : System.Web.UI.UserControl
            {
                public class Header_AutoComplete:Controller
                {
                        public string message;
                }
                public Header_AutoComplete ViewBag = new Header_AutoComplete();
            }
b.以上手动添加模板的ViewBag类,十分烦索.可以添加如下控制器函数,然后运行WebCompiler.aspx将自动生成ViewBag类:
            public void Header()
            {
                string message=null;
                View("Public/Header.ascx");
            }

使用模板

直接将.ascx拖入要使用的aspx(Web页面)地方，

                    public void Index(string message)
                    {
                        string message="我来自模版";
                        View("Index.aspx");
                    }


foreach

if

for


4.标签提示

参数提示

在Action中定义了变量的注释

            ///
            /// 房产列表页
            ///
            /// 区域</param>
            /// 价格</param>
            /// 房型</param>
            /// 类型</param>
            /// 当前页数</param>
            /// 搜索词</param>
            [GetHtmlZip("/List-{quyu}-{jiage}-{fangxing}-{leixing}-{p}.html")]
            public void List(int quyu,int jiage, int fangxing, int leixing, int p, string s=null)
            {
            }

运行WebCompiler.aspx,在对应的aspx模板中就会出现对应的中文提示


如果在Action中对变量加入了单行注释（只能为//的注释）

那么在模板中


5.数据库提示

数据库提示

当使用WebCompiler.aspx后，数据库中会多出一张nfinal_note的表。

此表共有三个字段，table_name,field_name,field_note.
对应为表名，列名，列注释.此表为自动生成，你可以手动添加注释

当你完成注释添加后，在模板中调用ORM返回的数据库对象时会的相关提示。


提示原则

如果前端和后端分为两个人，尽量在后台把所有的提示都加进去。
这样做的好处是，前端人员就算不懂一点代码，照样能够自己一个人把模板做好。
前端与后端的进一步隔离是加快工作效率的最有效的办法。


了解JNTemplate

New Help > 了解JNTemplate
了解JNTemplate

一、什么是JNTemplate

    极念模板引擎(JNTemplate engine)是一款.NET平台的解释型模板引擎，它允许用户使用简单的模板语言来定义页面及引用后台设置的对象,让前端展示与后端代码逻辑分离，同时，它也提供了一个在WebForm与Razor之外的选择！

二、JNTemplate能干什么

    1.可以通过简单的模板语言来呈现后端数据，让开发人员专注于逻辑处理

    2.可以通过引擎生成静态页面

    3.可以动态换肤

    以上功能仅仅是我们最常见的一些应用，实际上，JNTemplate可以生成任何基于文本格式的内容，比如XML，JSON等，而且不仅仅局限于WEB开发！

三、JNTemplate可以用于商业目的吗

    JNTemplate是基于apache license 2.0协议开源的，可以用于商业目的，只要你在延伸的代码中（修改和有源代码衍生的代码中）保留原来代码中的协议，作者与网址等信息，具体可见 手册中的许可协议

四、为什么选择JNTemplate

    1.易用:上手快，有一定c#基础或者js基础的，只要10分钟就可以学会使用，极大降低学习成本

    2.轻量:代码精简，功能不减! 结构清晰，只有最核心的代码， 能适应于绝大部分开发，也能很方便的进行二次开发

    3.开源：授权自由，可用于商业目的

    4.拓展：引擎可以很方便的过行拓展,甚至可以在不改变原有代码的基础上，很自由的支持解析各种自定义格式的标签

Copyright 2003-2012 Your company name(Edit the text to change this template.)


简明教程

New Help > 简明教程
简明教程

一、获取JNTemplate

极念模板引擎(JNTemplate engine)可以直接在我们的开源主页中下载得到源代码或者已经发行的DLL

或者在VS2010及以上版本中通过NuGet下载

二、JNtemplate配置

    如果是通过NuGet下载的文件，那么所有的都已经配置好了，我们可以直接使用

    如果是自行下载的文件（源代码需要生成），解压后，请将JinianNet.JNTemplate.dll引入到项目中来（4.0下以版本请使用2.0文件夹中的JinianNet.JNTemplate.dll，否则使用4.0文件夹中的）

三、Hello JNTemplate

    新建一个aspx页面，在Page_Load中写入如下代码：

JinianNet.JNTemplate.Template template = new JinianNet.JNTemplate.Template("hello,$name!");
template.Context.TempData["name"] = "JNTemplate";
template.Context.CurrentPath=@"c:\" //当前模板路径
template.Render(Response.Output);
Response.End();

按F5运行，可以看到我们的页面正确输出了 hello JNTemplate

我们也可以从指定文件加载模板

JinianNet.JNTemplate.Template template = JinianNet.JNTemplate.Template.FromFile(@"c:\default.html",System.Text.Encoding.UTF8);

具体的每个标签的用法，请参考“标签参考”一栏

Copyright 2003-2012 Your company name(Edit the text to change this template.)


保留关键词

New Help > 保留关键词
保留关键词

使用JNTemplate时，变量或方法的命名，就避免使用以下关键词：

  1. if
  2. set
  3. load
  4. include
  5. foreach
  6. for
  7. true
  8. false
  9. else
  10. elseif
  11. end
Copyright 2003-2012 Your company name(Edit the text to change this template.)


变量签标

New Help > 变量签标
变量签标
变量签标
标签作用：获取后台设置的对象的值或属性,属性与对象之间用英文“.”连接
书写格式：${标签名}
标签简写：$标签名
注意事项：标签名必须是字母与数字的组合且必须以字母开头（JNTemplate中的大多数命名规则都遵循此规则）
示例代码：
          模板：您的是姓名是：$user.Name 年龄：${user.Age}
          后台：
                UserInfo model = new UserInfo();
                model.Name = "张三";
                model.Age = 45;
                template.Context.TempData["user"]=model;
Copyright 2003-2012 Your company name(Edit the text to change this template.)


赋值标签

New Help > 赋值标签
赋值标签

赋值标签

标签作用：将值赋给某个指定变量，如果该变量已经存在，则改变该变量的值，否则创建一个新变量！该标签也是经常使用的一个标签，用以配合方法，循环等标签使用

书写格式：${set(变量名=值)}

标签简写：$set(变量名=值)

注意事项：

          1.等于后面可以接变量，方法，表达式，或者数字，字符，true与false

          2.字符串必须使用双引号！

          3.如果同一个变量重复赋值，最后面的结果会覆盖之前的结果！

          3.如果是在循环体（具体见循环标签）内创建的变量，则当前循环结速后，该变量不再存在！如果是在循环体内重新赋值，则离开循环体后将丢失更改！如：

          $set(id = 11.5)

          $id //结果是11.5

          $foreach(node in list)

              $set(id = 88/2)

              $id //值被改变，结果是44

          $end

          $id //结果是11.5

示例代码：

          模板：$set(id = getQuery("id")) //将getQuery方法返回的结果赋给id

                $set(a= 1) //将数字赋给变量a

                $set(a= true)//将布尔值(true/false)赋给变量a

                $set(a= "字符串") //将字符串赋给变量a

                $set(a= user.Name) //将变量usr的属性Name赋给变量a

                $set(a= 3+5) 将表达式3+5的结果赋给变量a,注意 表达式支持复杂的加减乘除四则运算与逻辑判断，比如 $set(a= (20+20)/40)

Copyright 2003-2012 Your company name(Edit the text to change this template.)


循环签标

New Help > 循环签标
循环签标
循环签标
标签作用：访问集合里的所有对象
书写格式：$foreach(迭代对象名称 in 集合名称)
          循环体
          $end
注意事项：
           1.集合名称只能是一个已经在模板引擎后台或前台定义(set)的集合（变量）的名称！
           2.循环标签可以无限嵌套，使用时应避免死循环
           3.在循环体内使用赋值标签进行赋值的变量，只能在循环体内访问!
示例代码：
          模板：
                <ul>
                $foreach(model in list)
                    <li>
                        <dl>
                            <dt>$model.Title</dt>
                            $set(item=getChild(model.ID)) //创建一个新对象item，该对象在循环结束后也将不存在,getChild 是一个访法标签，调用后台方法获取一个子对象列表
                            $foreach(node in item) //嵌套循环 注意：不能直接这么写foreach(node in getChild(model.ID))
                            <dd><a href="news.aspx?id=$node.ID">$node.Title</dd>
                            $end
                        </dl>
                    </li>
                $end
                </ul>

        后台代码略

Copyright 2003-2012 Your company name(Edit the text to change this template.)


判断标签

New Help > 判断标签
判断标签

判断标签

标签作用：进行逻辑判断

书写格式：

          ${if(表达式)}

              代码一

          ${else}

              代码二

          ${end}

标签简写：

          $if(表达式)

              代码一

          $else

              代码二

           $end

注意事项：

          1.表达式可以直接为true或者false

          2.支持以下逻辑运算符：||(逻辑或),&&(逻辑与)，>(大于),<（小于）,>=（大于等于）,<=（小于等于）,!=（不等于）

          3.目前暂时不支持非(!)运算

          4.暂时不支持else if

          5.四则运算符优先级大于逻辑运算符， 如果表达式比较复杂，应合理利用括号

          6.else 部分没有内容可以省略

示例代码：

          模板：$if((3+15)/2>=80) || (8*27-(55*2)!=21) || (5>4))//支持复杂的表达

                    条件成立

                $else

                    条件不成立

                $end

Copyright 2003-2012 Your company name(Edit the text to change this template.)


方法签标

New Help > 方法签标
方法签标

方法签标

标签作用：执行模板后台定义的方法

书写格式：${方法名(参数1，参数2，参数3,...)}

标签简写：$方法名(参数1，参数2，参数3,...)

注意事项：

          1.多个参数使用英文逗号“,”分隔！

          2.参数如果是字符串，必须使用双引号！

          3.变量无须加$，直接使用变量名即可！

          4.后台定义支持委托与反射两种方法(见示例)

          5.方法参数里面应避免嵌套使用方法，正确用法应该配合赋值标签(set)使用

          6.方法名称避免以下关键字：if,set,load,includeTag,foreach,for,true,false

示例代码：

          模板：$set(id = getQuery("id"))

                $cmd.ExcuteTable("myTable","id="+id);

          后台：

                template.Context.TempData["getQuery"]= new JinianNet.JNTemplate.FuncHandler(delegate(object[] args) //委托，获取当前页面的GET参数

                {

                    if (args == null || args.Length != 1)

                         throw new Exception("agrs");

                    return Request.QueryString[args[0].ToString()];

                });

                template.Context.TempData["cmd"] = new Command();//反射

          Command类：

                using System;

                using System.Collections;

                using System.Collections.Generic;

                using System.Data;

                using System.Linq;

                using System.Web;

                namespace JinianNet.DemoSite

                {

                    public class Command

                    {

                        public string ExcuteTable(string tableName,string where)

                        {

                            return DbHelper.ExecuteTable("select * from "+tableName+" where " + wher));//DbHelper为自定义的数据库操作类

                        }

                    }

                }

Copyright 2003-2012 Your company name(Edit the text to change this template.)


引用标签

New Help > 引用标签
引用标签

引用标签

标签作用：将指定文件内容加载到当前模板，类似于asp中的include或者asp.net的ascx

书写格式：${load("文件名")} 或 ${include("文件名")}

标签简写：$load("文件名") 或 $include("文件名")

注意事项：

          1.引用标签有2种写法，即load与include,前者会将文件内容解析后加载进来，后者只会简单的将文件加载进来而不会进行任何处理

          2.基于安全原因，目前引用文件必须与模板文件在同一个目录

          3.文件名不能带路径，即不支持$load("../files/template.html"),只能为$load("template.html"),原因同上

示例代码：

          模板：$load("template.html")

                $include("template.html")

Copyright 2003-2012 Your company name(Edit the text to change this template.)


4.编辑器

编辑器的使用

控件在Views/Default/Common/Public目录下.
1.使用之前请拖KindEditorLibrary.ascx到aspx页面中的head标签中.
2.然后在textarea中加入属性data-type="kindEditor"即可:
例:

        <textarea data-type="kindeditor"></textarea>

效果如下:


5.上传控件

上传按钮的使用

控件在Views/Default/Common/Public目录下.
1.使用之前请拖KindEditorLibrary.ascx到aspx页面中的head标签中.(如果是旧版需要去掉控件中的js注释)
2.然后在textarea中加入属性data-type="upload-img"和data-img属性,
当属性值与html中imgId值相同时,img标签则会自动获取上传的图片路径,并显示上传后的图片
例:

        <img id="img" />

        <input type="button" data-type="upload-img" data-img="img"/>

3.也可加入data-input属性,当属性值与html中input的Id值相同时,input的value值就是图片的路径值.
例:

        <img id="img1" />

        <input id="imgsrc" type="hidden" />

        <input type="button" data-type="upload-img" data-img="img" data-input="imgsrc"/>
如果提示指定目录不存在，则需要在【模块】/Content/kindeditor/下新建attached文件夹


6.分页控件

分页控件

必须确保在控制器中使用了Page函数,且仅使用了一次.

===============================================================================
使用方法:
1.用鼠标拖Views/Default/Common/Public/Navigator.ascx控件到aspx窗体中.对其进行数据绑定
<uc1:Navigator runat="server" db="<%ViewBag.page%>" />
2.在使用分页控件所对应的Controller中要声明Page对象，如：

            NFinal.Page page = new NFinal.Page(p, 2);


7.用户控件

自定义控件

定义控件

以Label控件为例：
1、首先在Views/Default/Common/Public文件夹下添加Label.cs文件，其中代码如下：
                    //a.control的实体类必须继承NFinal.UserControl类
                    public class Label:NFinal.UserControl
                    {
                        //b.因定格式,其中的泛型类必须为此控件类
                        public __Render__<Navigator> __render__ = null;

                        #region c.自定义变量
                        public string text;
                        #endregion
                    }
2、新建Label.ascx文件，其中可以定义要使用的控件的模板内容
                    <%=ViewBag.text%>

控件的使用

1、可以在要使用该模板的Controller.cs中声明成员。
        public void Index()
        {
            //声明控件实体对象
            Label label1=new Label();
            lable1.text="我来自控件";
            View("Index.aspx");
        }

2.用鼠标拖Label.ascx控件模版到Index.aspx窗体中，增加db属性,此属性绑定到控件变量上

            <div>
                <uc1:Label runat="server" id="label1" db="<%#ViewBag.label1%>" />
            </div>


1.如何手动发布

如何手动发布到.net 4.0下

1.传统发布：在VS下编译网站项目。
然后把bin目录和模块/Content/目录复制出来，网站即发布成功。
2.vNext发布：把App_Start/Startup.cs排除到项目外并重新编译网站项目。
然后把bin目录和模块/Content/目录复制出来，网站即发布成功。
vNext引擎只有在IIS7.5以上版本才能使用,其管道速度是传统Asp.Net及.net MVC的3-7倍。


1.NFinal执行原理

我带大家一起走一下NFinal的执行流程.
Web.Config中添加了NFinal.Module模块

        <add name="NFinalMoudle" type="NFinal.Module" />
在启动IIS时，IIS会调用模块下的Init函数进行模块注册，
NFinal.Module类位于NFinal目录中，其部分代码如下
        //模块初始化函数
        public virtual void Init(HttpApplication app)
        {
            //注册事件，当发生用户验证时执行函数
            app.AuthorizeRequest += new EventHandler(this.BaseModuleRewriter_AuthorizeRequest);
        }
        //重写URL路径,把所有NFinal相关的URL全重写为.nf后缀的url
        protected virtual void BaseModuleRewriter_AuthorizeRequest(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;
            // Remove the "Server" HTTP Header from response
            if (null != app && null != app.Request &&
                null != app.Context && null != app.Context.Response)
            {
                NameValueCollection headers = app.Context.Response.Headers;
                if (null != headers)
                {
                    headers.Remove("Server");
                }
            }
            Rewrite(app.Request.Path, app);
        }

当HTTP发送http://localhost/App/Index.htm请求时,IIS会按顺序执行所有的模块所注册的事件。
当执行到AuthorizeRequest事件时，也就是NFinal.Module中的BaseModuleRewriter_AuthorizeRequest函数
首先函数删除了HTTP头 Server:ASP.NET 4.0，删除一是为了安全考虑，二是节省流量。
然后调用了Rewrite函数进行Url重写。

        protected void Rewrite(string requestedPath, HttpApplication app)
        {
            string domainName = app.Context.Request.Url.Host;
            //执行自定义重写函数
            if (!RewriteUrl.Rewrite(requestedPath,app.Context))
            {
                //重写虚拟目录
                //重写虚拟路径,也可以把此部分放到find中
                switch (requestedPath)
                {
                    //网站图标
                    case "/favicon.ico": return;
                    case "/App/Index.html":requestedPath = "/App/IndexController/Index.html"; break;
                    default: break;
                }
                //获取actionUrl
                string actionUrl = GetActionUrl(requestedPath);
                int find = FindAction(domainName, actionUrl, requestedPath);
                if (find > 0)
                {
                    //重写url使其能够让NFinal.Handler进行处理
                    app.Context.RewritePath(string.Format("{0}{1}{2}", requestedPath, find.ToString("00000"), ".nf"));
                }
            }
        }

请求的/App/Index.html会被重写为/App/IndexController/Index.html
GetActionUrl用于查找/App/IndexController/Index.html是否属于网站下的Url.如果是，就返回一个代表该路径的数字.
(在NFinal框架下所有的控制器下的Action都会有一个URL对应，NFinal会给这个Action一个数字编号，返回的数字指的就是该值。)
随后调用.net的RewritePath进行重写，重写后的URL会在结尾加上一个五位数字和.nf后缀。

当执行完以上模块后，IIS会查看Web.Config中的配置.重写后的URL正好符合*.nf这个过滤条件

            <add verb="*" path="*.nf" type="NFinal.Handler"/>

于是IIS会找到NFinal.Handler这个类,
并调用其中的GetHandler返回Handler对象,然后再调用Handler对象的BeginProcess方法.

    public class Handler : IHttpAsyncHandler
    {
        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
        {
            //初始化配置
            NFinal.ConfigurationManager.Load(context.Server.MapPath("/"));
            AsynchOperation asynch = new AsynchOperation(cb, context, extraData);
            asynch.StartAsyncWork();
            return asynch;
        }
    }

其中AsynchOperation是异步执行类，其中的StartAsyncWork代码如下：

    private void StartAsyncTask(Object workItemState)
    {
        try
        {
            WebMvc.Router.Run(_context, _context.Request.Path);
        }
        catch (Exception e)
        {
            _context.Response.Write("错误消息：<br />");
            _context.Response.Write(e.Message);
            _context.Response.Write("<br />");
            _context.Response.Write("请求时发生错误：<br>");
            _context.Response.Write(_context.Request.FilePath);
            _context.Response.Write("<br />");
            if (_context.Request.Cookies.Count > 0)
            {
                _context.Response.Write("请求时的Cookie:<br>");
                for (int i = 0; i < _context.Request.Cookies.Count; i++)
                {
                    _context.Response.Write(_context.Request.Cookies[i].Name);
                    _context.Response.Write(":");
                    _context.Response.Write(_context.Request.Cookies[i].Values);
                    _context.Response.Write("<br />");
                }
            }
            _context.Response.Write("错误跟踪：</br>");
            string[] stackTraces = e.StackTrace.Split('\n');
            for (int i = 0; i < stackTraces.Length; i++)
            {
                _context.Response.Write(stackTraces[i]);
                _context.Response.Write("</br>");
            }
            _context.Response.End();
        }
        _completed = true;
        _callback(this);
    }

其代码中只有一句WebMvc.Router.Run(_context, _context.Request.Path);用于执行路由。 而catch内部的代码用于在页面输出详细的错误信息，这样调试时会方便一些。 路由用于根据不同的URL执行不同控制器下的函数。其代码如下：

    public static void Run(HttpContext context, string requestedPath)
    {
        //取出action标识
        int find = 0;
        string f = requestedPath.Substring(requestedPath.Length - 8, 5);
        int.TryParse(requestedPath.Substring(requestedPath.Length - 8, 5), out find);
        //去掉00000.htm后缀,还原requestPath
        requestedPath = requestedPath.Substring(0, requestedPath.Length - 8);
        string[] hostName = context.Request.Url.Host.ToString().Split('.');
        string subdomain = hostName.Length == 3 ? hostName[0] : "www";
        string[] parameters = null;
        NameValueCollection get = new NameValueCollection();
        //提取form参数
        if (context.Request.Form.Count > 0)
        {
            for (int i = 0; i < context.Request.Form.Count; i++)
            {
                get.Add(context.Request.Form.Keys[i], context.Request.Form[i]);
            }
        }
        //提取URL?后的参数
        if (context.Request.QueryString.Count > 0)
        {
            for (int i = 0; i < context.Request.QueryString.Count; i++)
            {
                get.Add(context.Request.QueryString.Keys[i], context.Request.QueryString[i]);
            }
        }
        //提取MethodType
        NFinal.MethodType methodType = NFinal.MethodType.NONE;
        switch (context.Request.HttpMethod)
        {
            case "POST":methodType = NFinal.MethodType.POST; break;
            case "GET":methodType = NFinal.MethodType.GET; break;
            case "DELETE":methodType = NFinal.MethodType.DELETE; break;
            case "PUT":methodType = NFinal.MethodType.PUT; break;
            case "AJAX":methodType = NFinal.MethodType.AJAX; break;
            default:methodType = NFinal.MethodType.NONE; break;
        }
        //获取actionUrl,用于获取参数
        string actionUrl = GetActionUrl(requestedPath);
        switch (find)
        {
                case 1:
                {
                    context.Response.ContentType = "text/html";
                    Regex regParameters = new Regex("/App/IndexController/Index.html");
                    Match matParameters = regParameters.Match(requestedPath);
                    App.Web.Default.IndexController.IndexAction control= new App.Web.Default.IndexController.IndexAction(context);
                    control._methodType = methodType;
                    control._context = context;
                    control._subdomain = subdomain;
                    control._url=requestedPath;
                    control._get = get;
                    control._app="/App";
                    control.Before();
                    control.Index();
                    control.After();
                    control.Close();
                    context.Response.End();
                }break;
            default: break;
        }
    }

解析URL时，先支掉.nf后缀，再提取出后5位数字，然后根据数字去执行Web层下不同的html生成类。 而生成类IndexAction的代码如下：

    public class IndexAction  : Controller
    {
        public IndexAction(System.IO.TextWriter tw):base(tw){}
        public IndexAction(string fileName) : base(fileName) {}
        public IndexAction(HttpContext context):base(context){}
        public void Index()
        {

            Write("<!doctype html><html><head><title>nfinal框架</title><script src=\"/App/Content/js/jquery-1.11.2.min.js\"></script><link href=\"/App/Content/css/frame.css\" type=\"text/css\" rel=\"stylesheet\" /><style type=\"text/css\">.auto-style1 {width: 140px;}.auto-style2 {width: 197px;}</style></head><body>");
            Write("<header class=\"header\"> NFinal框架:这是头,来自模版 </header><article> Hello,NFinal! </article>");
            Write("<footer> NFinal框架.这是尾 </footer></body></html>");
        }
    }


2.NFinal生成原理

NFinal生成原理.

1.数据库生成方面:
在开发时,我们的数据库的类型是已知的.
因此,程序可以从SQL语句中判断出数据库类型,
然后再根据数据库类型判断出对应的csharp类型,最终生成类手工的代码.

2.NFinal.Module生成方面:
在开发时,所有的重写URL都是已知的.
因此,程序可以从所有的Attribute重写与config.json中提取出所有的URL重写，并生成相应的Module

3.NFinal.Handler生成方面:
在开发时,所有的URL都是已知的.
因此,程序可以从所有的Action与AttributeUrl中提取出所有的URL，并生成相应的handler类

2.Router生成方面
在开发时,控制器都是放在Controllers目录下.
因此通过分析类中的方法,就可以逆向生成相对应的Router执行代码.

3.代码的生成
代码生成利用的是.net开源的JN模版引擎,此引擎是纯开源协议.

4.为什么一定要生成？
以上生成，一是为了灵活，二是为了执行效率的考虑。
这就是为什么我们的框架的ORM最简单，而执行速度比任何其它php，java,.net框架都快的原因。


联系作者

QQ群:459156828
邮箱:xiaolu-6t-6t@163.com
官网:http://www.nfinal.com
源码:http://git.oschina.net/LucasDot/NFinal