ASP.NET MVC深入浅出系列(持续更新)

 

一. ASP.NET体系

  从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模式,ViewState功不可没,通过的控件的拖拽和绑定,很快就可以搭建出来一个Web项目,其开发速度远胜Java、PHP,当年Web项目并不很重视体验,没有今天响应式,没有各种前端js框架,所以在当年的WebForm,微软是以引以为豪的。

  该框架毕竟有时代局限性,随着前端的崛起,随着人们对项目体验的提高,随着项目的体量的增大,WebForm在当下强调松耦合、模块化、组件化的时代,略显落后,故微软于2009年推出第一个开源框架,即MVC框架,俗称ASP.Net MVC1,后面又陆续的推出 MVC2、3、4、5。现在最新的跨平台Web开发框架 Core MVC,它隶属于.Net Core框架,而非 .Net FrameWork下。

  下面用一张图表示一下在Core出现之前,.Net在Web开发即ASP.NET中包含的内容。

   简单分析一下:WebPages(没用过哎)、WebForms、MVC均为ASP.NET下的Web开发框架,WebAPI负责构建HTTP常规服务,SignalR负责构建实时服务。

   MVC和WebAPI简单对比一下:MVC用来构建网站既关心页面又关心数据,WebAPI只关心数据;MVC通过可以通过Action的名字来接受CURD操作,而WebAPI通过HTTP的访问方式(GET、PUT、POST、DELETE)来表达不同的CURD操作。

  (WebService、WCF、WebAPI、SignalR在后面会有单独的章节详细介绍)

二. WebForm和MVC对比

1. WebForm

  做过WebForm开发的朋友们应该很清楚,WebForm开发形式主要分为三种:

  ①:服务器端控件,即拖拽控件(aspx)

  ②:一般处理程序(ashx) + html模板

  ③:html静态页 + ajax + 一般处理程序(ashx)

  请求模型:

  

  WebForm的几个臭名昭著的缺点:封装太强,导致开发不灵活;ViewState的无状态;声明周期模型复杂,对于初学者不易于理解。

2. MVC

  我们经常从各种开发工程师(JAVA 、.NET、PHP)口中听到MVC,但不同人说的MVC貌似类似,但又有区别,那么MVC到底是什么呢?(类似的还有MVP和MVVM)

  1. 从宏观上来说:MVC是框架分层的一种搭建思想,在最原始的项目中,没有什么框架分层之说,所有的项目代码都在一个层里,这样会导致代码冗杂,耦合性强,项目迭代升级困难,MVC是一种分层思想,将一个项目代码分为几类,分别放到不同的层里,Model层存储一些数据和业务逻辑,View层处理页面问题,Controller层用来接收人机交互指令。MVC分层思想和传统的三层(数据库访问层、业务逻辑层、表现层)还是有区别的。

  MVC的核心是分离了View和Model(即页面和数据),Controller负责接收和发送指令,交给Model层处理。

  2. 从.Net的角度来说,MVC是微软提供一个Web开发框架,全称ASP.NET MVC,它是传统三层中View层的一种功能细分,一种表现形式而已,与三层没有直接关系。ASP.NET MVC框架中的开发流程围绕:

  ① Model:存储实体类,实现系统中的业务逻辑。

  ② View:页面展示(Razor模板),展现给用户。

  ③ Controller:页面的逻辑处理,用来与用户的交互,处理接受的Http请求。然后从Model中获取相应的数据,转发给View进行页面展示。

 补充:通过Controller中的Action,可以充当APP的服务端接口的作用,和WebAPI达到的效果一致,但访问方式是有区别的。

  请求模型:

  下面列举MVC框架的几个典型的优点:

  ①:松耦合,M-V-C分工明确,有利于代码的维护。

  ②:便于Seo优化,能自定义url的生成规则。

  ③:ASP.NET MVC5是个出色的表现层框架。

  ④:对单元测试支持友好。

三. ASP.Net MVC 框架

1. 包含的技术

  ASP.NET MVC框架,这里以MVC5为例,涉及到的知识有:Model-View-Controller的使用、Area和Global的理解、路由配置、数据传递的方式、AOP思想的体现(4大过滤器),各种Result、Razor语法、HttpContext相关的五大对象、分布视图、特性的补充、Html扩展控件、Bundles压缩js和css的原理、Owin约束、管道模型及MVC的工作原理。

   以及最新的Core MVC框架的相关知识。

2. 常见的约定

  ①:控制器命名通常以Controller结尾。

  ②:一个控制器对应一个View视图文件夹,常用页面都存放在里面。

四. 系列章节

第一节:走进MVC的世界(未完成)

第二节:各种路由约束(动态路由、静态路由、组合路由、正则约束、命名空间约束、区域内路由)

第三节:Action向View传值的四种方式(ViewData、ViewBag、TempData、Model)

第四节:MVC中AOP思想的体现(四种过滤器)并结合项目案例说明过滤器的实际用法

第五节:从源码的角度理解MVC中各种Result(ActionResult、JsonResult、JavaScriptResult等)---待补充

第六节:Razor引擎及相关语法(未完成)

第七节:HtmlHelper及其扩展(未完成)

第八节:layout和partialView、RenderPartial和Partial、 action和RenderAction(未完成)

第九节:从源码的角度分析MVC中的一些特性及其用法

第十节:数据批注(DataAnnotationModel)和自定义验证(包括Model级别的验证)

第十一节:Bundles压缩合并js和css及原理分析

第十二节:MVC中的一些特殊优化

第十二节:管道模型(未完成)

第十三节:HttpHander扩展及应用(自定义扩展名、图片防盗链)

第十四节:再探MVC中路由的奥秘

第十五节:HttpContext五大核心对象的使用(Request、Response、Application、Server、Session)

第十六节:利用MVC的扩展完成“一个后台,多套前端页面”的解决方案(未完成)

第十七节:XXX

第十八节:XXX

ORM系列之Entity FrameWork详解(持续更新)

 

一. 谈情怀

  从第一次接触开发到现在(2018年),大约有六年时间了,最初阶段连接数据库,使用的是【SQL语句+ADO.NET】,那时候,什么存储过程、什么事务 统统不理解,生硬的将SQL语句传入SQLHelper类中,后来知道还要注意SQL注入,引入参数化的方式,现在回想一下,那个阶段简直Low到爆。

  (PS:现在很多场景下还是需要使用ADO.NET,所以没有最好,只有最适合)

