• ASP.NET MVC3入门学习总结


      首先MVC是基于ASP.NET的一种软件架构模式,由三部分组成:模型(Model)、视图(View)和控制器(Controller)。

      MVC工作原理简单概括为:当用户请求一个URL时,首先系统会根据MVC中的路由系统对URL进行匹配,若匹配成功,系统会根据URL和路由匹配规则找到相应的Controller进行处理(Controller中会有一个具体的Action方法处理请求),最后将相关处理数据交给View进行数据呈现。下面将分细步骤详细呈述:

    MVC路由系统中的原理及路由规则(讲的很肤浅):在系统启动时,会在全局类文件Global.asax.cs中注册路由,如下代码:

    protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
    
                RegisterGlobalFilters(GlobalFilters.Filters);
                RegisterRoutes(RouteTable.Routes);
            }

    路由规则在方法RegisterRoutes(RouteTable.Routes)中进行定义,如下:

    public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
                routes.MapRoute(
                    "Default", // 路由名称
                    "{controller}/{action}/{id}", // 带有参数的 URL
                    new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // 参数默认值           
           new { id="d+" },
          
    new string[] { "namespace" } ); }

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}")表示路由系统将忽略匹配(或者禁止直接请求)任何路径下的后缀名为.axd的文件(关于.axd文件的解释http://www.cnblogs.com/zgqys1980/archive/2010/08/31/1813852.html,感谢分享);

    routes.MapRoute第一个参数定义路由名称;

    第二个参数是关键,{xxx}表示占位符,{controller}/{action}/{id}第一个占位符表示控制器名称,第二个占位符表示action名称,第三个占位符表示URL参数,对于没有通过占位符定义的规则,在URL中必须以相同的字符串进行匹配,如movie/{action}/{id}表示控制器名称必须为movie,{controller}/v_{action}/{id}表示action必须以v_开头。若在该参数中没有以占位符的形式定义controller或action,路由将使用第三个参数中的controller或action的默认值进行匹配,如:

    routes.MapRoute(
                    "UsingParams",
                    "p/{p1}/{p2}/{p3}.jsp",
                    new { controller = "Home", action = "UsingParams" },
                    new { p1="[a-z0-9]+", p2=@"d+" }
                );

    对于URL为http://hostname/p/aa/11/1a.jsp的请求,路由会找到名为Home的controller和名为UsingParams的action。如果没有定义controller和action的默认值,则会匹配失败;

    第三个参数定义第二个参数中各占位符中的默认值,若URL中省略了某一占位符不写,则使用默认值代替,若没有定义默认值,URL中就必须显示的指定其名称,URL匹配规则就是配少不配多,少的的部分由默认值代替,少的部分没有设置默认值,则匹配失败,另外Urlparameter.Optional表示参数可选;

    第四个和第五个参数可选,第四个用正则表达式对占位符的值进行验证,第五个指定应用程序可引用的命名空间,由string数组定义。

    当系统对URL匹配成功之后,首先会去找匹配出来的controller及其中action,执行action完成之后,紧接着在Views文件夹下面找到与controller同名的文件夹,然后在该文件夹下面找到与action名相同的view加载数据返回给用户。

      模型(Model):在此定义你的数据实体类、业务组件接口、业务组件实现类等,用于对数据进行封装、操作。本人在做demo时使用了EFCodeFirst模式(基于Entity FrameWork)封装实体类操作,关于EFCodeFirst有以下几点需要注意的:

      1、Entity Framework和EFCodeFirst的安装,在利用VS 2010工具Add Library Package Reference安装时提示“This package (or one of this dependencies) contains  PowerShell scripts and needs to be installed from the pAckage Manager  Console.”错误,后来在网上找到了一个解决方案,大致如下:

    在控制台输入:install-package -id EntityFramework -Version 4.1.10331.0,EntityFramework安装完成;

    然后输入:install-package -id EFCodeFirst -Version 1.1,EFCodeFirst安装完成;

    然后打开Add Library Package Reference工具,显示如下说明你安装成功了:

    (来自http://blog.csdn.net/yangzhencheng_001/article/details/6684853,感谢分享!)

      2、使用EFCodeFirst模式时需要再建立一个除实体类之外的表示数据库实体上下文的类,通过该类对实体类数据进行操作。如下上下文类:

    namespace MvcMusicStore.Models
    {
      //该类必须继承DbContext类
    public class MusicStoreEntities : DbContext {      //通过该属性获取Album相关数据,该属性必须是一个DbSet泛型集合
    public DbSet<Album> Albums { get; set; } public DbSet<Genre> Genres { get; set; } public DbSet<Artist> Artists { get; set; } public DbSet<Cart> Carts { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<OrderDetail> OrderDetails { get; set; } } }

    注:该类名必须与配置文件中数据库连接字符串中name值相同。

    当第一次通过该上下文类获取相关数据时,EFCodeFirst会根据model中定义的实体类名建立数据库(应用程序根目录下web.config默认配置了连接字符串),在该自动建立的数据库中,数据库名称根据上下文类约定建立,实体类名映射到库中表名,每一个实体类的实例映射表中一条数据,类中每一个属性映射表中每一个字段。注意:当类中属性发生改变时,在实际开发中一般选择手动修改数据库中的表使之对应(还有一种方法使用DropCreateDatabaseIfModelChanges类对表进行重建,这种方法会删除以前保存的数据,不推荐),修改完成之后需要删除自动生成的EdmMetadata表,否则还是会抛出异常。

      对实体类的属性利用特性进行验证,实现DRY(“Don’t Repeat Yourself,中文意思为:不要让开发者重复做同样的事情)原则,需要引用namespace System.ComponentModel.DataAnnotations,同时需要结合view页面中的帮助器实现,example:

    Using System.ComponetModel.Annotations;

    //实体类
    public
    class Movie { public const string pricePattern = @"^[1-9]d*.d*|0.d*[1-9]d*$";      //EF会约定默认使用名称为Id或者类名 + Id的字段做为主键,但是你可以使用特性Key另外指定主键
         //ScaffoldColumn标注在使用Movie模型的视图中不使用该字段建立基架,但是应该用一个隐藏标签绑定此字段,以便post请求时填充模型
         [ScaffoldColum(false)]
         [key]
    public int RecordID { get; set; }      //系统首先会根据数据库表中该字段的设置进行验证,若此处设置了特性验证,此处的特性验证将会覆盖默认验证
            //若无Required特性描述验证,系统会根据数据库表中该字段的值是否允许为空的设置进行验证

    [Required(ErrorMessage
    ="Title Required")] public string Title { get; set; } [Required(ErrorMessage="ReleaseDate Required")] public DateTime ReleaseDate { get; set; }      //DisplayName指示在视图模型中利用该属性建立的基架的显示名称
    [Required(ErrorMessage
    = "Genre Required")]      [DisplayName("Genre")]
    public string Genre { get; set; } [Required(ErrorMessage = "Price Required")] [RegularExpression(pricePattern, ErrorMessage = "Price format error")] [Range(1,100, ErrorMessage="the price must be between 1 and 100")] public decimal Price { get; set; } [StringLength(5, ErrorMessage="the max length is 5 char")] public string Rating { get; set; } //virtual表示该属性做为外键引用Album模型,会延迟加载,可以使用Album属性加载到Album对象的相关信息
            public virtual Album Album { get; set; }

    }

    //View页面中一部分,注意这是一个使用强类型模板的view,接受Controller传过来的强类型model
    <script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
    @* 客服端验证支持脚本使用jquery定义,需要先引用jquery库 *@
    <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>


    @using (Html.BeginForm()) {
            @Html.ValidationSummary(true)
            <fieldset>
                <legend>Movie</legend>
        @Html.HiddenFor(model => model.RecordID)
                <div class="editor-label">
                    @Html.LabelFor(model => model.Title)
                </div>
                <div class="editor-field">
                    @Html.EditorFor(model => model.Title)
                    @Html.ValidationMessageFor(model => model.Title, "", new { style="color:red;"}) //new { style="color:red;"}用于定义错误消息的样式
                </div>
       
                <div class="editor-label">
                    @Html.LabelFor(model => model.ReleaseDate)
                </div>
                <div class="editor-field">
                    @Html.EditorFor(model => model.ReleaseDate)
                    @Html.ValidationMessageFor(model => model.ReleaseDate)
                </div>
       
                <div class="editor-label">
                    @Html.LabelFor(model => model.Genre)
                </div>
                <div class="editor-field">
                    @Html.EditorFor(model => model.Genre)
                    @Html.ValidationMessageFor(model => model.Genre)
                </div>
       
                <div class="editor-label">
                    @Html.LabelFor(model => model.Price)
                </div>
                <div class="editor-field">
                    @Html.EditorFor(model => model.Price)
                    @Html.ValidationMessageFor(model => model.Price)
                </div>
                <div class="editor-label">
                    @Html.LabelFor(model => model.Rating)
                </div>
                <div class="edit-field">
                    @Html.EditorFor(model => model.Rating)
                    @Html.ValidationMessageFor(model => model.Rating)
                </div>
                <p>
                    <input type="submit" value="Create" />
                </p>
            </fieldset>
        }

      利用这种验证方式可以在任何使用该model建立的强类型视图中对model Movie进行验证,在运行应用程序时,应用程序首先会用脚本进行验证(需要引用相关脚本文件),若客服端禁用了javascript,则会在服务器端进行验证。这将使我们的代码更加清晰明确,更加具有可读性、可维护性与可移植性。

      关于model中实体类及业务组件的几点理解:

    毋庸置疑,实体类结构与数据库中表结构相互映射,类名映射表名,属性名映射字段名。实体类中除了属性外,不需要任何构造器和方法的实现;对于业务组件类,建议首先定义一个业务组件接口,定义该业务组件的种类和业务类型,然后再定义一个业务组件类继承该接口,在该业务组件类中对接口一一实现。另外,建议再定义一个专门用于构造各种业务组件的类,在该构造类中返回类型为业务组件接口的各种业务组件实例。

      默认情况下,EF只会加载查询涉及到的实体,但它还支持两种实体加载方式:

    贪婪加载:必须显式的使用Include()方法加载,使用贪婪加载关联实体时会使用left out join查询相关联的所有关系数据,只会有一次服务器查询,但这样会有效率问题(如果加载的关联数据较多)

    延迟加载:在poco类中用关键字virtual修改关联实体属性,在需要用到关联实体时,才会到服务器单独查询关联实体,会有多次服务器查询,但每一次查询的效率是比较快的

    一般在循环中采用贪婪方式加载关联实体,其它都建议使用延迟加载。

      View(视图):

      View页面根据Controller中返回的数据进行页面呈现,同时系统将从View页面中捕捉的用户数据post到Controller进行处理(在View表单中的数据一般用post方式进行提交,使用Get方法会打开一个安全漏洞)。

      MVC中controller和view的文件结构对应关系如下:

    所有controller文件默认放在Controllers文件夹下面,也可以自定义文件夹存放controller,若不同文件夹下面存在同名的controller,则需要在路由注册方法中显示添加你想要访问的controller的命名空间,如new string[] { "MVCDemo2.Controllers"}。所有view放在Views文件夹下面,对于每一个controller都会在Views下面对应一个名为Controller名的文件夹,在Controller名文件夹下的每一个view对controller里面的每一个action。

      在MVC3中可以是引用razor引擎对视图进行布局,在razor引擎视图中以@开头来编写非html标签代码块,如下所示:

    @* 调用变量或方法 *@
    @Model.attr
    @html.ActionLink()
    @* 编写代码块 *@
    @{
       xxxx
       xxxx 
    }

    html标签和代码嵌套使用,如下所示:

    @{
       List<StudentEntity> list = BuildServerModel.CreateStudentModel().GetAllStudent();
       <ul>
       @{
           foreach(StudentEntity stu in list)
           {
              <li>@stu.UserName</li>
           }
       }
       </ul>
    }

      视图可以简单分为五类类:

    1、普通视图,与controller中action一一对应;

    2、布局视图,类似于普通web form中母版页,可统一放在Master文件夹下;

    3、部分视图,类似于普通web form中用户控件,可统一放在Partial文件夹下;

    4、共用视图,用于处理系统中某种特殊需求,如显示错误页面(凡是出现异常的action都可以返回该视图),一般共用视图放在Views/Shared文件夹下面

    5、全局视图_ViewStart.cshtml,存在于在Views下面,也可以在某一视图文件夹下面建立_ViewStart.cshtml,二者作用范围不一样,只作用于当前文件夹下的视图,子文件夹下面的_ViewStart.cshtml会覆盖父文件夹下面的_ViewStart.cshtml中定义;当启动任何一个在其作用范围内视图时,都会执行该全局视图中的操作。

    布局视图使用示例:

    <div id="main">
      @* 渲染指定部分视图 *@
      @Html.Partial("_LogOnPartial")
      

      @* @RenderBody()渲染当前请求的页面,在LayOut页面中只能使用一次 *@            
      @RenderBody()<br />

         @* @RenderBody()渲染指定的页面,在LayOut页面中能多次使用 *@

         @RenderPage("~/Views/Home/ViewPage1.cshtml")<br />            

         @RenderPage("~/Views/Home/ViewPage1.cshtml")<br />

       @* 定义占位符,类似于WebForm中Master中的<asp:ContentPlaceHolder /> *@
         @RenderSection("SectionA", false)<br />

          <div id="footer"> </div> </div>

    <p>
       @* 在请求上面布局视图的普通视图中定义section *@
    @section SectionA{
    this is SectionA } </p>

      另外,MVC提供了一个HtmlHelper类用于在视图中完成很多任务,如:@html.ActionLink。同时,我们还可以自定义HtmlHelper扩展,如下所示两种方法:

    @* 直接在视图中定义 *@
    @helper Truncate(string input, int length)
    {
        if(input.Length <= length)    
        {
            @input
        }
        else
        {
            @input.Substring(0, length)<text>...</text>
        }
    }
    
    public static class HtmlHelpers
        {
            //this HtmlHelper htmlHelper表示方法需要通过当前视图中的HtmlHelper对象进行调用
            public static string Truncate(this HtmlHelper htmlHelper, string input, int length)
            {
                if (input.Length <= length)
                {
                    return input;
                }
                else
                {
                    return input.Substring(0, length) + "...";
                }
            }
        }

      下面简单阐述一下使用强类型的视图:

      controller(控制器):

      所有的controller都必须继承Controller类,Controller类又继承了IController接口,Controller类对IController接口进行了很丰富的实现,这使得我们的开发更加高效(当然,我们也可以自己去实现接口)。对于controller中的action都必须返回一个类型为ActionResult的值(Controller类中有很多实现方法返回类型为ActionResult的值),下面列举了十一种方法返回actionresult类型:

    public ActionResult Index()
            {
                return View();
            }
    
            public ActionResult ContentResult()
            {
                return Content("返回ContentResult类型结果!");
            }
    
            public ActionResult FileResult()
            {
                return File(Server.MapPath("~/Content/images/demo.jpg"), "application/x-jpg", "demo.jpg");
            }
    
            public ActionResult EmptyResult()
            {
                return new EmptyResult();
            }
    
            public ActionResult HttpNotFoundReusult()
            {
                return HttpNotFound("Page not found");
            }
    
            public ActionResult HttpUnauthorizedResult()
            {
                return new HttpUnauthorizedResult();
            }
    
            public ActionResult JavascriptResult()
            {
                return JavaScript("alert("Hi, I'm JavaScript.");");
            }
    
            public ActionResult JsonResult()
            {
                var jsonObj = new { id = 1, name = "penny" };
                return Json(jsonObj, JsonRequestBehavior.AllowGet);
            }
    
            public ActionResult RedirectResult()
            {
                return Redirect("~/Content/images/demo.jpg");
            }
    
            public ActionResult RedirectToRouteResult()
            {
                return RedirectToRoute(new { controller = "Home", action = "Index" });
            }

    对action方法是用特性:

    1、利用特性对action重命名,如[ActionName("NewName")];

    2、利用特性将action声明为无法请求,如[NonAction];

    3、[HttpGet]、[HttpPost]声明request请求的方式,默认为get请求,一般在包含用户输入数据的请求中使用Post方式(使用get请求会打开一个安全漏洞),post请求需要显式地声明特性[HttpPost],example:

    //用户第一次请求一个表单提交页面时会默认使用get方式调用第一个Create方法返回表单提交页面View       
    public ActionResult Create()
            {
                return View();
            }
    //用户点击提交按钮再次请求时会使用post方式调用第二个Create方法
            [HttpPost]
            public ActionResult Create(Movie newMovie)
            {
                if (ModelState.IsValid)
                {
                    db.Movies.Add(newMovie);
                    db.SaveChanges();
                    return RedirectToAction("Index");
                }
                else
                {
                    return View(newMovie);
                }
            }
    view中如何接受action传过来的数据:
    action中如何接受view传过来的数据:

    1、view中只有form表单里面的数据才能传递到action中,接受form表单参数的action必须用[HttpPost]描述;

    2、form表单里面的数据在请求action时都会被添加到一个FormCollection集合对象中,在后台action中可以通过此对象获取请求参数,如:

    [HttpPost]
    public ActionResult AddressAndPayment(FormCollection values)
    

    3、还可以通过Request[“paramName”]获取表单请求参数;

    4、若view使用了强类型对象模板,在请求action时,系统会利用form表单中数据自动构建一个模型对象传到action,在action中可以直接以参数的形式获取该模型对象,如:

    [HttpPost]
    public ActionResult AddressAndPayment(Orders order)
    
    增删改实体的两种操作方式:

    1、通过DbContext容器类提供的方法进行增删改;

    //add
    [HttpPost]
     public ActionResult Create(Student model)
    {
             db.Students.Add(model);
              db.SaveChanges();
              return RedirectToAction("Index");
     }
    //update
     public ActionResult Edit(Student model)
    {
    //method1
                        var student = db.Students.Find(model.StudentID);
                        TryUpdateModel<Student>(student);
    //method2
                        var student = db.Students.Find(model.StudentID);
                        UpdateModel<Student>(student);
                        db.SaveChanges();
     }
    //delete
    [HttpPost]
            public ActionResult Delete(Student model)
            {
                    var student = db.Students.Find(model.StudentID);
                    db.Students.Remove(student);
                    db.SaveChanges();
                    return RedirectToAction("Index");
            }

    2、通过修改实体状态属性来进行增删改,这里说的实体类是指从form表单中构建的模型;

    //add
    db.Entry<Student>(model).State = EntityState.Added;
    db.SaveChanges();
    //update
    db.Entry<Student>(model).State = EntityState.Modified;
    db.SaveChanges();
    //delete
    db.Entry<Student>(model).State = EntityState.Deleted;
    db.SaveChanges();
    
    Controller中资源释放:
    一般Controller类可以再继承IDisposable接口,以确保及时释放资源:
    protected override void Dispose(bool disposing)
            {
                storeDB.Dispose();
                base.Dispose(disposing);
            }
    
    关于EF中查询操作的加载时间的理解:
    var students = from s in db.Students select s;
    

    这个时候的students是IQueryable类型的,之后将查询操作翻译成sql语句,不执行;

    students = students.ToList();
    

    这个时候students是IEnumerable类型的,此时才会执行sql操作。

  • 相关阅读:
    MRC下多个对象的内存管理
    MacOS安装flutter(MacOS下flutter环境配置)
    swift 简化使用UserDefaults,UserDefaults使用优化
    swift中多继承的实现
    iOS blowfish加密解密
    *** Assertion failure in void UIViewReportBrokenSuperviewChain(UIView *__strong, UIView *__strong, BOOL)()
    iOS建立自己cocoaPods库
    iOS关于UISwitch按钮值的改变无法监听到的问题
    iOS 11 UICollectionView顶部出现白色间隔的问题
    iOS 对UIAlertController内的输入框进行输入监听,实时改变确定、取消按钮颜色
  • 原文地址:https://www.cnblogs.com/JDotNet/p/3185268.html
Copyright © 2020-2023  润新知