zoukankan      html  css  js  c++  java
  • .Net MVC个人笔记

    目录

    前言

    写了一个.Net MVC的个人笔记,但是不是MarkDown,好难受,博客园也不支持以前的旧文章转MarkDown,以后有时间看看能不能整理一下,这次新开一个MarkDown的

    快捷键

    F9 打断点
    ctrl + k + f 格式化代码
    ctrl + k + s 外侧代码,例如enum,region
    ctrl -
    ctrl + shift + -
    ctrl + tab 鸟瞰
    

    泛型约束

    母版页节点

    在母版页的footer里面写

    <footer>    
         @RenderSection("Footer")
    </footer>
    

    然后在子页中写

    @section Footer{
        <h3>大家好,我是脚</h3>
    }
    

    这样就可以把子页的内容放到母版页的节点位置了

    Controller将数据传给View的方式

    ViewBag和ViewData共享数据

     public ActionResult Index () {
         ViewBag.UserName = "小李飞刀";
         ViewData["UserName"] = "陆小凤";
         TempData["UserName"] = "楚留香"; //临时数据
    
         User model = new User { UserName = "谢晓峰" };
    
         return View (model); //这行代码其实就相当于ViewData.Model=model
     }
    

    View代码

    @{
        ViewBag.Title = "Index";
    }
    
    <div>@ViewBag.UserName </div>
    <div>@ViewData["UserName"] </div>
    <div>@TempData["UserName"] </div>
    <div>@Model.UserName</div>
    

    结果是:

    陆小凤

    陆小凤

    楚留香

    谢晓峰

    原因是ViewData和ViewBag本质上都是【ViewDataDictionary】类型,并且两者之间的数据共享,只不过提供了不同的语法操作方式而已。所以“陆小凤”覆盖了原先的值”小李飞刀“。

    TempData使用一次之后会立即删除数据

    至于Model,在视图里面可以直接使用@Model.username进行使用,但是你会发现没有提示,这个是因为编译器无法在编译的时候获取Model的类型,如果想要提示可以这样做,在视图的上面写上@model是user类型的,注意必须是@model小写的,不能是@Model

    @model    User
    

    Visual Studio不显示添加的文件

    有的时候我在项目的文件夹里加了一些文件,但是Visual Studio不显示,明明已经加了,就是不显示,这时候可以点击显示所有文件按钮,如下图

    点击了这个按钮,所有的文件都会显示了

    Ajax

    Ajax在.net MVC中用的非常多,一般是用来从后端获取数据,然后无刷新加载,Ajax的特点就是无刷新加载

    普通的Ajax使用方法

    基本上使用Ajax都是使用Jquery的Ajax,所以Jquery的js文件引用一下,最好放在母版页里面,这样所有的子页直接开写

    获取文本内容加载

    前端,Razor

    @{
        ViewBag.Title = "Index";
    }
    
    <h2>我是学习ajax的页面</h2>
    
    <div>
        <p id="text">我是字段</p>
        <button id="ctbtn">传统ajax</button>
    </div>
    
    <script>
        $(function () {
            $('#ctbtn').click(function () {
                $.post('/Ajax/GetData', { id: 666 }, function (data, status) {
                    $('#text').html(data + '  状态是:' + status);
                });
            });
        });
    </script>
    

    后端:

        public ActionResult GetData(int id) {
                return Json("普通的Ajax"+id,JsonRequestBehavior.AllowGet);
            }
    

    其实前端的ajax有三种,load,get和post,也可以写成

    <script>
        $(function () {
            $('#ctbtn').click(function () {
                $.get('/Ajax/GetData', { id: 666 }, function (data, status) {
                    $('#text').html(data + '  状态是:' + status);
                });
            });
        });
    </script>
    

    获取Model数组类型加载

    新建一个Model,我新建的是User,如下

        public class User
        {
            public string Name { get; set; }
            public string Sex { get; set; }
            public int Phone { get; set; }
        }
    

    然后前端Razor如此:

    <script>
        $(function () {
            $('#ctbtn').click(function () {
                $.post('/Ajax/GetData', { id: 666 }, function (data, status) {
    
                    $.each(data, function (key, value) {
                        //console.log(data[key].Name); 不需要再使用data了,可以直接使用value,这个value就是一个User对象,后面的属性记得保持一致,没有提醒
                         console.log(value.Name); 
                    });
    
                });
            });
        });
    </script>
    

    后端返回一个Json就可以了

     public ActionResult GetData(int id) {
    
                List<User> userList = new List<User>()
                {
                    new User(){ Name="蜀云泉",Sex="男",Phone=123 },
                    new User(){ Name="许嵩",Sex="男",Phone=123 },
                    new User(){ Name="林俊杰",Sex="男",Phone=123 }
                };
    
                return Json( userList );
            }
    

    看看效果图

    非入侵式Ajax(Unobtrusive Ajax)

    这个简单的介绍一下,什么是非入侵式,就是前端页面只有纯粹的HTML和CSS,HTML元素里面没有一丁点的JavaScript,比如onclick方法之类的,所有的JavaScript都是单独的一个文件,这就是非入侵式,但是,根据我目前的水平来看,根据我目前接触的项目来看,JavaScript都是写在Razor里面的,并没有做到非入侵式,所以简单的介绍一下

    打开.Net MVC的web.config文件,你可以发现如下

    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    

    这两行就是开启客户端验证和非入侵式JavaScript的

    如果只想在指定页面关闭非入侵式,可以在页面写

    @{ Html.EnableUnobtrusiveJavaScript(false); }
    @{ Html.EnableClientValidation(false); }
    

    讲真这个非入侵式开启关闭什么的,我是没有看懂的,有什么用?

    AjaxHelper

    这个是封装的,分为异步连接按钮和异步表单,知道了就行了,感觉用的不多,不学

    MVC Areas 区域

    为什么会有Areas?因为多人协作,比如一个网站,我做购物车,你做商品管理,他做权限验证,如果使用普通的都在Controller文件夹里面新建Controller,在View文件夹里新建View,你会发现,好乱啊

    Areas出现了,我做购物车,使用购物车Areas,你做商品管理,使用商品管理Areas

    当然,首先每个部分都得有一定的复杂度,要是很简单的几个Controller就完成的模块,就不要分Areas了

    新建Areas

    新建一个AdminAreas,新建完成之后如下

    点开AdminAreaRegistration看看,很明显里面的路由变成了Controller前面加了Admin

    在Admin这个区域里面的Controller新建index,运行,都是一样的

    不知道发现了没,看看上图,除了路由的文件多加了一个Admin,其他的文件夹很明显的三层,Controller,Model,View,所以Areas其实就相当于新建了一个文件夹而已,都是在项目内

    把Areas转移到类库

    先说一下,转移到类库有什么好处.首先,还是我上面说的,具备一定复杂度的模块才会新建一个Areas,两三个Controller就可以实现的模块没必要新建Areas

    那么问题来了,多人协作的时候,我在Admin的Areas里面新建东西,写代码,测试,我一提交,那么StudyMVC这个项目的人就得获取我的代码,就得编译,万一我的AdminAreas有bug,他必须等我修复bug

    新建一个类库就不一样了,我StudyMVC引用了你这个类库,你类库有问题不影响我的主项目,你编译你自己的,我编译我自己的

    新建.Net FrameWork类库

    我第一次没看清,新建的是.Net Standard,坑死了啊,选Net FrameWork类库

    然后把StudyMVC里面的AdminAreas里面的AdminAreaRegistration.cs复制到外面类库AdminAreas里面,复制之后,会报错,这是因为必须引用两个文件

    第一个using System.Web.Mvc,使用Nuget引用本地的就行

    第二个using System.Web,这个就更简单了,直接在类库的引用上面右键添加引用就可以了

    AdminAreaRegistration.cs文件复制到类库之后,原项目里面的就删了,原项目StudyMVC右键添加服务引用,引用AdminAreas这个类库

    然后再来运行一下,输入Admin这个区域加上原项目的Controller,发现运行一样是ok的

    如果你没有运行成功,请再次检查你的MVC项目有没有引用区域类库

    重点来了

    我现在在AdminAreas类库里面新建一个文件夹,叫Controller,然后新建一个Controller叫UserController,可能你无法新建控制器,可以从StudyMVC项目里面复制一个控制器过来,改改名字就好了

    UserController里面新建一个Index方法,然后在MVC项目的Admin区域里面的View里面新建对应的User文件夹,下面新建Index.cshtml

    给你们看看代码吧

    首先外部AdminAreas类库的UserController

    using System.Web.Mvc;
    
    namespace StudyMVC.Areas.Admin.Controllers
    {
        public class UserController : Controller
        {
            // GET: Admin/AreaTest
            public ActionResult Index()
            {
                ViewBag.user = "我是区域类库里面的UserController";
                return View();
            }
        }
    }
    

    然后,我是MVC项目里面的index.cshtml

    @{
        Layout = null;
    }
    
    <h2>什么东西</h2>
    <h2>@ViewBag.user</h2>
    

    最后,结果大图

    我讲一下,这个区域啊,放到外部的类库之后,就好多了.我的Admin区域的Controller在类库里,随便过来一个程序员,你改吧,只要符合我的前端cshtml的要求,返回值不要动,其他的逻辑代码你随便改

    而且,改完之后,你自己在类库里面编译,编译成功后把dll丢给我的MVC项目就可以了

    看到这里,可能有人会有疑问,为什么Controller丢在外面的类库,视图cshtml还在MVC 内部呢?

    因为Controller是代码需要编译.....

    视图cshtml不需要编译......

    而且视图一旦写好了,不会经常修改的,反而是后台,会需要修修改改,所以我只需要定好我这个前端cshtml需要的返回值,你类库那边的Controller怎么写我不关心,只要返回值给对就行

    我终于学会了Areas了

    对象加问号判断是否为空

    代码如下:

    User user = null;
    if (user?.Name=="蜀云泉")
    {
        Console.WriteLine("测试");
    }
    

    如果我不加?的话,因为user对象是null,所以我调用user.Name的时候会直接报错

    对象后接一个?就是判断是否为空的意思,如果是空的话就不会执行判断了,很好用

    过滤器

    过滤器一般用来做身份验证,比如购物电商网站,你在购买的时候会检测你是否登录,如果没登录就让你登录,还有其他很多地方需要身份验证的,如果你每个地方都写一次身份验证的代码,那就违背DRY原则了,代码重复了

    所以,有了过滤器,这个相当于AOC切面编程

    三种过滤器

    • ActionFilterAttribute:方法执行之前后和结果执行之前后
    • AuthorizeAttribute:首先执行,在所有方法或过滤器之前
    • HandleErrorAttribute:异常过滤器

    我不会详细介绍,网上大把,我简单的说一下怎么用

    新建过滤器

    一般都是在Filter文件夹下面新建,我的

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    namespace StudyMVC.Filter
    {
        /// <summary>
        /// 这是我写的第一个过滤器,过滤器通常用在身份验证吧我感觉,F12进去看看ActionFilterAttribute就知道了
        /// </summary>
        public class MyCustomerFilter : ActionFilterAttribute
        {
            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                filterContext.HttpContext.Response.Write("方法执行之前");
                base.OnActionExecuting(filterContext);
            }
    
            public override void OnActionExecuted(ActionExecutedContext filterContext)
            {
                filterContext.HttpContext.Response.Write("方法执行之后");
                base.OnActionExecuted(filterContext);
            }
    
            public override void OnResultExecuting(ResultExecutingContext filterContext)
            {
                filterContext.HttpContext.Response.Write("视图加载之前");
                base.OnResultExecuting(filterContext);
            }
    
            public override void OnResultExecuted(ResultExecutedContext filterContext)
            {
                filterContext.HttpContext.Response.Write("视图加载之后");
                base.OnResultExecuted(filterContext);
            }
    
        }
    }
    

    为什么我会这么写?因为你在ActionFilterAttribute上按下F12,查看源代码就知道了,如下

    #region 程序集 System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
    // 未知的位置
    // Decompiled with ICSharpCode.Decompiler 4.0.0.4285
    #endregion
    
    namespace System.Web.Mvc
    {
        //
        // 摘要:
        //     Represents the base class for filter attributes.
        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
        public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
        {
            //
            // 摘要:
            //     Called by the ASP.NET MVC framework before the action method executes.
            //
            // 参数:
            //   filterContext:
            //     The filter context.
            public virtual void OnActionExecuting(ActionExecutingContext filterContext)
            {
            }
    
            //
            // 摘要:
            //     Called by the ASP.NET MVC framework after the action method executes.
            //
            // 参数:
            //   filterContext:
            //     The filter context.
            public virtual void OnActionExecuted(ActionExecutedContext filterContext)
            {
            }
    
            //
            // 摘要:
            //     Called by the ASP.NET MVC framework before the action result executes.
            //
            // 参数:
            //   filterContext:
            //     The filter context.
            public virtual void OnResultExecuting(ResultExecutingContext filterContext)
            {
            }
    
            //
            // 摘要:
            //     Called by the ASP.NET MVC framework after the action result executes.
            //
            // 参数:
            //   filterContext:
            //     The filter context.
            public virtual void OnResultExecuted(ResultExecutedContext filterContext)
            {
            }
        }
    }
    
    

    里面说的很明白了,方法执行前后,和结果执行前后

    使用过滤器

    因为过滤器是AOC面向切面编程的产物,所以使用很方便,有三种方式,不过最常用的还是在方法上加,其他两个一个是在Controller上加,一个是在全局过滤器加,不讲。

     [MyCustomerFilter]
            public ActionResult Index()
            {
                string test = string.Format("{0:P0}", 0.24583);
    
                return View(); 
            } 
    
    

    就是这么简单,Index这个方法已经加上过滤器了,

    身份验证过滤器

    我们的网站需要登录才能访问一些数据,这个时候就需要身份过滤器

    一般我们在项目下新建一个Filter文件夹,然后新建一个类,我命名UserAuthorize,如下

        public class UserAuthorize : AuthorizeAttribute
        {
            public override void OnAuthorization(AuthorizationContext filterContext)
            {
                var user = filterContext.HttpContext.Session["CurrentUser"];
                if (filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
                //判断是否Action判断是否跳过授权过滤器
                {
                    return;
                }
                else if (filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true))
                //判断是否Controller判断是否跳过授权过滤器
                {
                    return;
                }
                else if (user == null || string.IsNullOrWhiteSpace(user.ToString()))
                //判断用户是否登录
                {
                    filterContext.Result = new RedirectResult("/Account/Login");
                }
                else
                {
                    return;
                }
            }
        }
    

    全局和单个控制器使用

    怎么使用呢?

    可以全局注册,在FilterConfig的RegisterGlobalFilters方法里面加上

    filters.Add(new UserAuthorize());
    

    这样就是全局的身份验证过滤了,你启动网站,访问任意一个页面,都得身份验证

    也可以写在控制器上

        [UserAuthorize]
        public class HomeController : Controller
        {
    

    这样就是对这个控制器下的Action起效

    一般来说,我的网站希望个别页面需要身份验证,比如购物车,个人账户信息,等等,大部分内容页面还是可以免登陆看的

    登录Session存储账户

            [HttpPost]
            public ActionResult Login(LoginViewModel loginViewModel)
            {
                //验证成功
                if (ModelState.IsValid)
                {
                    Session["CurrentUser"] = loginViewModel.Email;
    
                    return RedirectToAction("Index","Home");
                }
    

    个人账户控制器需要忽略验证

    注意,如果不在账号控制器写忽略验证的话,启动网站会陷入死循环

    当前无法使用此页面 localhost 重定向次数过多

    这个时候,我们需要给账号控制器加上忽略验证,如下

        [AllowAnonymous]
        public class AccountController : Controller
        {
    

    缓存

    这里讲一下OutputCache这个缓存

    我在Index方法上加上OutputCache缓存,如下

            [OutputCache(Duration =5)]
            public ActionResult Index()
            {
                ViewBag.time = DateTime.Now.ToString();
                Response.Cache.SetOmitVaryStar(true);
                return View();
            }
    

    Duration这个属性是时间,就是缓存几秒的意思,属性有很多,不写,去网上查

    运行,发现,网络是200,然后我刷新一次,发现是304,如下图

    这个304就是从缓存读取的意思,因为我设置了Duration缓存时间为5秒,所以,你在5秒内刷新都是304,过了5秒就又变成200了

    SEO

    SEO,搜索引擎优化。做网站最看重的就是SEO了吧,这关乎到你的网站在搜索引擎中的权重,SEO做的好,就会出现在搜索引擎的前几名,对网站的流量和知名度影响很大

    开发时要考虑SEO

    蜘蛛只爬取静态HTML

    JavaScript动态生成的HTML,蜘蛛是不会爬取的,蜘蛛就是各大搜索引擎获取网站信息的工具,说到这里我想到了一点,Bootstrap Table这个插件是在js里面生成表格的,这样蜘蛛就不会爬取,SEO会变差

    使用a标签,不要使用LinkButton

    因为搜索引擎蜘蛛只认a标签,不认JavaScript,例如

    <a href="javascript:document.location='www.baidu.com'">百度</a>
    

    这个搜索引擎的蜘蛛是不认的,而LinkButton就是这个

    Lucene.Net分词

    WebService

    WebService是什么暂且不细说,先看看怎么新建最简单的WebService

    新建空的EntityFramework网站

    直接右键新建空的EntityFramework网站,没什么好说的

    新建WebService

    这个新建Web服务即可,如图

    我的命名是MyWebService,双击打开可以看到里面已经有了一个HelloWorld方法

    现在我们来新加一个方法,如下:

     [WebMethod]
     public int Multiplier(int a,int b)
     {
         return a * b;
     }
    

    直接在MyWebService右键,在浏览器中运行,如图


    超经典的图面了,这就是WebService了,你点击Multiplier,还可以直接在线测试方法

    调用WebService

    首先,项目先引用WebService,直接在引用那里,右键添加服务引用,选择我们刚才的WebService地址即可,注意,这里引用的地址不要和WebService重名了

    然后新建一个Razor页面来测试吧,单纯的HTML没办法写C#,还是Razor好用

    @{
        Layout = null;
    }
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title></title>
    </head>
    <body>
        <div>
    
            @{
                WebService.ServiceReference.MyWebServiceSoapClient client = new WebService.ServiceReference.MyWebServiceSoapClient();
                <text>@client.Multiplier(5,6)</text>
            }
    
        </div>
    </body>
    </html>
    
    

    直接右键运行这个Razor,可以直接看到30,至此,最简单的WebService创建和调用完成了

    调用网络上的WebService

    这个有点坑啊,本地的WebService是直接添加服务引用,网络上的不一样,需要点击添加服务引用,点击高级=>添加Web引用才可以,如下图,网络的WebService带有Web

    我们这里以天气服务助手为例,网上搜一下天气服务助手,可以搜到这个

    http://www.webxml.com.cn/WebServices/WeatherWS.asmx
    

    你可以点着看看各个接口是干嘛的,我不介绍了,直接调用,还是在我很喜欢的Razor里面

    @{
        Layout = null;
    }
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title></title>
    </head>
    <body>
        <div>
    
            @{
                WebService.WeatherService.WeatherWS weatherClient = new WebService.WeatherService.WeatherWS();
                string[] text = weatherClient.getWeather("深圳", "");
    
                foreach (var item in text)
                {
                    <text> @item </text>
                }
    
            }
        </div>
    </body>
    </html>
    
    

    不知道发现了没,这个getWeather接口也是坑,明明两个参数,一个是城市名,一个是城市的编号ID,但是输入ID却没有身份验证....所以上面的几个接口都是获取Id的有什么用???我直接输入城市名就获取了.....

    看看结果

    根据下拉框内容更新表格

    下拉框怎么获取枚举的值

    肯定不能写死数据了,所以从枚举获取值最好

    <select class="form-control input-sm" id="adjustStatus">
        <option value="-1">调整状态</option>
        @foreach (var item in Enum.GetValues(typeof(ProductSaleStatus)))
        {
            <option value="@item.GetHashCode()">@item</option>
        }
    </select>
    

    获取表格选择的Id,下拉框的点击事件

    $('#adjustStatus').on("click", function () {
      if ($('#adjustStatus').val() == -1) {
          return
      }
    
      id = [];
      $('input[name="checkbox"]:checked').each(function () {
          if ($(this).prop("checked")) {
              id.push($(this).val());
          }
      })
    
      if (id.length > 0) {
          var ids = id.join(",");
          $.ajax({
              type: "post",
              url: "/ProductSale/AdjustSaleStatus",
              data: { ids: ids, status: $('#adjustStatus').val() },
              success: function (data) {
                  if (data.Status == 1) {
                      layer.alert(data.Message, { title: '提示', icon: 1, time: 10000});
                      # 刷新页面
                      window.location.reload()
                  }
              },
              error: Common.Public.Error
          });
      } else {
          layer.alert("请最少选择一条销售记录", { title: '提示', icon: 2, time: 5000, anim: 6 });
      }
        # 最后,把我的下拉框恢复原样,不然点一次又执行了
      $('#adjustStatus').val(-1)
    });
    

    最好的方法

    还可以使用更好的方法,如下

    
    //这个是HTML
    <td>
    <select class="adjustStatus">
           @foreach (var item in Enum.GetValues(typeof(ChatMessageStatus)))
            {
                  <option value="@item.GetHashCode()" @if ((int)chatMessage.Status == item.GetHashCode()) { <text> selected</text> }>@item</option>
            }
    </select>
    </td>
    
    //这个是js
            $('.table tbody').on('change', 'tr .adjustStatus', function () {
                $.ajax({
                    type: "post",
                    url: "/Messenger/AdjustStatus",
                    data: { id: $(this).parents("td").attr("id"), status: $(this).val() },
                    success: function (data) {
                        if (data.Result == "succeed") {
                            window.location.reload()
                        }
                    },
                });
            });
    
    

    枚举的值怎么显示为中文

    一般来说,枚举的值在数据库中都是一个int类型的字段,里面存储0123这样的数字

    然后枚举是类似这样的,一般都在类上面写了

        public enum People
        {
            许嵩 = 0,
            蜀云泉 = 1
        }
    

    添加一个枚举类型的字段,给个默认值

    public People people { get; set; } = People.许嵩;
    

    前端页面使用的时候,直接调用,因为字段本身就是枚举的,所以显示的时候显示的就是中文

    <td>@chatMessage.people</td>
    

    Bootstrap的table点击下一页js不可以使用

    使用了Bootstrap的一个table插件,发现点击下一页之后,js不起作用了

    例如我下面的表格里面的一个class是people,我直接使用.people不行,下一页js根本不生效,换成下面那种就可以了

    //$('.people').on("click", function () {
    //将上面的换成下面的,就可以了
    
    $('.table tbody').on('click', 'tr .people' , function () {
    

    查看用户是从哪个页面过来的

    if (HttpContext.Current.Request.UrlReferrer != null)
    {
        rurl = HttpContext.Current.Request.UrlReferrer.AbsoluteUri;
    }
    

    插入数据后返回该数据的id

    代码里面写的是bool,我就很奇怪,我还得再写一个返回int类型的方法?

    但是我万万没想到, customer.CustomerId = id;这一行代码就已经返回了id了

      public bool AddCustomer(Customer customer)
            {
                string strSql = @"insert into Customer(company,contact) values (@company,@contact);Select @@Identity";
                DbParameter[] cmdParms = {
    					_helper.CreateInDbParameter("company", DbType.String,customer.Company),
    					_helper.CreateInDbParameter("contact", DbType.String,customer.Contact)};
                        int id = _helper.GetInt(_helper.ExecuteScalar(CommandType.Text, strSql.ToString(), cmdParms));
                customer.CustomerId = id;
                return id > 0;
            }
    

    IIS部署https

    在使用push.js进行通知提醒的时候,使用本地的IIS部署的项目无法测试,因为push.js只能在https这样的请求头下工作,所以新加一个IIS部署为https即可

    选择网站,点击右侧的绑定,出来一个网站绑定,选择https即可,如图

    选择https,输入本机ip,默认443端口,最下面的证书可以选择IIS

    然后访问网址直接输入https://192.168......即可

    使用JSON JavaScriptSerializer 进行序列化或反序列化时出错。字符串的长度超过了为 maxJsonLength属性

    只需要把代码改为以下

    return new JsonResult() {
        Data = new { total, rows },
        MaxJsonLength = int.MaxValue,
        ContentType = "application/json",
        JsonRequestBehavior = JsonRequestBehavior.AllowGet
    };
    

    使用KeyName

    使用KeyName真的是太必要了,有的Name里面是有奇怪的字符的,显示效果太差,所以需要KeyName

            public static string GetKeyName(this string name)
            {
                if (!string.IsNullOrWhiteSpace(name))
                {
                    return name.Trim().Replace("~", "-").Replace("`", "-").Replace("!", "-").Replace("@", "-")
                        .Replace("#", "-").Replace("$", "-").Replace("%", "-").Replace("^", "-").Replace("&", "-").Replace("*", "-")
                        .Replace(" ", "-").Replace("(", "-").Replace(")", "-").Replace("+", "-").Replace("®", "-").Replace("™", "-")
                        .Replace("=", "-").Replace(",", "-").Replace(".", "-").Replace("<", "-").Replace(">", "-").Replace("’", "-").Replace(",", "-").Replace("±", "-").Replace("[", "-").Replace("]", "-")
                        .Replace("?", "-").Replace("/", "-").Replace("\", "-").Replace(";", "-").Replace(":", "-").Replace("–", "-").Replace("ω", "-").Replace("{", "-").Replace("}", "-")
                        .Replace("'", "-").Replace(""", "-").Replace("“", "-").Replace("”", "-").Replace("|", "-").Replace("_", "-").Replace("---", "-").Replace("--", "-").ToLower().Trim();
                }
                return "";
            }
    

    EntryFramework

    EF有三种模式,分别是

    1. DBFirst:数据库优先

    2. ModelFirst:实体类优先

    3. CodeFirst:代码优先

    接下来分别介绍一下这三种模式

    DB First方式

    新建

    数据库优先,那你得先有了数据库,有了业务需要的表,然后新建项,选择数据,实体数据模型,如图

    接下来选择你的数据库,选择完成之后可以勾选哪些表生成Model

    新建一个方法,测试一下EF

    public void DBFirst(){    VaeDBEntities entity = new VaeDBEntities();    Movie movie = new Movie     {        Title = "测试DB",        ReleaseDate = DateTime.Now,        Genre = "测试",        Price = 2    };    entity.Movie.Add(movie);    entity.SaveChanges();}
    

    执行一下即可,数据已经更新到表中了

    entity.Movie.Add(movie);这条语句作用就是构造SQL语句,insert Movie Values ....

    entity.SaveChanges();这句作用就是生成SQL语句然后去数据库执行

    可以看到,EF其实就是我们写的代码转换成了SQL然后执行

    EF的增删改查

    延时加载

    public void DBFirstQuery(){    using (VaeDBEntities entity = new VaeDBEntities())    {        DbQuery dbQuery = entity.Movie.Where(x => x.ID == 1002) as DbQuery;        Movie movie = dbQuery.FirstOrDefault();        Console.WriteLine(movie.Title);    }}
    

    代码如上,我使用的是SQLServer数据库,可以打开SqlServer Profiler,在工具菜单栏下

    然后执行代码在这个方法里面打个断点,你会发现在查询结果dbQuery的时候SqlServer Profiler并没有SQL查询记录,在执行FirstOrDefault方法的时候才出现了执行的SQL语句

    再举个例子,如下

    public void DBFirstQuery2(){    using (VaeDBEntities entity = new VaeDBEntities())    {        IQueryable dbQuery = entity.Movie.Where(x => x.ID == 1002);        Movie movie = dbQuery.FirstOrDefault();        IQueryable movies = entity.Movie;        foreach (var item in movies)        {            Console.WriteLine(item.Title);        }    }}
    

    这个例子中,上面的movie还是和上个例子一样的,不过接受类型换成了IQueryable,在EF中查询的结果都是继承了IQueryable接口的,所以使用IQueryable接收也是ok的

    重点是下面的一个查询集合

    IQueryable movies = entity.Movie;

    这句代码在SqlServer Profiler中没有SQL查询,仅仅是在拼接SQL,没有执行,当我运行到下一句代码的时候

    foreach (var item in movies)
    

    SQL会执行,也就是

    select * from Movie
    

    其后的遍历都不会生成SQL查询了,看看SqlServer Profiler的截图

    EF的增删改查这些方法,例如

    entity.Movie.Add(movie);entity.Movie.Remove(movie);entity.SaveChanges();
    
    

    不管是Add还是Remove方法,都仅仅是标记,标记当前对象为新增状态或者删除状态,在执行SaveChanges方法的时候才是转化为SQL语句去数据库中执行了

    批处理

    public void DBFirstAdd(){    using (VaeDBEntities entity = new VaeDBEntities())    {        Movie movie = new Movie        {            Title = "测试DB",            ReleaseDate = DateTime.Now,            Genre = "测试",            Price = 2        };        entity.Movie.Add(movie);        entity.Movie.Remove(movie);        entity.SaveChanges();    }}
    

    其实就是多次标记,一次SaveChanges

    Linq To EF

    public void LinQ(){    using (VaeDBEntities entity = new VaeDBEntities())    {        var result = from c in entity.Movie select c;        var result2 = from c in entity.Movie where c.ID == 1002 select c;        //Lambda        var result3 = entity.Movie.Where(c => c.ID == 1002);        //排序        IQueryable movies = (from c in entity.Movie orderby c.ID select c).Skip(0).Take(10);        //聚合函数        var max = entity.Movie.Max(x => x.ID);    }}
    

    这些Linq也只是生成了SQL语句,没有执行,使用的时候才会执行SQL,所以也算是延迟加载吧

    Model First

    还是新建项,选择数据>ADO.NET实体数据模型,然后选择空的EF设计模型即可,然后点击edmx文件,自己右键新建Model....

    很明显,Model First适合什么都没有的情况下,没有数据表,没有实体类.我就不演示了,这种做法我不会用的

    Code First

    Code First适合已经存在了实体类,你可以借助于实体类生成数据表,这个有DataAnnotation方式和Fluent API方式,把已有Model生成数据表我也不想看了,不学

    总结EF

    总结一下EF,首先三个模式

    1. DB First :有数据表,生成Model,并使用EF

    2. Model First : 啥都没有,新建Model,生成数据表,使用EF

    3. Code First : 有Model类了,生成数据表,使用EF

    其中,我简单介绍了DB First,其他两个我不想看,在实际开发中,我都是先设计数据表,再进行开发的

    分部视图

    有一些可以复用的视图,可以创建为分部视图,使用方式如下

    1. Partial:在内部将html转换为string字符串,然后缓存输出,降低了一点效率,不如RenderPartial

    2. RenderPartial:不需要Controller里面的Action,适合简单的HTML页面

    3. Action:经过Action方法,但是和Partial一样存在转换,不如RenderAction

    4. RenderAction:执行了Action方法,所以可以从数据库读取数据,可以使用ViewBag

    @{Html.RenderPartial("About");}
    
    @{Html.RenderAction("About");}
    

    如果你的分部视图仅仅是简单的HTML,例如几个表单元素,输入框,按钮什么的,可以使用RenderPartial

    如果分部视图里面需要后端传入数据,不管是ViewBag还是从数据库读取的,可以使用RenderAction

    Action

    Action接收方式有post和get两种方式,如果你想表单传输,不希望有人在url栏目书写参数,那么你可以使用HTTPPost,这样该方法就只可以使用post方式了

    [HttpPost]
    public ActionResult test()
    {.....}
    

    如果Action方法上加上了HttpPost,表明这个方法只能接收post请求了,get请求不通过

    Action返回View的几种方式

    //返回当前Action的View
    return View();
    //返回当前Controller里面的指定Action的View
    return View("Test");
    //返回指定Controller的指定Action的View
    return View("~/Views/Home/Test.cshtml");
    

    HtmlHelper

    为什么要使用HtmlHelper?因为单纯的使用a标签之类的HTML标签,如果改变路由了,单纯的HTML标签就会失效,而HtmlHelper的方式就会自动更新路由

    
    

    表单传到后台的几种方法

    我的前端很简单

    <form role="form" action="/Blog/Add" method="post" enctype="multipart/form-data">
        <div class="form-group">
            <label for="name">标题</label>
            <input type="text" class="form-control" id="Title" name="Title" placeholder="请输入标题">
        </div>
        <div class="form-group">
            <label for="name">内容</label>
            <input type="text" class="form-control" id="Content" name="Content" placeholder="请输入内容">
        </div>
        <div class="form-group">
            <label for="name">摘要</label>
            <input type="text" class="form-control" id="Summary" name="Summary" placeholder="请输入摘要">
        </div>
        <button type="submit" class="btn btn-default">提交</button>
    </form>
    

    方法1,使用FormCollection

            [HttpPost]
            public ActionResult Add(FormCollection form)
            {
                string title = Request.Form["Title"];
                string content = Request.Form["Content"];
                string summary = Request.Form["Summary"];
    
                return View();
            }
    

    方法2,使用TryUpdateModel

    
            [HttpPost]
            public ActionResult Add(int id = 1)
            {
                Blog blog = new Blog();
    
                if (id > 0)
                {
                    //数据库拿出model,我测试一个,懒得从数据库拿
                    blog = new Blog() { Title = "猪", Content = "猪", Summary = "猪" };
                }
                //TryUpdateModel会增量的把表单的更改内容赋值给数据库拿出的model,不要使用AutoMapper了
                TryUpdateModel(blog);
    
                return View();
            }
    

    方法3,使用Model

            [HttpPost]
            public ActionResult Add(Blog blog)
            {
                if (blog.ID > 0)
                {
                    //数据库拿出model,我测试一个,懒得从数据库拿
                    blog = new Blog() { Title = "猪", Content = "猪", Summary = "猪" };
                }
                //TryUpdateModel会增量的把表单的更改内容赋值给数据库拿出的model,不要使用AutoMapper了
                TryUpdateModel(blog);
    
                return View();
            }
    

    Model后端验证和前端Jquery validate表单验证

    我们在使用表单的时候,通常是需要做验证的,验证分为表单的前端验证和后端的Model验证

    为什么需要双重验证?就是为了安全,如果别人使用postman这种工具提交,那不就绕过了表单验证了

    前端的表单验证,使用Jquery validate

    我来个最简单的,一般把jquery和jquery.validate这两个js放到母版页里面去

    <form id="registerForm" action="/Account/Register" method="post" enctype="multipart/form-data">
        <label>用户名: </label><input type="text" id="Name" name="Name" /><br />
        <label>密码: </label><input type="text" id="Password" name="Password" /><br />
        <label>确认密码: </label><input type="text" id="RePassword" name="RePassword" /><br />
        <label>邮箱: </label><input type="text" id="Email" name="Email" /><br />
        <label>备注: </label><input type="text" id="Remark" name="Remark" /><br />
        <button type="submit">注册</button>
    </form>
    <script src="~/Scripts/jquery-3.4.1.min.js"></script>
    <script src="~/Scripts/jquery.validate.min.js"></script>
    <script>
        $(function () {
            $("#registerForm").validate({
                rules: {
                    Name: {
                        required: true,
                        minlength: 2
                    },
                    Password: {
                        required: true,
                        minlength: 5
                    },
                    RePassword: {
                        required: true,
                        minlength: 5,
                        equalTo: "#Password"
                    },
                    Email: {
                        required: true,
                        email: true
                    },
                    Remark: {
                        required: true,
                        minlength: 5
                    }
                },
                messages: {
                    Name: {
                        required: "please input name",
                        minlength: "用户名必需由两个字符组成"
                    },
                    Password: {
                        required: "请输入密码",
                        minlength: "密码长度不能小于 5 个字符"
                    },
                    RePassword: {
                        required: "请输入密码",
                        minlength: "密码长度不能小于 5 个字符",
                        equalTo: "两次密码输入不一致"
                    },
                    Email: "请输入一个正确的邮箱",
                    Remark: "备注最少5个字符"
                }
            })
        });
    </script>
    

    前端表单也可以使用HtmlHelper

    @using (Html.BeginForm("Register", "Account", FormMethod.Post,new { id = "registerForm" }))
    {
        @Html.TextBox("Name")
        @Html.Password("Password")
    	...省略....
    }
    

    后端Model验证

    很简单,自己写一个就可以,然后邮箱的验证自己写了一个EmailAttribute

     public class EmailAttribute : RegularExpressionAttribute
        {
            public EmailAttribute() : base(@"^w+([-+.]w+)*@w+([-.]w+)*.w+([-.]w+)*$")
            {
                
            }
        }
    
        public class RegisterViewModel
        {
            [Required]
            [DisplayName("用户名")]
            [Remote("IsExitUserName","Account")]
            public string Name { get; set; }
    
            [DisplayName("密码")]
            [PasswordPropertyText]
            public string Password { get; set; }
    
            [DisplayName("确认密码")]
            [PasswordPropertyText]
            [System.Web.Mvc.Compare("Password")]
            public string RePassword { get; set; }
    
            [Email]
            public string Email { get; set; }
    
            [DisplayName("备注")]
            [StringLength(20,MinimumLength =5)]
            public string Remark { get; set; }
    
        }
    

    使用的时候这样

    [HttpPost]
    public ActionResult Register(RegisterViewModel registerViewModel)
    {
        if (ModelState.IsValid)
        {
            //验证成功
            return View();
        }
        else
        {
            //验证失败
            return View();
        }
    }
    

    Web项目怎么在Razor视图里面使用Model或ViewModel

    每一个web项目都有两个web.config,找到Views下面的那个web.config,然后再里面加上你的Model调用即可

    <add namespace="Models" />
    

    加完之后,保存一下,然后点击文件 => 关闭解决方案,然后再打开项目,你会发现Razor里面可以随意使用Model了

    项目中使用log4进行日志记录

    项目不可避免会遇到错误,有日志记录可以快速的定位错误的位置,有时候调试复现问题是很麻烦的,所以来在.net MVC中加一个日志,这里使用log4的net版本

    使用NuGet下载log4net

    NuGet下载log4net

    log4net配置

    直接在项目下新建一个配置文件,叫log4net.config,内容如下

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
      </configSections>
    
      <system.web>
        <compilation debug="true" targetFramework="4.5.2" />
        <httpRuntime targetFramework="4.5.2" />
      </system.web>
    
      <log4net>
        <!--错误日志:::记录错误日志-->
        <!--按日期分割日志文件 一天一个-->
        <!-- appender 定义日志输出方式   将日志以回滚文件的形式写到文件中。-->
        <appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
          <!--保存路径:下面路径项目启动的时候自动在C盘中创建log、logError文件-->
          <!--<file value="Content\LogError"/>-->
          <file value="App_Data/"/>
          
          <!-- 如果想在本项目中添加路径,那就直接去掉C:\  只设置log\LogError   项目启动中默认创建文件 -->
          <appendToFile value="true"/>
          <!--按照何种方式产生多个日志文件(日期[Date],文件大小[Size],混合[Composite])-->
          <rollingStyle value="Date"/>
          <!--这是按日期产生文件夹-->
          <datePattern value="yyyy\yyyyMM\yyyyMMdd'.txt'"/>
          <!--是否只写到一个文件中-->
          <staticLogFileName value="false"/>
          <!--保留的log文件数量 超过此数量后 自动删除之前的   好像只有在 按Size分割时有效 设定值value="-1"为不限文件数-->
          <param name="MaxSizeRollBackups" value="100"/>
          <!--每个文件的大小。只在混合方式与文件大小方式下使用。超出大小后在所有文件名后自动增加正整数重新命名,数字最大的最早写入。可用的单位:KB|MB|GB。不要使用小数,否则会一直写入当前日志-->
          <maximumFileSize value="1GB" />
          <!-- layout 控制Appender的输出格式,也可以是xml  一个Appender只能是一个layout-->
          <layout type="log4net.Layout.PatternLayout">
            <!--每条日志末尾的文字说明-->
            <!--输出格式 模板-->
            <!-- <param name="ConversionPattern"  value="记录时间:%date 线程ID:[%thread] 日志级别:%-5level 记录类:%logger   
            操作者ID:%property{Operator} 操作类型:%property{Action}%n  当前机器名:%property%n当前机器名及登录用户:%username %n  
            记录位置:%location%n 消息描述:%property{Message}%n   异常:%exception%n 消息:%message%newline%n%n" />-->
    
            <!--样例:2008-03-26 13:42:32,111 [10] INFO  Log4NetDemo.MainClass [(null)] - info-->
            <!--<conversionPattern value="%newline %n记录时间:%date %n线程ID:[%thread] %n日志级别: %-5level %n错误描述:%message%newline %n"/>-->
            <conversionPattern value="%n==========
                                      %n【日志级别】%-5level
                                      %n【记录时间】%date
                                      %n【线程编号】[%thread]
                                      %n【执行时间】[%r]毫秒
                                      %n【出错文件】%F
                                      %n【出错行号】%L
                                      %n【出错的类】%logger 属性[%property{NDC}]
                                      %n【错误描述】%message
                                      %n【错误详情】%newline"/>
          </layout>
        </appender>
    
        <!--Error日志::: 错误日志-->
        <logger name="logerror">
          <level value="ERROR" />
          <appender-ref ref="ErrorAppender" />
        </logger>
      </log4net>
    </configuration>
    

    在Properties下的AssemblyInfo里面新加如下内容,在最下面新加,我们的配置文件指定了

    [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]
    

    在App_Start文件夹下新建一个异常的类叫MyErrorAttribute,这里我们指定了,如果发生异常了,那肯定不能展示给用户看,所以定义了一个500页面跳转的

        public class MyErrorAttribute: HandleErrorAttribute
        {
            public static Queue<Exception> ExceptionQueue = new Queue<Exception>();
    
            public override void OnException(ExceptionContext filterContext)
            {
                ExceptionQueue.Enqueue(filterContext.Exception);
    
                //可以跳转
                filterContext.HttpContext.Response.Redirect("~/Html/500.html");
    
                base.OnException(filterContext);
            }
    
        }
    

    然后在FilterConfig里面修改为我们自定义的异常类

        public class FilterConfig
        {
            public static void RegisterGlobalFilters(GlobalFilterCollection filters)
            {
                //filters.Add(new HandleErrorAttribute()); 默认的注释
                filters.Add(new MyErrorAttribute());
            }
        }
    

    最后一步,在Global.asax写

                //使用Log4日志
                log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(Server.MapPath("/Log4net.config")));
                ThreadPool.QueueUserWorkItem(o =>
                {
                    while (true)
                    {
                        if (MyErrorAttribute.ExceptionQueue.Count > 0)
                        {
                            Exception exception = MyErrorAttribute.ExceptionQueue.Dequeue();
                            if (exception != null)
                            {
                                ILog logger = LogManager.GetLogger("logerror");
                                logger.Error(exception.ToString());
                            }
                        }
                    }
                });
    

    完成,可以写一个错误看看效果了

  • 相关阅读:
    linux 修改文件夹颜色 终端颜色
    每日更新FadeTop背景为必应图片
    odoo 去除动作菜单的删除按钮
    crontab详解
    odoo 创建初始数据库 切换当前数据库
    python for else
    lfi phpinfo
    python __dict__
    iscsi 开机自动挂载
    HP SSD smart path
  • 原文地址:https://www.cnblogs.com/yunquan/p/11085441.html
Copyright © 2011-2022 走看看