凡事都是从零开始的,正是这个阶段积累,才有了后续的发展。一次机会,接触到了微软的EF(ps:如果没有记错的话,当时用的是EF 5.0 的DBFirst模式),彻底颠覆了我的开发观念,不需要写SQL语句(那时还不清楚EF也可以调用SQL语句),Lambda表达式(并不知道Lambda还分Lambda表达式和Lambda语句),兴奋了一阵,然后进入了漫长的EF摸索和填坑阶段了(EF5.0→EF6.2→EF Core)。

    如果你觉得EF不灵活,不能调用存储过程或自定义事务?如果你觉得EF在处理海量数据库的时候会很慢?如果你觉得EF满足不了你的开发需求?。。。。。。 那么也许该系列的文章会对你有些帮助。

二. ORM框架

  说起DotNet领域的ORM框架,我们想到的有EF、NHibernate、FluentData、Dapper、SQLSugar等等。那么到底什么是ORM呢?

    ORM(Object Relational Mapping),中文名字:对象关系映射,它是面向对象思想的进一步的发扬光大,主要实现【程序对象】到【关系型数据库】的映射。在之前使用ADO.NET开发的时候,数据库访问层(即 DAL),要写很多增加、删除、保存的方法,很是麻烦,如果引入ORM框架,它自带CRUD的API(相当于DAL层已经给你写好了),可以自动生成SQL语句,所以作为开发者而言,只需要关注对象即可。

   ORM的含义:

  ① O:Domain Object  领域模型

  ② R:Relational DataBase 关系型数据库

  ③ M : Mapping 映射关系

ORM的三大核心原则:

  ① 简单性:以最简单的模式建模数据。

  ② 传达性:数据库结构被任何人都能理解的语言文档化。

③ 精确性:基于数据模型创建正确标准化的结构。

  EF的核心架构:

  ①  EDM(Entity Data Model):这是微软提供的一个强大的可视化工具,用来生成 O、R、M的

  ②  两种语法的查询:LINQ to Entities 和 Entity SQL

  ③ ObjectServices:Linq to Entities 和Entity Client Data Provider 之间起到转换作用

  ④ Entity Client Data Provider: 将Lambda转换成SQL语句

  ⑤ ADO.Net Data Provider: 标准的ADO.Net

  ⑥ DataBase:数据库

三. 从EF的入手,深入理解ORM框架

  EF(Entity FrameWork),是微软推出基于ADO.NET的数据库访问技术,它是一套ORM框架, 框架中包括了实例模型、数据模型、关系映射三部分,EF的上下文为DbContext,EF在 【.Net FrameWork】版本中最新的是 6.2.0 版本,最稳定的是6.2版本,在【.Net Core】中推出 EF Core (即所谓的“EF7”,^_^,当然已经不这么叫了)。

  EF包括三种模式:DBFirst、CodeFist、ModelFirst 。EF可以调用SQL语句、可以使用Linq查询、可以使用Lambda查询,EF还有很多高级属性,比如延迟加载、缓存属性等等,在该系列文章中都将逐一详细介绍。

  EF也有优缺点:

   优点:①  EF是微软自家的产品,和VS的集成度很高,极大的提高了开发效率。

        ②  EF中ModelFirst模式提供的模型设计器功能很强大,且采用T4模板生成的代码的功能很强大。

③  支持多数据库开发,简单的改变配置文件,就可以切换数据库。

缺点: 在复杂的查询生成SQL脚本的效率不是很高,浪费了性能。

     举例:之前在开发中,一张幼儿表里大约有60个字段左右,一个业务需要根据复杂的条件查询出来一些信息(大约5个字段左右),当时由于按开发人员的失误,写的Lambda表达式直接将所有字段查询出来,导致该业务耗时很长大约25s左右,后来在我的引导下,先去数据库中直接写SQL语句,查询所有字段,发现速度很快;然后将EF中的Lambda查询改为只查询指定的5个字段,发现速度由25s→3s左右,提升非常明显,当然这也很好的说明了EF的局限是在生成SQL的脚本效率上。

四. 系列章节

    第一节: 结合EF的本地缓存属性来介绍【EF增删改操作的几种形式

         第二节: 比较EF的Lambda查询和Linq查询写法的区别

         第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery )

         第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法

         第五节: EF高级属性(一) 之 本地缓存、立即加载、延迟加载(不含导航属性)

         第六节: EF高级属性(二) 之延迟加载、立即加载、显式加载(含导航属性)

         第七节: EF的三种事务的应用场景和各自注意的问题(SaveChanges、DBContextTransaction、TransactionScope)。

         第八节: EF的性能篇(一) 之 EF自有方法的性能测试   :

         第九节: EF的性能篇(二) 之 Z.EntityFramework.Extensions程序集解决EF的性能问题   :

         第十节: EF的三种追踪实体属性变化方式(DBEntityEntry、ChangeTracker、Local)     :

         第十一节: EF的三种模式(一) 之 DBFirst模式(SQLServer和MySQL两套方案):

         第十二节: EF的三种模式(二) 之 ModelFirst模式(SQLServer为例):

         第十三节: EF的三种模式(三) 之 来自数据库的CodeFirst模式    :

         第十四节: EF的三种模式(四) 之 原生正宗的 CodeFirst模式的默认约定   :

         第十五节: EF的CodeFirst模式通过DataAnnotations修改默认协定   :

         第十六节: EF的CodeFirst模式通过Fluent API修改默认协定   :

         第十七节: EF的CodeFirst模式的四种初始化策略和通过Migration进行数据的迁移  :

         第十八节: EF的CodeFirst模式连接MySQL数据库的解决方案(未完成)  :

         第十九节: 结合【表达式目录树】来封装EF的BaseDal层的方法 :

         第二十节: 深入理解并发机制以及解决方案(锁机制、EF自有机制、队列模式等):

         第二十一节:ADO层次上的海量数据处理方案(SqlBulkCopy类插入和更新):

         第二十二节: 以SQLServer为例介绍数据库自有的锁机制(共享锁、更新锁、排它锁等)(未完成)  :

         第二十三节: EF性能篇(三)之开源组件 Z.EntityFrameWork.Plus.EF6解决EF性能问题   :

         第二十四节:    :

         第二十五节:    :

第十六节:语法总结(3)(C#6.0和C#7.0新语法)

 

一. C# 6.0 新语法

1. 自动属性初始化可以赋值

 1     /// <summary>
 2     /// 自动属性初始化
 3     /// </summary>
 4     public class UserInfor
 5     {
 6         public string userId { get; set; } = "123456";
 7
 8         public string userName { get; set; } = "lmr";
 9
10     }
11    {
12        Console.WriteLine("------------------------- 1. 自动属性初始化可以赋值----------------------------");
13        UserInfor uInfor = new UserInfor();
14        Console.WriteLine($"id={uInfor.userId},userName={uInfor.userName}");
15    }

2. 字符串嵌入值【$配合{}使用】

  特别注意:{}中如果有业务计算需要配合()使用

1    {
2                 Console.WriteLine("------------------------- 2. 字符串嵌入值【$配合{}使用】----------------------------");
3                 UserInfor uInfor2 = new UserInfor();
4                 Console.WriteLine($"名字为:{uInfor2.userName}");
5                 //代表要输入一个括号{}
6                 Console.WriteLine($"名字为:{{{uInfor2.userName}}}");
7                 //{}中如果有业务计算需要配合()使用
8                 Console.WriteLine($"{(uInfor2.userName != "lmr" ? "小鲜肉" : "老鲜肉")}");
9    }

3. 可以在命名空间出使用static声明静态类

1 {
2                 Console.WriteLine($"-------------------------3.可以在命名空间出使用static声明静态类-------------------------");
3                 Console.WriteLine($"之前的使用方式: {Math.Pow(4, 2)}");
4                 Console.WriteLine($"导入后可直接使用方法: {Pow(4, 2)}");
5 }

4. 空值运算符(?)

  不需要判断是否为空了.

1  {
2                 Console.WriteLine($"-------------------------4. 空值运算符-------------------------");
3                 int? iValue = 10;
4                 Console.WriteLine(iValue?.ToString());//不需要判断是否为空
5                 string name = null;
6                 Console.WriteLine(name?.ToString());
7 }

5. 对象初始化

  可以直接给字典类型通过类似“索引”的形式赋值 (原先是通过Add方法赋值)

 1  {
 2                 Console.WriteLine($"-------------------------5. 字典对象初始化-------------------------");
 3                 Dictionary<string, string> dicList = new Dictionary<string, string>()
 4                 {
 5                     ["1"] = "ypf1",
 6                     ["2"] = "ypf2",
 7                     ["3"] = "ypf3"
 8                 };
 9                 Dictionary<string, string> dicList2 = new Dictionary<string, string>()
10                  {
11                      { "1","lmr1"},
12                      { "2","lmr2"},
13                      { "3","lmr3"}
14                  };
15                 foreach (var item in dicList)
16                 {
17                     Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString());
18                 }
19                 foreach (var item in dicList2)
20                 {
21                     Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString());
22                 }
23             }

6. 异常过滤器增加when判断条件

  只有符合when里的条件,才能进入catch,如果不满足的话,直接代码报错,不能抛异常

 1   {
 2                 Console.WriteLine("-------------------------6. 异常过滤器增加when判断条件-------------------------");
 3                 int epCheck = 100;
 4                 try
 5                 {
 6                     int.Parse("ypf");
 7                 }
 8                 catch (Exception e) when (epCheck > 11)
 9                 {
10                     Console.WriteLine(e.Message);
11                 }
12 }

7. nameof表达式

  把实例转换成同名的字符串

1  {
2                 Console.WriteLine("-------------------------7. nameof表达式-------------------------");
3                 UserInfor userInfor = new UserInfor();
4                 Console.WriteLine(nameof(userInfor));
5 }

8. 在catch和finally语句块里使用await(暂不介绍,不常用)

9. 在属性或方法上使用Lambada表达式

  ①:方法针对于只有一行的方法

  ②:属性通过Lambda的形式进行赋值

   public class UserInfor
    {
        public string userId { get; set; } = "123456";

        public string userName { get; set; } = "lmr";

        /// <summary>
        /// Lambda类型的方法
        /// </summary>
        public void LambdaPrint() => Console.WriteLine("我是Lambda类型的方法");

        public string userSex => string.Format("男");
    }
 {
                Console.WriteLine("-------------------------9.在属性或方法上使用Lambada表达式-------------------------");
                UserInfor userInfor = new UserInfor();
                userInfor.LambdaPrint();
                Console.WriteLine($"userSex的值为:{userInfor.userSex}");
}

二. C# 7.0 新语法

1. out参数的改进

  在C#7.0之前, out参数使用前必须先声明,然后传入方法中,在C#7.0后可以直接在传入方法的时候进行声明。

 1         /// <summary>
 2         /// out参数的改进
 3         /// </summary>
 4         /// <param name="x"></param>
 5         /// <param name="y"></param>
 6         public void DoNoting(out int x, out int y)
 7         {
 8             x = 1;
 9             y = 2;
10         }
11         {
12             Console.WriteLine("--------------------1-out参数的改进------------------------");
13             SevenEdition s = new SevenEdition();
14             s.DoNoting(out int x, out int y);
15             Console.WriteLine(x + y);
16          }

2. 元组(Tuples)

  ①:需要通过nuget安装这个System.ValueTuple

  ②:元组创建的三种方式:语法糖、Create静态方法、构造函数 (默认情况下是通过Item1、Item2 。。。)

  ③:指定元祖命名:可以通过左边指定,也可以通过右边指定

  ④:将元祖应用于方法中返回值,即一个方法可以返回多种不同类型的值,不需要封装实体即可以实现。

 1         /// <summary>
 2         /// 默认元组命名
 3         /// </summary>
 4         /// <returns></returns>
 5         public (int, string, string) TupleWay1()
 6         {
 7             return (1, "ypf1", "ypf2");
 8         }
 9         /// <summary>
10         /// 指定元祖命名
11         /// </summary>
12         /// <returns></returns>
13         public (int m, string n, string k) TupleWay2()
14         {
15             return (1, "ypf1", "ypf2");
16         }
17            {
18                 Console.WriteLine("--------------------2-元组(Tuples)------------------------");
19                 //1. 创建元组的三种形式
20                 Console.WriteLine("--------------------1. 创建元组的三种形式------------------------");
21                 var tuple1 = (1, 2);   //语法糖
22                 var tuple2 = ValueTuple.Create("ypf", "lmr");  //Create静态方法
23                 var tuple3 = new ValueTuple<int, string>(12, "ypf12");  //构造函数
24                 Console.WriteLine($"tuple1的两个值为:{tuple1.Item1}和{tuple1.Item2}");
25                 Console.WriteLine($"tuple1的两个值为:{tuple2.Item1}和{tuple2.Item2}");
26                 Console.WriteLine($"tuple1的两个值为:{tuple3.Item1}和{tuple3.Item2}");
27
28                 //2. 指定元祖命名
29                 Console.WriteLine("--------------------2. 指定元祖命名------------------------");
30                 (int m, string n) tuple4 = (100, "erp");  //左边命名
31                 Console.WriteLine($"tuple4的两个值为:{tuple4.m}和{tuple4.n}");
32                 var tuple5 = (one: 250, two: "spz");     //右边命名
33                 Console.WriteLine($"tuple5的两个值为:{tuple5.one}和{tuple5.two}");
34
35                 //3. 元祖应用于方法中返回值
36                 Console.WriteLine("--------------------3. 元祖应用于方法中返回值------------------------");
37                 SevenEdition s = new SevenEdition();
38                 //默认命名
39                 var result1 = s.TupleWay1();
40                 Console.WriteLine($"返回值有:{result1.Item1}、{result1.Item2}、{result1.Item3}");
41                 //指定命名
42                 var result2 = s.TupleWay2();
43                 Console.WriteLine($"返回值有:{result2.m}、{result2.n}、{result2.k}");
44
45             }

3. 局部函数

  即在{}中声明一个函数,只有{}中能使用

1   {
2                 Console.WriteLine("--------------------3-局部函数------------------------");
3                 DoSome();
4                 void DoSome()
5                 {
6                     Console.WriteLine("我是局部函数");
7                 }
8   }

4. 模式的比较和匹配

  ①:is的新模式。

    首先补充一个概念:拆箱和装箱

    拆箱是将引用类型→值类型 (object ,class均为引用类型)

    装箱是将值类型→引用类型

  案例:有一个object类型的变量a,如果它是int类型,则+10赋值给m,并输出m的值,下面看新老用法比较。

 1  {
 2                     Console.WriteLine("--------------------4-模式比较(is)------------------------");
 3                     object a = 9;
 4                     //老写法
 5                     if (a is int)
 6                     {
 7                         int b = (int)a;  //拆箱
 8                         int c = b + 10;
 9                         Console.WriteLine($"老写法c的值为:{c}");
10                     }
11                     //新写法
12                     if (a is int m)   //这里的a直接拆箱为m
13                     {
14                         int c = m + 10;
15                         Console.WriteLine($"新写法c的值为:{c}");
16                     }
17 }

  ②:switch-case中可以自定义参数类型

  传统情况,所有的case中必须是同类型的;而在C# 7.0 新版本中,case中可以不同类型,即Swich中可以传入任何类型,然后通过case中进行对应匹配,这就叫做模式匹配。

 1  /// <summary>
 2         /// 单一类型
 3         /// </summary>
 4         /// <param name="m"></param>
 5         /// <returns></returns>
 6         public dynamic SwitchWay1(string m)
 7         {
 8             dynamic data;
 9             switch (m)
10             {
11                 case "ypf1":
12                     data = m + "lmr1";
13                     break;
14                 case "ypf2":
15                     data = m + "lmr2";
16                     break;
17                 default:
18                     data = "spz00000";
19                     break;
20             }
21             return data;
22         }
23
24         /// <summary>
25         /// 多种类型
26         /// </summary>
27         /// <param name="m"></param>
28         /// <returns></returns>
29         public dynamic SwitchWay2(object m)
30         {
31             dynamic data;
32             switch (m)
33             {
34                 case int a when a > 10:
35                     data = a + 10;
36                     break;
37                 case int b:
38                     data = b + 100;
39                     break;
40                 case String c:
41                     data = c + "mmmmmmmmmmmmmmm";
42                     break;
43                 default:
44                     data = "spz00000";
45                     break;
46             }
47             return data;
48         }
 1    {
 2                     Console.WriteLine("--------------------4-模式匹配(switch-case)------------------------");
 3                     SevenEdition sE = new SevenEdition();
 4                     //老用法
 5                     var data1 = sE.SwitchWay1("ypf1");
 6                     Console.WriteLine($"类型为:{data1.GetType()},值为:{data1}");
 7                     //新用法
 8                     var data2 = sE.SwitchWay2(1);
 9                     Console.WriteLine($"类型为:{data2.GetType()},值为:{data2}");
10
11                     var data3 = sE.SwitchWay2(11);
12                     Console.WriteLine($"类型为:{data3.GetType()},值为:{data3}");
13
14                     var data4 = sE.SwitchWay2("ypf1");
15                     Console.WriteLine($"类型为:{data4.GetType()},值为:{data4}");
16
17  }

5. 数字文本语法的改写

   比如10000000 可以写成10_000_000 ,方便识别。

 {
                Console.WriteLine("--------------------5-数字文本语法的改写------------------------");
                long a = 10000000;
                long b = 10_000_000;
                Console.WriteLine($"a的值为:{a},b的值为:{b}");

 }

6. 补充一些特殊地方也可以写异常表达式 (不详细测试了)

  比如:条件表达式(? :)、null合并运算符(??)、一些Lambda

  eg:private string _name = GetName() ?? throw new ArgumentNullException(nameof(GetName));

第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字

 

一. 各类数据结构比较及其线程安全问题

1. Array(数组):

  分配在连续内存中,不能随意扩展,数组中数值类型必须是一致的。数组的声明有两种形式:直接定义长度,然后赋值;直接赋值。

  缺点:插入数据慢。

  优点:性能高,数据再多性能也没有影响

  特别注意:Array不是线程安全,在多线程中需要配合锁机制来进行,如果不想使用锁,可以用ConcurrentStack这个线程安全的数组来替代Array。

 1  {
 2                 Console.WriteLine("---------------------------01 Array(数组)-----------------------------------");
 3                 //模式一:声明数组并指定长度
 4                 int[] array = new int[3];
 5                 //数组的赋值通过下标来赋值
 6                 for (int i = 0; i < array.Length; i++)
 7                 {
 8                     array[i] = i + 10;
 9                 }
10                 //数组的修改通过下标来修改
11                 array[2] = 100;
12                 //输出
13                 for (int j = 0; j < array.Length; j++)
14                 {
15                     Console.WriteLine(array[j]);
16                 }
17
18                 //模式二:直接赋值
19                 string[] array2 = new string[] { "二胖", "二狗" };
20 }

2. ArrayList(可变长度的数组)

  不必在声明的时候指定长度,即长度可变;可以存放不同的类型的元素。

  致命缺点:无论什么类型存到ArrayList中都变为object类型,使用的时候又被还原成原先的类型,所以它是类型不安全的,当值类型存入的时候,会发生装箱操作,变为object引用类型,而使用的时候,又将object类型拆箱,变为原先的值类型,这尼玛,你能忍?

  结论:不推荐使用,建议使用List代替!!

  特别注意:ArrayList不是线程安全,在多线程中需要配合锁机制来进行。

 1   {
 2                 Console.WriteLine("---------------------------02 ArrayList(可变长度的数组)-----------------------------------");
 3                 ArrayList arrayList = new ArrayList();
 4                 arrayList.Add("二胖");
 5                 arrayList.Add("马茹");
 6                 arrayList.Add(100);
 7                 for (int i = 0; i < arrayList.Count; i++)
 8                 {
 9                     Console.WriteLine(arrayList[i] + "类型为:" + arrayList[i].GetType());
10                 }
11 }

3. List<T> (泛型集合) 推荐使用

  内部采用array实现,但没有拆箱和装箱的风险,是类型安全的

  特别注意:List<T>不是线程安全,在多线程中需要配合锁机制来进行,如果不想使用锁,可以用ConcurrentBag这个线程安全的数组来替代List<T>

 1 {
 2                 Console.WriteLine("---------------------------03 List<T> (泛型集合)-----------------------------------");
 3                 List<string> arrayList = new List<string>();
 4                 arrayList.Add("二胖");
 5                 arrayList.Add("马茹");
 6                 arrayList.Add("大胖");
 7                 //修改操作
 8                 arrayList[2] = "葛帅";
 9                 //删除操作
10                 //arrayList.RemoveAt(0);
11                 for (int i = 0; i < arrayList.Count; i++)
12                 {
13                     Console.WriteLine(arrayList[i]);
14                 }
15 }

4. LinkedList<T> 链表

  在内存空间中存储的不一定是连续的,所以和数组最大的区别就是,无法用下标访问。

  优点:增加删除快,适用于经常增减节点的情况。

  缺点:无法用下标访问,查询慢,需要从头挨个找。

  特别注意:LinkedList<T>不是线程安全,在多线程中需要配合锁机制来进行。

{
                Console.WriteLine("---------------------------04 ListLink<T> 链表-----------------------------------");
                LinkedList<string> linkedList = new LinkedList<string>();
                linkedList.AddFirst("二胖");
                linkedList.AddLast("马茹");

                var node1 = linkedList.Find("二胖");
                linkedList.AddAfter(node1, "三胖");
                //删除操作
                linkedList.Remove(node1);
                //查询操作
                foreach (var item in linkedList)
                {
                    Console.WriteLine(item);
                }
}

5. Queue<T> 队列

  先进先出,入队(Enqueue)和出队(Dequeue)两个操作

  特别注意:Queue<T>不是线程安全,在多线程中需要配合锁机制来进行,如果不想使用锁,线程安全的队列为 ConcurrentQueue。

  实际应用场景:利用队列解决高并发问题(详见:http://www.cnblogs.com/yaopengfei/p/8322016.html)

 1  {
 2                 Console.WriteLine("---------------------------05 Queue<T> 队列-----------------------------------");
 3                 Queue<int> quereList = new Queue<int>();
 4                 //入队操作
 5                 for (int i = 0; i < 10; i++)
 6                 {
 7                     quereList.Enqueue(i + 100);
 8                 }
 9                 //出队操作
10                 while (quereList.Count != 0)
11                 {
12                     Console.WriteLine(quereList.Dequeue());
13                 }
14 }

6. Stack<T> 栈

  后进先出,入栈(push)和出栈(pop)两个操作

  特别注意:Stack<T>不是线程安全

 1  {
 2                 Console.WriteLine("---------------------------06 Stack<T> 栈-----------------------------------");
 3                 Stack<int> stackList = new Stack<int>();
 4                 //入栈操作
 5                 for (int i = 0; i < 10; i++)
 6                 {
 7                     stackList.Push(i + 100);
 8                 }
 9                 //出栈操作
10                 while (stackList.Count != 0)
11                 {
12                     Console.WriteLine(stackList.Pop());
13                 }
14 }

7. Hashtable

  典型的空间换时间,存储数据不能太多,但增删改查速度非常快。

  特别注意:Hashtable是线程安全的,不需要配合锁使用。

{
                Console.WriteLine("---------------------------07 Hashtable-----------------------------------");
                Hashtable tableList = new Hashtable();
                //存储
                tableList.Add("001", "马茹");
                tableList["002"] = "二胖";
                //查询
                foreach (DictionaryEntry item in tableList)
                {
                    Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString());
                }
}

8. Dictionary<K,T>字典 (泛型的Hashtable)

  增删改查速度非常快,可以用来代替实体只有id和另一个属性的时候,大幅度提升效率。

  特别注意:Dictionary<K,T>不是线程安全,在多线程中需要配合锁机制来进行,如果不想使用锁,线程安全的字典为 ConcurrentDictionary。

 1  {
 2                 Console.WriteLine("---------------------------08 Dictionary<K,T>字典-----------------------------------");
 3                 Dictionary<string, string> tableList = new Dictionary<string, string>();
 4                 //存储
 5                 tableList.Add("001", "马茹");
 6                 tableList.Add("002", "二胖");
 7                 tableList["002"] = "三胖";
 8                 //查询
 9                 foreach (var item in tableList)
10                 {
11                     Console.WriteLine("key:{0},value:{1}", item.Key.ToString(), item.Value.ToString());
12                 }
13 }

强调: 

以上8种类型,除了Hashtable是线程安全,其余都不是,都需要配合lock锁来进行,或者采用 ConcurrentXXX来替代。

详细的请见:http://www.cnblogs.com/yaopengfei/p/8322016.html

二. 四大接口比较

1. IEnumerable

  是最基本的一个接口,用于迭代使用,里面有GetEnumerator方法。

2. ICollection

  继承了IEnumerable接口,主要用于集合,内部有Count属性表示个数,像ArrayList、List、LinkedList均实现了该接口。

3. IList

  继承了IEnumerable 和 ICollection,实现IList接口的数据接口可以使用索引访问,表示在内存上是连续分配的,比如Array、List。

4. IQueryable

  这里主要和IEnumerable接口进行对比。

  Enumerable里实现方法的参数是Func委托,Queryable里实现的方法的参数是Expression表达式。

  实现IQueryable和IEnumabler均为延迟加载,但二者的实现方式不同,前者为迭代器模式,参数为Func委托,后者为Expression表达式目录树实现。

三. yield关键字

1. yield必须出现在IEunmerable中

2. yield是迭代器的状态机,能做到延迟查询,使用的时候才查询,可以实现按序加载

3. 例子

  测试一:在 “var data1 = y.yieldWay();”加一个断点,发现直接跳过,不能进入yieldWay方法中,而在“foreach (var item in data1)”加一个断点,第一次遍历的时候就进入了yieldWay方法中,说明了yield是延迟加载的,只有使用的时候才查询。

  测试二:对yieldWay和commonWay获取的数据进行遍历,通过控制台发现前者是一个一个输出,而后者是先一次性获取完,一下全部输出来,证明了yield可以做到按需加载,可以在foreach中加一个限制,比如该数据不满足>100就不输出。

 1     //*********************************  下面为对比普通返回值和使用yeild返回值的方法  ************************************************
 2
 3        /// <summary>
 4        /// 含yield返回值的方法
 5        /// </summary>
 6        /// <returns></returns>
 7         public IEnumerable<int> yieldWay()
 8         {
 9             for (int i = 0; i < 10; i++)
10             {
11                 yield return this.Get(i);
12             }
13         }
14         /// <summary>
15         /// 普通方法
16         /// </summary>
17         /// <returns></returns>
18         public IEnumerable<int> commonWay()
19         {
20             int[] intArray = new int[10];
21             for (int i = 0; i < 10; i++)
22             {
23                 intArray[i] = this.Get(i);
24             }
25             return intArray;
26         }
27
28         /// <summary>
29         /// 一个获取数据的方法
30         /// </summary>
31         /// <param name="num"></param>
32         /// <returns></returns>
33         private int Get(int num)
34         {
35             Thread.Sleep(1000);
36             return num * DateTime.Now.Second;
37         }
 1             Console.WriteLine("-----------------------下面是调用yield方法-----------------------");
 2             yieldDemo y = new yieldDemo();
 3             var data1 = y.yieldWay();
 4             foreach (var item in data1)
 5             {
 6                 Console.WriteLine(item);
 7             }
 8             Console.WriteLine("-----------------------下面是调用普通方法-----------------------");
 9             var data2 = y.commonWay();
10             foreach (var item in data2)
11             {
12                 Console.WriteLine(item);
13             }

各种通讯连接方式

 

1. http  https (ajax)

2. 总结一下同一个项目下 前后交互的集中提交方式 (.Net特有的 EasyUI封装的)

3. 跨域请求的几种形式

3. HttpClient

4. WebService

5. WebAPI(重点)

6. socket

7. websocket (重点)+superSocket

8. SignalR (重点)

9. nodejs中的net通讯模块

10. WCF  (可以弃疗了)

设计模式篇

 

一. 什么是设计模式

  纠结了好久,今天终于下定决心开始写设计模式系列,因为这个系列章节确实不好写,在这之前,也看了好多关于设计模式的博客、视频、书籍等,大多数用的例子要么猫啊狗啊、大雁等动物类;要么就是学生类,教师类,吐槽一下,真shit,试想一下,哪个项目中会用这些动物类教师类呢?

  我也和我同行的朋友交流了一下关于设计模式,对设计模式的理解,可以分为这么几个层次:

  ①:根本不知道什么是设计模式。

  ②:听说过几种设计模式,理解不深。

  ③:能写出并理解几种设计模式,但不知道在项目中该怎么用。

  毋庸置疑,能否灵活的运用好设计模式,是一个名开发工程师迈向架构师的必经之路,上面说的这么玄乎,那么到底什么是涉及模式呢?这里先借助金庸的武侠小说来类比一下。

  作为金庸迷的我,金庸老师的“飞雪连天射白鹿,笑书神侠倚碧鸳”14部小说每一部看了都不低于3遍以上,对里面个各种武功也是了如指掌,像效果比较炫丽,威力比较大的有:“乔帮主降龙十八掌、段誉的六脉神剑、杨过的黯然销魂掌、任我行的吸星大法等等”,这些都是外家功夫,种类很多,一个人可能会多种,这就好比.Net的中MVC、EF、SignalR等等;当然也有内功心法,典型的有:”少林和尚的易筋经、张无忌的九阳神功”,这两种功夫本身并没有太大的杀伤力,但会了这种功夫,更容易融会贯通外家功夫,使外家功夫发挥出更大效果,拿到我们开发领域,“设计模式”就是内功心法,没有语言之分,它是一种模式,一种思想指导着我们开发。

  那么怎么才能算精通设计模式呢?

  看过《倚天屠龙记》的朋友可能会记得里面有这么一个场景:赵敏冒充明教挑战张三丰的时候,张无忌办成小道童出来救场,在对阵三个家奴的的时候,张三丰教了张无忌一套太极拳法,里面有这么一段对话:

 张三丰演示完后,问张无忌:“无忌,你记住了多少”,张无忌回答说:“无忌不才,只有一小部分没有记住”;过了一会,张三丰又问道:“现在能记住多少”,无忌说:“太师傅,我已经全部忘记了”,这时,张三丰说:“无忌你可以上了”,结果显然而知,对手被打的那叫一个惨啊。

  所以:设计模式的最高境界是,忘记设计模式,将23种的设计模式自然而然的融入开发中,哈哈,当然这个有点难,没有个五年以上的功力,很难达到这个层次。

 

二. 设计模式的内容

  设计模式是一种套路,是把 “别人成功的例子” 拿过来灵活运用,我们的优秀的前辈总结出来7个设计原则23种设计模式

设计原则:

    1. 单一职责原则    2. 里氏替换原则    3. 依赖倒置原则

    4. 接口隔离原则    5. 迪米特原则(最小知道原则)    6. 开闭原则

    7. 组合聚合原则

设计模式:

  1. 创建型模式 :工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

  2. 结构型模式:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  3. 行为型模式:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

   解释:设计模式只是一个大概的套路,不能生搬硬套,有一些做法可能是多个设计模式的融合,有的也很难归为某个设计模式。不要为了设计模式而设计模式,没有十全十美的代码。

  在上述设计模式中,大部分设计模式都用到了上面的设计思想,在设计模式中,我们会经常看到:开闭原则、里氏替换原则(多态)、面向接口编程、面向抽象编程、抽象类、override覆写这几种技术。

  如果对于抽象类或者override不明白的,有必要先看一下下面的章节:

                          第一节:从面向对象思想(oo)开发、接口、抽象类以及二者比较 

                          第二节:重写(new)、覆写(overwrite)、和重载(overload)

三. 系列章节

  下面将由浅入深,结合实际案例来讲解设计原则和设计模式。

   第一节:见识设计模式的神器之处。

     第二节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第三节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第四节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第五节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第六节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第七节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第八节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第九节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第十节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第十一节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第十二节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第十三节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第十四节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第十五节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第十六节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第十七节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第十八节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第十九节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第二十节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第二十一节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第二十二节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第二十三节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第二十四节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第二十五节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第二十六节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第二十七节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第二十八节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第二十九节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第三十节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第三十一节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第三十二节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

     第三十三节:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

 

(进度:一周写 2-3篇,大约2个半月 完成该系列)

第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借助TopSelf、服务类】)

 

一. IIS部署

  比如在MVC框架中集成了Quartz.Net定时调度,此时该调度系统会随着MVC框架被挂在IIS下,IIS会进程回收,所以大部分开发都会遇到Quartz挂在IIS下一段时间不好用。

  补充:IIS可以设置定时自动回收,默认回收是1740分钟,也就是29小时。IIS自动回收相当于服务器IIS重启,应用程序池内存清空,所有数据被清除,相当于IIS重启,在度量快速开发平台服务器端,为了减小数据库负担,内存中暂存了很多信息,不适合频繁的回收,因为回收会造成服务器端所有存在内存中的数据丢失,如果没有及时保存到数据库中,可能导致程序出现问题。而如果系统使用高峰时期,并不适合回收,回收可能导致几十秒IIS无响应,对于正在工作的人员来说,是一种很不好的体验,会以为是网络或者掉线等问题。

  解决方案:关闭该项目在IIS上对应的进程池的回收机制。

  如何关闭进程池的回收机制:选中IIS中部署的项目对应的进程池,点击【高级设置】,里面有5个核心参数:

    ① 发生配置更改时禁止回收:如果为True,应用程序池在发生配置更改时将不会回收。

    ② 固定时间间隔(分钟):超过设置的时间后,应用程序池回收,设置为:0 意味着应用程序池不回收。系统默认设置的时间是1740(29小时)。

    ③ 禁用重叠回收:如果为true,将发生应用程序池回收,以便在创建另一个工作进程之前退出现有工作进程

    ④ 请求限制:应用程序池在回收之前可以处理的最大请求数。如果值为0,则表示应用程序池可以处理的请求数没有限制。

    ⑤ 生成回收事件日志条目:每发生一次指定的回收事件时便产生一个事件日志条目。

   总结:即使可以将IIS进程池回收关掉,仍然不建议把Quartz挂到IIS下,长时间不回收,会存在内存溢出的问题。

二. C/S程序直接运行

  我们可以用控制台的形式或者Winform的形式单独做一套定时调度系统,与主框架分类,也便于维护,可以直接将exe程序或者Winform窗体程序在服务器上运行。

  总结:该方法不存在回收的问题,但直接在服务器上运行,容易不小心被运维人员关掉了。

  对于专业一点的人员来说,该方案,直接运行程序太Low了,所以通常是将exe程序发不成windows服务,通过服务的开启和关闭来 维护。

三. 借助topshelf来进行的windows服务部署

  官网:http://topshelf-project.com/ , 这是一种通用的发布服务的方式,步骤如下:

  1. 通过NuGet下载 Topshelf 的程序集

  2. 配置QuartzService类,充当定时任务的服务端程序

    ①:构造函数中执行定时调度任务

    ②:Start()方法控制调度的开启 (必填)

    ③:Stop()方法控制调度的关闭 (必填)

    ④:Pause()方法暂停调度 (选填)

    ⑤:Continue()方法继续调度 (选填)

    ⑥:Shutdown() 关闭 (需要支持)

  3. 在主程序中通过topshelf代码调用: HostFactory.Run 详见主程序。(在里面可以设置服务的名称、描述等

  4. 通过指令进行服务发布和卸载(查看windows服务:services.msc)

   ①:通过cmd命令定位到bin文件目录下(以管理员身份运行)

    (eg: 先输入 d: 切换到D盘,再执行下面的命令  cd D:\06-我的开发之路\DotNet体系\04-DotNet专题篇\01-Quartz.Net定时调度\01-Code\MyQuarzt\QuartzDemo\bin\Debug  )

   ②:QuartzDemo.exe help: 查看指令

     QuartzDemo.exe install: 安装服务

     QuartzDemo.exe install start : 安装服务且开启

     QuartzDemo.exe uninstall :卸载服务

  截止此处,大功告成,可以看到D盘中多了一个txt文件,每隔3s多一条数据

下面分享整个过程的代码部署的相应截图:

 (1). Quartz代码和TopSelf调用代码:

 1    public class QuartzService
 2     {
 3         IScheduler scheduler = null;
 4         /// <summary>
 5         /// 定时调度业务
 6         /// </summary>
 7         public QuartzService()
 8         {
 9             //1.创建作业调度池(Scheduler)
10             scheduler = StdSchedulerFactory.GetDefaultScheduler();
11
12             //2.创建一个具体的作业即job (具体的job需要单独在一个文件中执行)
13             var job = JobBuilder.Create<HelloJob5>().Build();
14
15             //3.创建并配置一个触发器即trigger   3s执行一次
16             var trigger = TriggerBuilder.Create().WithSimpleSchedule(x => x.WithIntervalInSeconds(3)
17                                                                            .RepeatForever()).Build();
18             //4.将job和trigger加入到作业调度池中
19             scheduler.ScheduleJob(job, trigger);
20         }
21         /// <summary>
22         /// 开启任务
23         /// </summary>
24         public void Start()
25         {
26             scheduler.Start();
27         }
28         /// <summary>
29         /// 关闭任务
30         /// </summary>
31         public void Stop()
32         {
33             //true:表示该Sheduler关闭之前需要等现在所有正在运行的工作完成才能关闭
34             //false:表示直接关闭
35             scheduler.Shutdown(true);
36         }
37         /// <summary>
38         /// 暂停调度
39         /// </summary>
40         public void Pause()
41         {
42             scheduler.PauseAll();
43         }
44         /// <summary>
45         /// 继续调度
46         /// </summary>
47         public void Continue()
48         {
49             scheduler.ResumeAll();
50         }
51
52     }
 1             HostFactory.Run(x =>                                 //1
 2                     {
 3                         x.Service<QuartzService>(s =>                        //2
 4                         {
 5                             s.ConstructUsing(name => new QuartzService());     //3
 6                             //开启和关闭  必选项
 7                             s.WhenStarted(tc => tc.Start());              //4
 8                             s.WhenStopped(tc => tc.Stop());               //5
 9
10                             // optional pause/continue methods if used
11                             // 暂停和继续  选填
12                             s.WhenPaused(tc => tc.Pause());
13                             s.WhenContinued(tc => tc.Continue());
14
15                             //// optional, when shutdown is supported
16                             //s.WhenShutdown(tc => tc.Shutdown());
17
18                         });
19                         x.RunAsLocalSystem();                            //6
20                         x.SetDescription("测试借助TopSelf将Quartz发布成服务");        //7
21                         x.SetDisplayName("QuartzService");                       //8
22                         x.SetServiceName("QuartzService2");                       //9
23                     });

(2). 定位到指定路径,并进行相应的服务部署

a. 很多指令

b. 安装服务并开启

查看服务列表:

查看服务运行结果:

删除服务:

三. 借助window服务类

  这里不详细介绍(网上一堆一堆的),推荐采用上面的TopShelf的服务发布方式。

ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借的更多相关文章

  1. 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字

    一. 各类数据结构比较及其线程安全问题 1. Array(数组): 分配在连续内存中,不能随意扩展,数组中数值类型必须是一致的.数组的声明有两种形式:直接定义长度,然后赋值:直接赋值. 缺点:插入数据 ...

  2. 【C#复习总结】探究各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字

    前言 先普及一下线程安全和类型安全 线程安全: 如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码.如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的 ...

  3. ASP.NET MVC 5 学习教程:Edit方法和Edit视图详解

    原文 ASP.NET MVC 5 学习教程:Edit方法和Edit视图详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 ...

  4. 【译】ASP.NET MVC 5 教程 - 7:Edit方法和Edit视图详解

    原文:[译]ASP.NET MVC 5 教程 - 7:Edit方法和Edit视图详解 在本节中,我们继续研究生成的Edit方法和视图.但在研究之前,我们先将 release date 弄得好看一点.打 ...

  5. ORM系列之Entity FrameWork详解

    一. 谈情怀 从第一次接触开发到现在(2018年),大约有六年时间了,最初阶段连接数据库,使用的是[SQL语句+ADO.NET],那时候,什么存储过程.什么事务 统统不理解,生硬的将SQL语句传入SQ ...

  6. asp.net mvc常用的数据注解和验证以及entity framework数据映射

    终于有时间整理一下asp.net mvc 和 entity framework 方面的素材了. 闲话少说,步入正题: 下面是model层的管理员信息表,也是大伙比较常用到的,看看下面的代码大伙应该不会 ...

  7. ASP.NET MVC深入浅出(被替换) 第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式 第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery ) 第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法 第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性) 第十节: EF的三种追踪

    ASP.NET MVC深入浅出(被替换)   一. 谈情怀-ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态 ...

  8. ASP.NET MVC深入浅出系列

    一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模式,ViewState功不可没,通过的控件的拖拽和绑 ...

  9. ASP.NET MVC深入浅出(被替换)

    一. 谈情怀-ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模式,ViewState功不可没,通过的控件的 ...

随机推荐

  1. SQL使用开窗函数与CTE查询每月销售额的前几名

    WITH tagTab AS( SELECT YearMonth, pm=RANK() OVER(PARTITION BY YearMonth ORDER BY amount DESC) FROM S ...

  2. sass学习--什么是sass

    1.预备知识--什么是 CSS 预处理器 CSS 预处理器定义了一种新的语言,其基本思想是,用一种专门的编程语言,为 CSS 增加了一些编程的特性,将 CSS 作为目标生成文件,然后开发者就只要使用这 ...

  3. Keil C51 知识点

    第一节 Keil C51扩展关键字     深入理解并应用C51对标准ANSIC的扩展是学习C51的关键之一.因为大多数扩展功能都是直接针对8051系列CPU硬件的.大致有以下8类: 8051存储类型 ...

  4. WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!问题

    ➜ web_develop git:(master) ✗ ssh root@172.16.146.143@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ...

  5. python 实现神经网络算法

    注: Scratch是一款由麻省理工学院(MIT) 设计开发的一款面向少年的简易编程工具.这里写链接内容         本文翻译自“IMPLEMENTING A NEURAL NETWORK FRO ...

  6. [12] 扇形体(Fan)图形的生成算法

    顶点数据的生成 bool YfBuildFunVertices ( Yreal radius, Yreal degree, Yreal height, Yuint slices, YeOriginPo ...

  7. 20155226 2016-2017-2 《Java程序设计》第2周学习总结

    20155226 2016-2017-2 <Java程序设计>第2周学习总结 教材学习内容总结 了解了基本类型以及初识类类型,熟悉了注释,变量及运算符的使用. 了解了几种运算方式但还不算熟 ...

  8. (转)OpenCV 访问Mat中每个像素的值

    转自:http://blog.csdn.net/xiaowei_cqu/article/details/19839019 在<OpenCV 2 Computer Vision Applicati ...

  9. Netty--JDK序列化编解码传输对象

    使用JDK序列化不需要额外的类库,只需要实现Serializable即可,但是序列化之后的码流只有Java才能反序列化,所以它不是跨语言的,另外由于Java序列化后码流比较大,效率也不高,所以在RPC ...

  10. python调试方法

    之前调试python程序都是用print参数,感觉有点弱爆啊,最近发现python也有类似C语言gdb的工具pdb,记录下pdb的使用方法和心得. 先找了段简单的测试程序: 复制代码 !/usr/bi ...