浏览器地址框输入域名并回车,通过DNS解析找到对应的IP地址,然后通过HTTP协议建立链接,找到目标服务器位置,接着就是TCP三次握手建立可靠链接,发送数据,服务器处理数据,TCP四次挥手断开链接,最后浏览器根据返回的数据解析渲染呈现页面。
SLOID原则
单一职责原则(SRP原则)
高内聚,低耦合
开放封闭原则(OCP原则)
封闭修改,开放拓展
里斯替换原则(LSP原则)
子类替换父类,实现多态
接口隔离原则(ISP原则)
使用专用小接口
依赖倒置原则(DIP原则)
高层调用接口,底层实现接口
对象变量赋值,判断,装箱
public override bool Equals(object obj){//重写父类equal方法,引用类型不是数据内容的比较,重写GetHashCode方法。
if(obj==null){
return false;
}
else{
if(obj is MyClass){
return this.value==(obj as MyClass).value;
}
}
return false;
}
继承
Parent p=new Child();
Console.WriteLine(p.value);//调用的父类的对象
Console.WriteLine((p as Child).value);
接口与回调
新写一个类实现ICallBack接口,然后将其加入到Controller对象的内部集合
static void Main(string[] args){
Controller controller=new controller();
controller.AddCallBack(new CallBackClass());//CallBackClass实现了ICallBack接口
controller.Begin();
}
class CallBackClass:ICallBack{
Console.WriteLine(DateTime.Now.ToString());
}
多态
继承多态基于抽象类和抽象方法
abstract class Fruit{
public abstract void GrowInArea();
}
FeedAnimals(IEnumerable ans)
对象注入的构造方式
class B{
}
class A{
private B innerobj;
public A(B obj){
innerobj=obj;
}
}
基于接口的对象注入
interface MyInterface{
}
calss B:MyInterface{
}
class A{
private MyInterface innerObj;
public A(MyInterface obj){
innerobj=obj;
}
}
A obj=new A(new B());
1、泛型
属性
private int age;
public int Age{
get{return age;}
set{
if(age
age=18;
else
age=value;
}
}
相较于传值为object的方法
public static SayHi(T tparameter) where T:people //类型约束
{Console.WriteLine(tparameter.SayHi());
}
public static SayHiObject(object tparameter)
{Console.WriteLine(tparameter);
}
泛型约束
类型后添加
where T:class ,new() //默认是个类,有构造函数
where T:class //引用类型 默认null
where T:struct //值类型
new() //含有无参构造函数
//具体类型,或该类型的子类
Set() 调用T对应的数据实体
1.1序列化
[Serializable]//使类支持序列化
class CollegeStudent{
public string Name="空";
public bool IsMale=true;
}
private void SeriallizeObj(String FileName,CollegeStudent stu){
//创建FileStream对象
using(FileStream writer=new FileStream(FileName,FileMode.Create)){
//创建格式化器对象
IFormatter formatter=new BinaryFormatter();
//格式化器对对象使用FileStream对象序列化对象
formatter.Serialize(writer,stu);
MessageBox.Show("对象成功保存到文件:"+FileName);
}
}
//大批量的复制对象反序列化
MyClass obj=new MyClass();
//创建内存流对象
using(MemoryStream ms=new MemoryStream){
IFormatter formator=new BinaryFormatter();
//对象序列化到内存流
formator.Serialize(ms,obj);
//克隆100个对象
for(int i=0;i
//回到流开头
ms.Seek(0,SeekOrigin.Begin);
//反序列化对象
obj=(formator.Deserialize(ms) as MyClass);
obj.Index+=i;
Console.WriteLine("对象{0}已处理",obj.Index);
}
}
2、反射
便于代码配置
strig IHelperConfig=ConfigurationManager.AppSettings["IHelperConfig"]
Assembly assembly=Assembly.load(IHelperConfig.split(',',0));//动态加载dll
Module[] modules=assembly.GetModules();//取dll所有命名空间
Type[] types=assembly.GetTypes()//取dll所有类
Type typeHelper=assembly.GetType(IHelperConfig)//根据命名空间.类名获取类名
object oHelper=Activator.CreateInstance(typeHelper,false)//根据类名获取对象实例,后面参数为是否调用private构造函数
IHelper iHelper=(IHelper)oHelper;//强转类型为继承的接口,以便调用接口方法
iHelper.Query();
public class SalserverHelper:IHelper{
public void Query(){
Console.WriteLine("1");
}
}
public interface IHelper{ //接口
void Query();
}
//获取方法
foreach(MethodInfo method in typeHelper.GetMethods()){
Console.WriteLine(method.Name);
}
MethodInfo Query=type.GetMethod("Query",new Type[]{typeof(int)});//根据具体方法名称
Query.Invoke(iHelper,null);
//调用私有方法
Methodinfo show1=type.GetMethod("Show1",BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic);//构造,公开,非公开
//属性赋值
foreach(var prop in type.GetProperties()){
if(prop.Name.Equals("ID")){
prop.SetValue(iHelper,12); //赋值
}
Console.WriteLine(prop.GetValue(iHelper)); //取值
}
例)
实体类
Public class user:BaseModel{
public string Name{set;get;};
public string ID{set;get;}
}
Public class DBHelper:IDBHelper{
Private static string ConnectionStringCustomers=ConfigurationManager.ConnectionStrings["Customs"].ConnectionString;
Public DBHelper(){
//构造函数
}
public T QueryDomain(){
Type type=typeof(T);
string colunms=string.Join(",",type.GetProperties().Select(p=>string.Format("[{0}]",p.Name)));
string sql=string.Format("select {0} from {1} where id={2}",colunms,type.Name,id);
using(SqlConnection conn=new SqlConnection(ConnectionStringCustomers)){
SqlCommand command=new SqlCommand(sql,conn)
conn.open();
SqlDataAdapter reader=command.ExcuteReader(CommandBehavior.CloseConnection)//自动关闭
if(reader.Read()){
foreach(var prop in type.GetProperties()){
string propertyName=prop.Name;
prop.SetValue(t,reader["propertyName"]);
}
}
}
}
}
2.1、抽象类,接口
抽象类 //类只能被继承一个
public abstract class BasePhone{//描述是什么
Public int Price{get;set;}
public void call(){}
public abstract void system(){}//由于存在未实现的方法,不能被实例化
}
接口 //接口能被实现多个
public interface IPhoneExtend{ //描述做什么
void Pay();
}
3、委托
委托与类同级
private delegate int MyDelegate(string id);声明委托
MyDelegate myDelegate=new MyDelegate (SayHi);//委托实例化
myDelegate+=SayHi;
myDelegate-=SayHi;//方法执行,但是只返回最后一次执行的值
myDelegate()或者myDelegate.Invoke//委托实例调用
委托逻辑分离,去耦合,便于拓展
pravite delegate string MyDelegate(string id);
public string SayHi(string a){
if(a=="1"){
return “逻辑1”
}else
return “逻辑2”
}
public string DelegateSayHi(string method,Delegate myDelegate){
myDelegate(method);
}
public string SayHi1(){
return “逻辑1”;
}
public string SayHi2(){
return “逻辑2”;
}
总结:代码增多,但去掉方法间关联,便于拓展
4、事件
public event EventHander Click;//事件为一种特殊委托
Click事件的响应方法必须符合EventHander委托的要求:
public delegate void EventHander(Object sender,EventArgs e)
form窗体中实现切换text焦点
foreach(Control ctl in groupBox1.Controls){
if(ctl is TextBox){
(ctl as TextBox).KeyDown+=this.EnterToTab;//委托挂接事件
}
}
private void EnterToTab(Object sender,EventArgs e){
TextBox txt=null;
if(e.KeyCode==Keys.Enter){
groupBox1.SelectNextControl(sender as Control,true, true, true, true);
txt=(sender as TextBox);
if(txt!=null){
txt.SelectAll();
}
}
}
protected override void onClick(EventArgs e){
base.OnClick(e);
ClickCount++;
//激发自定义事件
if(MyClick!=null){
MyClick(this,new MyClickEventArgs(ClickCount));
}
}
组件化开发
1、重用已有组件
2、开发部分新组件
3、组合新旧组件搭建新系统
代码风格
毛病
1、重复代码(同样的代码重复出现,不同代码完成同一功能)
2、过长函数
3、过大的类(明确各类的职责,一个类拆分多个独立的类,采用继承,公有代码移到基类)
4、过多的参数(改为传送对象)
5、原子弹的链式反应(类之间耦合性太强,类实现的功能太多,降低底层数据库变化对上层的影响)
6、曝光隐私(避免暴露内部数据结构,内部具体算法,仅公开必要职责)
7、乱起名字(命名要易于理解,劲量用英语,名字可以很长)
糟糕的设计
1、代码写死文件读取路径
2、UI控件设定底层数据源字段名
3、数据表新增字段,导致N处要修改
脆弱的设计
无法重用的设置
过于复杂的设计
DRY原则-老路不走两遍
KISS原则-绝不画蛇添足
KISS原则-简单即美
DDIY原则-不要一切自己动手
private void Init(){
InitUIControl();
setEventHandler();
}
private void InitUIControl(){//统一控件初始化
txtNum.Text="o";
}
private void setEventHandler(){//统一设置事件响应代码
EventHandler calculateEventHandler=()=>{Calculate();}
txtNum.TextChanged+=calculateEventHandler;
}
public void Calculate(){//公用算法放公共类
...
}
重构改进软件内部结构
软件测试(黑盒&&白盒)
单元测试,功能测试,集成测试,场景测试,系统测试,压力测试
基于职责设计类
命令方法和查询方法
5、Lambda表达式//匿名方法
public delegate void NoReturn(int x,int y);//无法返回值委托
public delegate int WithReturn(int x);//有返回值委托
public static void Show(){
1)无返回值委托
NoReturn method1=new NoReturn(
delegate(int x,int y)//匿名方法{
console.writeLine("delegate");
}
)
NoReturn method2=new NoReturn(
(int x,int y)=>{
console.writeLine("delegate");
}
)
NoReturn method3=new NoReturn(
( x, y)=>{//委托严格现在了取值类型
console.writeLine("delegate");
}
)
NoReturn method4=new NoReturn(
( x, y)=>console.writeLine("delegate")//如果方法体只有一行,可以去掉大括号和分号
)
2)有返回值委托
WithReturn method5=(x)=>{return 1;};
WithReturn method6=x=> 1;
Action act1=()=>{};//接收0到16个无返回值委托
Action act2=(x,y,z)=>Console.WriteLine("")
}
Func fun1=()=>1//接收0到16个带返回值委托
Funcfun2=(x,y,z)=>z.ToString();
集合查询拓展方法
筛选
IEnumerable nums=IEnumerable.Range(1,100);
var numList=num.Where((num)=>num%5==0);
投影与转换
var fileList=Directory.GetFiles("c:\\","*.*");
var files=fileList.Select(
files=>new FileInfo(file)); //找出C盘根目录所有文件,lambda表达式把string转换为FileInfo对象
var infos=files.Select(info=>new {FileName=info.Name,
FileLength=info.Length //取出FileInfo对象的文件名和文件大小属性
});
排序
OrderBy(升序)或OrderByDescending(降序)
var fileList=Directory.GetFiles("c:\\","*.*").Select(file=>new FileInfo(file))
.OrderByDescending(fileInfo=>fileInfo.Length);
综合(分页)
var pageData=dbContext.UserInfo.where(u=>u.ID>0)
.OrderByDescending(u=>u.ID)//降序
.Skip(5*(3-1))//一条5页,取第三页
.Take(5);
LINQ(表达式返回值实现了IQueryable)
//初始化了IQueryable接口里的三个参数
1、Linq表达式转换成Expression类
2、给Type ElementType赋值
3、给IQueryProvider Provider赋值,ef provider
当用到IQueryable接口集合数据的时候,provider解析Expression然后获取相应数据,进行遍历执行
//1.指定数据源
var number=new int[](0,1,2,3)
//2.创建查询表达式
var numQuery=
from num in numbers //from指定数据源
where (num%2)==0 //where指定筛选器
select num; //select指定返回元素类型
//执行查询
foreach(int num in numQuery){
Console.write("{0}",num);
}
技巧:
1、IEnumerable files=
from fileName in Directory.GetFiles("C:\\")
where File.GetLastWriteTime(fileName)
Path.GetExtension(fileName).ToUpper()==".TXT"//使用C#的运算符和方法
select new FileInfo(fileName);//动态创建新对象
2、混用LINQ与拓展方法
var methods=(from method in members
select method.Name).Distinct();
3、orderby
Enumerable files=
from fileName in Directory.GetFiles("C:\\")
orderby (new FileInfo(fileName)).Length,
fileName ascending
select fileName ;
4、使用部分数据
var files=from fileName in Directory.GetFiles("C:\\")
select new{
Name=fileName,
Length=new FileInfo(fileName).Length //封装成新的匿名对象 (只读)
}
5、引入范围变量暂存数据
Enumerable files=
from fileName in Directory.GetFiles("C:\\")
let file=new FileInfo(fileName)
orderby file.Length,
fileName ascending
select file ;
6、在LINQ中直接调用本地方法
var queryEvenNums=
from num in numbers
where IsEven(num)//C#方法
select num;
分组:
var studentQuery=from student in students
group student //分组中的单个数据key
by student,city //分组依据
foreach(var studentGroup in studentQuery){//第一层循环,遍历每个组
Console.WriteLine("在{0}学生清单:",studentGroup.Key);
int count=0;
foreach(Student stu in studentGroup ){//第二层循环,遍历组中元素
count++;
Console.WriteLine("{0}:{1}:{2}",count,stu.Name,stu.City);
}
}
//into字句对分组进一步处理
var wordGroups=
from w in words
group w by w[0] into grps//引用分组结果
where (grps.Key=='a'||grps.Key=='e'||grps.Key=='o')
select grps;
//实现非此即彼分组,多段分组
var studentQuery=from student in students
group student
by GroupKey(student.Scores) //分组方法
XML
XML文档的查询
使用XPath查询XML文档(例: /books/book[price>50]/price)
XML文档的转换
XML通过XSLT转换为HTML
LINQ to XML
XElemet elem=new XElemet("ElemetName ","ElemetValue")//新建
elem.SetAttributeValue("AttributeName","AttributeValue");//设置修改属性,删除为设置值为null
//XElement的Attributes方法可以获取所有属性集合
foreach(var attr in elem.Attributes()){}
构建xml树
1)XElement Grandparent=new XElement("GrandParent",
new XElement("Parent",
new XElement("Me",
new XElement("Son"),new XElement("Daughter"))));
new XAttribute("attributeName","attributeValue"),//添加属性
new XElement("Child","ChildValue")//添加子元素
);
3)string[] childs=new string[]{"child1","child2","child3"};
XElement elem=new XElement ("Parent",
from str in childs
select new XElement(str)
);
string xmlstr="";//字符串创建xml
XElement xmlTree=XElement .Parse(xmlstr);
xmlTree.Save("tree.xml")//保存xml文件
XElement xmlTree=XElement .Load("tree.xml")//装载xml
查询和访问xml中节点
var elems=from elem in xmlTree.Descendants()
let name=elem.Attribute("Name")
where Startswith(name,"M")
select elem;
修改xml树
XElement Root=new XElement("Root");
Root.Add(from dir in Directory.GetDirectories(@"c:\")
select new XElement("Dir"+(++counter),dir);
删除节点
var childToBeDeleted=new List();
foreach(var child in root.Elements()){
counter++;
if(counter%2!=0){
childToBeDeleted.Add(child);
}
}
childToBeDeleted.ForEach(node=>node.Remove());
PLINQ
var source=Enumerable.Range(1,100);
var evenNums=from num in source.AsParalle()
where num%2==0
select num;
AsSequential
var query=students.AsParallel().where(student=>student.Score>60)//使用并行查询,分数大于60
.OrderByDescending(stu=>stu.Score)//按成绩降序排序
.AsSequential()//强制转换为串行执行
.Select(studentInfo=>
new{
TempID=++counter,//名次
studentInfo.Name.
studentInfo.Score
}
);
AsOrdered
var parallelQuery=
from num in source.AsParallel.AsOrdered()
where num%2==0
select num;
var First10Numbers=parallelQuery.Take(10);
AsSequential()强制PLINQ查询以串行方式执行,而AsOrdered()仍是并行执行,只不过并行执行的结果先被缓存起来,然后再按原始数据顺序排序
Aggregate自定义聚合函数
PLINQ任务取消
CancellationTokenSource cts=new CancellationTokenSource();
//在另一个线程中取消操作
Task.Factory.StartNew(()=>{
CancelPLINQ(cts);
})
//在主线程中启动并行查询
int[] results=null;
try{
results=(from num in source.AsParallel()
.WithCancellation(cts.Token)
where num%3==0
orderby num descending
select num).ToArray();
}
catch(OperationCanceledException e){
Console.WriteLine(e.Message);
}
static void CancelPLINQ(CancellationTokenSource cts){
Random rand=new Random();
Thread.Sleep(rand.Next(150,350));
cts.Cancel();
}
6、异步
private void DoSomething(string name){
Stopwatch watch=new Stopwatch();
watch.Start();
Console.WriteLine("这里是name={0},当前线程ID={1}",name,Thread.CurrentThread.ManagedThreadId);
long Result=0;
for(int i=0;i
Result+=i;
}
Thread.Sleep(1000);
watch.Stop();
Console.WriteLine("这里是name={0},当前线程ID={1},计算结果{2},耗时{3}",name,Thread.CurrentThread.ManagedThreadId,Result,watch.ElapsedMilliseconds);
}
//同步
private void btnSync_Click(object sender,EcentArgs e){
console.WriteLine("****btnSync_Click******");
for(int i=0;i
string name=string.Format("btnSync_Click_{0}",i);
DoSomething(name);
}
console.WriteLine("****btnSync_Click******");
}
//异步 (主线不需等待子线)
private void btnAsync_Click(object sender,EcentArgs e){
console.WriteLine("**** btnAsync _Click******");
for(int i=0;i
string name=string.Format(" btnAsync _Click_{0}",i);
Action Ac= DoSomething ;
AsyncCallback Callback=>Console.WriteLine("这里是AsyncCallback,AsyncState={0} ",t.AsyncState),"xyz");
IAsyncResult result= Ac.BeginInvoke(name,t=>Console.WriteLine("这里是AsyncCallback,AsyncState={0} ",t.AsyncState));
}//回调
主线等待子线结束的方式
1、result.AsyncWaitHandle.WaitOne(-1);
2、while(!result.IsCompleted){
Thread.Sleep(100);
}
3、
method.EndInvoke(result);
console.WriteLine("**** btnAsync _Click******");
}
理解await的作用
1、static async void TestAsyncInvokeUseAwait(){
Console.WriteLine("DoLongJob方法正在后台执行...");
await DoLongJobUseTask();
Console.WriteLine("DoLongJob方法执行完毕");
2、static void TestAsyncInvokeUseTPL(){
Console.WriteLine("DoLongJob方法正在后台执行...");
Task.Run(()=>DoLongJob())
.ContinueWith(
(task)=>{Console.WriteLine("DoLongJob方法执行完毕");}
)
}
async与线程
7、多线程
//多线程
private void btnThreads_Click(object sender,EventArgs e){
console.WriteLine("****btnThreads_Click******");
List threadList=new List();
for(int i=0;i
Thread thread=new Thread(()=>TestThread("Threads"));
threadList.Add(thread);
thread.Start();
}
threadList.ForEach(t=>t.Join());
console.WriteLine("****btnThreads_Click******");
}
//线程池
private void btnThreadPool_Click(object sender,EventArgs e){
console.WriteLine("****btnThreadPool_Click******");
using(ManualResetEvent m1=new ManualResetEvent(false)){//计时
ThreadPool.QueueUserWorkItem(t=>TestThread("ThreadPool");m1.Set(););
ThreadPool.QueueUserWorkItem(t=>TestThread(t.ToString()),“ThreadPool”);
}
m1.WaitOne();//使用等待句柄阻止主进程终止
console.WriteLine("****btnThreadPool_Click******");
}
//Parallel并行执行
1、Parallel.Invoke(
()=>DoWork1(),
()=>DoWork2()
);
//并行循环
2、Parallel.For(0,100,(i)=>DoWork(i));
//Foreach
3、Parallel.ForEach(sourceCollection,item=>Process(item));
//Task
Task t=new Task(
()=>{//任务方法代码
}
);t.Start();
.Net4.5后推荐使用Task.Run()
Task t=Task.Run(
()=>{任务方法代码}
);
Task t=Task.Factory.StartNew(
()=>{任务方法代码}
)
Task.Delay(5000).Wait();
取回线程运行结果
1) Functiondel=(end)=>{
long sum=0;
for(int i=1;i
sum+=1;
return sum;
};
Task tsk=new Task(del,1000000);
tsk.Start();
Console.Write("程序运行结果为{0}", tsk.Result);
2)Action GetResult=(finishedTask)=>{
if(finishedTask.isFaulted){
Console.Write("程序运行发生异常:{0}", finishedTask.Exception);
}else{
Console.Write("程序运行结果为{0}", finishedTask.Result);
}
}//创建并行处理数据任务对象
Task tskProcess=new Task(ProcessData,1000000);
//数据处理结束时,字段启动下一个工作任务
Task tskGetResult=tskProcess.ContinueWith(GetResult);
//开始并行处理数据
tskProcess.Start();
Task之间的关联与相互协作
Task.Run(()=>DoStep1()).ContinueWith(
(PrevTask)=>DoStep2()
);
父子任务
Task tskParent=new Task(()=>{
//父任务完成的工作
//创建后续子任务并自动启动
Task.Run(...);
Task.Run(...);
})
//启动父任务
tskParent.Start();
父任务等待子任务结束再继续执行
方法一:Task tskParent=new Task(()=>
{
//父任务完成的工作
//创建后续子任务并自动启动
Task child1=Task.Run(()=>{
Task.Delay(1000).Wait();
});
Task child2=Task.Run(()=>{
Task.Delay(1000).Wait();
});
Task.WaitAll(child1,child2);
}
)
方法二:Task tskParent=Task.Factory.StartNew(()=>{
Task tskParent=Task.Factory.StartNew(()=>{
//父任务完成的工作
//创建后续子任务并自动启动
var child1=Task.Factory.StartNew(()=>{
Task.Delay(1000).Wait();
},TaskCreationOptions.AttachedToParent);
var child2=Task.Factory.StartNew(()=>{
Task.Delay(1000).Wait();
},TaskCreationOptions.AttachedToParent);
})
})
任务之间相互等待
Wait:等待单个任务的完成 例: Task t=Task.Run(...);...;t.Wait();
WaitAll:等待一组任务的全部完成
Task t1=Task.Run(...);Task t2=Task.Run(...);...;Task.WaitAll(t1,t2);
WaitAny:等待一组任务的任何一个任务完成
Task t1=Task.Run(...);Task t2=Task.Run(...);...;Task.WaitAny(t1,t2);
TaskFactory类提供:Task.Factory.ContinueWhenAll(任务数组,前期任务)
ContinueWhenAll,ContinueWhenAny
并行计算中异常处理
try{
Task taskobject=;
taskobject.Start();
taskobject.Wait();
}
catch(AggregateException ae){
//将多层的嵌套异常“展平”为一层
ae.Flatten();
foreach(Exception ex in ae.InnerExceptions){
...
}
}
线程中断取消
public Volatile bool IsCancelRequested=false;
Public void ThreadFunction(){
if(IsCancelRequested){
return;
}
}
线程统一取消模型
CancellationToken的常用属性和方法
ThrowlfCancellationRequested() 方法 检测到取消请求时,抛出OperationCanceledException
IsCancellationRequested 只读 外界是否提出取消请求
CancellationTokenSource的常用属性和方法
Cancell() 方法 外界调用表明“想取消操作”
Token 只读属性 共外界使用CancellationToken对象
IsCancellationRequested 只读属性 外界调用Cancel()方法后,此属性未true
取消模型示例代码
private CancellationToken _token;
public ThreadTFuncObject(CancellationToken token){
_token=token;
_token.Register(()=>{
//在此操作取消执行代码
})
}
public void DoWork(){
if(!_token.IsCancellationRequested ){
...
}
//创建取消令牌对象,传值给线程函数
var tokenSource=new CancellationTokenSource();
var obj=new ThreadFuncObject(tokenSource.Token);
//启动“可以取消”的线程
Thread th=new Thread(obj.DoWork);
th.Start();
//发出“取消”命令
tokenSource.Cancel();
Task取消
var cst=new CancellationTokenSource();
//创建一个关联了取消令牌的Task对象
Task tsk=new Task(taskFunction,cts.Token);
抛出TaskCanceledException即可将Task状态置为canceled
private void btnTask_Click(object sender,EventArgs e){
console.WriteLine("****btnTask_Click******");
List taskList=new List();
TaskFactory taskFactory=new TaskFactory();
for(int i=0;i
taskList.Add(taskFactory.StartNew(()=>TestThread("Task")));
}
taskFactory.ContinueWhenAll(taskList.ToArray(),t=>Console.WriteLine("****ContinueWhenAll******"););//所有线程完成执行
taskFactory.ContinueWhenAny(taskList.ToArray(),t=>Console.WriteLine("****ContinueWhenAny******"););//某个线程完成就执行
Task.WaitAll(taskList.ToArray());
Console.WriteLine("****btnTask_Click******");
}
Entity Framework
static async void ShowProducts(){//获取表单数据
using(var context=new NorthwindEntities){//创建context对象,NorthwindEntities为数据库对象
context.Database.Log=Console.WriteLine();//记录日志
int count=0;
var query=from p in context.Products
select p;//用拓展方法 var query=context.Products.Where(p=>p.ProductName.Length>1);
foreach(var product in query){
Console.WriteLine(product.ProductName);
}
}
}
数据新增
using(var context=new NorthwindEntities){//创建context对象,NorthwindEntities为数据库对象
{
User user=new User(){
...}
context.User.add(user);
//方法二
context.Entry(user).State=System.Data.EntityState.Added//获取入口
修改删除的时候指定ID
user.ID=8;
context.Entry(user).State=System.Data.EntityState.Modified//修改
context.User.Attach(user);//附加到上下文;
context.Entry(user).Property(u=>u.LoginName).IsModified=true;//修改某个列
context.SaveChanges();//变化存到数据库
}
异步查询
static async void ShowProductsUseAsync(){//获取表单数据
using(var context=new NorthwindEntities){//创建context对象,NorthwindEntities为数据库对象
await context.Products.ForEachAsync((product)=>{
Console.WriteLine(product.ProductName);
})
}
}
static void UseView(){
using(var context=new NorthwindEntities){
foreach(var product in context.Current_Product_Lists){
Console.WriteLine(product.ProductName);
}
}
}
static void useStoreProcedures(){//调用存储过程
using(var context=new NorthwindEntities){
Console.WriteLine("input product id:);
int id=int.Parse(Console.ReadLine());
var product=context.GetProduct(id).FirstOrDefault();
if(product!=null){
Console.WriteLine(product.ProductName);
}
}
}
}
EF延迟加载技术
IQueryable temp=from u in context.UserInfo
where u.UName.Contain("o")
select u;
1、用到的时候去查询数据
var temp2=from u in context.UserInfo
where u.ID>0
select u;
foreach(var userInfo in temp2){
}
2、大数据查询优化,大表单独查询,数据组合查询
使用临时表降低死锁概率
select * into ##Tb from UserInfo
Eager Loading 预装载
public async TestFetchFirstPersonUseInclude(){
using(var context=new MyDBEntities)()){
Person person=await context.People.Include("IdentityCard").FirstOrDefaultAsync();
if(person!=null){
Console.WriteLine(person.Name,person.IdentityCard);
}
}
}
协变 把一个子类的泛型集合赋值给父类的泛型集合。外部用的时候是父类泛型
List ListInt=new List(){1,2,3};
Listlistobj=ListInt;
逆变
CodeFirst适合改造原来用实体类的项目
UserInfo.cs页面
public class UserInfo(){
public UserInfo(){
OrderInfo=new HashSet();
}
[Key]
public int Id{get;set;}
public int SName{get;set;}
public int Age{get;set;}
public virtual ICollection OrderInfo{get;set;}
}
Program.cs页面//相关引用可通过新增空的EF数据实体,更改webconfig数据库配置
class Program{
static void Main(string[] args){
CodeFirstDbContext dbContext=new CodeFirstDbContext();//改为单例模式保证只能生成一个上下文对象
dbContext.Database.CreateIfNotExists();//若无数据库则新增
UserInfo userInfo=new UserInfo();
userInfo.SName="xyz";
userInfo.Age=10;
dbContext.UserInfo.Add(userInfo);
dbContext.SaveChanges();
}
}
CodeFirstDbContext.cs 上下文对象页面
public class CodeFirstDbContext:DbContext{
public CodeFirstDbContext():base("name=DataModelContainer"){//此为webconfig数据库链接对象
}
public DbSet UserInfo{set;get;};
}
8、设计模式
1、单例模式(类在整个进程中被实例化一次)(简化构造函数消耗,例链接池创建)
1)private static object Singleton_Lock=new object ();
private static Singleton _Singleton=null; //静态参数保证唯一
private long Result=0;
private Singleton(){//构造函数
for(int i=0;i
Result+=i;
}
Console.writeLine(Result);
}
public static Singleton CreateInstance(){
if(_Singleton==null){//是否初始化过
Lock(Singleton_Lock){//一开始线程都在这等待
if(_Singleton==null){//检查是否为空
_Singleton=new Singleton();
}}
return _Singleton;
}
2)public class Singleton { // 定义一个静态变量来保存类的实例 private static Singleton uniqueInstance; // 定义私有构造函数,使外界不能创建该类实例 private Singleton() { } public static Singleton GetInstance() { // 如果类的实例不存在则创建,否则直接返回 if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
对象销毁显示回收继承IDisposable接口
析构函数用于清理非托管资源
2、简单工厂
上端
class program
{
static void Main(string[]args){
//Human human=new Human();
//IRace human=new Human();
IRace human=Factory.CreateInstance(Race.Human);
}
}
public class Factory{
Public static IRace CreateInstance(RaceType raceType ){
switch(raceType ){
case RaceType.Human:
return new Human();
}
}
Public enum RaceType{
Human
}
}
Public interface IRace{
void ShowKing();
}
Public class Human:IRace{
public void ShowKing(){
Console.WriteLine("The King");
}
}
配置项优化
private static string RaceTypeConfig=ConfigurationManager.AppSettings["RaceTypeConfig"];
Public static IRace CreateInstance(RaceType raceType ){
switch((RaceType)Enum.Parse(typeof(RaceType),RaceTypeConfig) ){
case RaceType.Human:
return new Human();
}
反射优化
private static string RaceTypeReflect=ConfigurationManager.AppSettings["RaceTypeReflect"];
Public static IRace CreateInstance(RaceType raceType ){
string assemblyName=RaceTypeReflect.split(',')[0];
string TypeName=RaceTypeReflect.split(',')[1];
return (IRace)(Activator.CreateInstance(assemblyName,TypeName).Unwrap())
}
3、工厂方法
//解决工厂方法内部的高度耦合,拆分为多个类 (对拓展开放,对修改封闭)
IFactoryMethod humanFactory=new HumanFactory();
IRace human=HumanFactory.CreateInstance();
4、抽象工厂
//拆分为多个方法 工厂职责稳定才使用
使用HttpClient组件发出Get请求
static HttpClient getHttpClient(){
var client=new HttpClient();
client.BaseAddress=new Uri("http://localhost:12623");
client.DefaultRequestHeaders.Add("Accept","application/json");
return client
}
static async Task TestGetFromWebServer(){
Console.WriteLine("向web Server发出HTTP GET请求:\n");
var client=getHttpClient();
Console.WriteLine("收到服务器发回的数据");
client.DefaultRequestHeaders.Add("my-header","my-header value");//自定义head添加
string result=await client.GetStringAsync("api/myservice");
string searchWhat="中国人 abc";
string searchUrl=$"api/myservice/search?value={searchWhat}";
string result1=await client.GetStringAsync(searchUrl);//包含查询字符串的请求
Console.WriteLine(result);
}
响应HTTP请求
[HttpGet]
[Route("headers")]//可通过Request.Headers获取Headers
public IActionResult Get()}{
Response.StatusCode=200;
return Json(new MyClass(){
Info="Web Server发回的Json格式信息";
Id=100
})
}
包含查询字符串的响应
[HttpGet]
[Route("[action]")]
public IActionResult Search(string value)}{
Response.StatusCode=200;
return Json(new {
message="正在搜索:"+value;
})
}
向客户端Post数据
var response=await client.PostAsJsonAsync("api/myService",data);
response.EnsureSuccessStatusCode();
var result=await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
服务端接收Post数据
[HttpPost]
public IactionResult Post([FromBody]MyClass obj){//[FromBody]指明冲Http请求的Body提取数据并实例化-*/
return ok(obj)
}
MongoDB(NoSQL)
DatabaseDatabase
CollectionTable
DocumentRow
{Key:value}Column
db.foo.save({id:1,value:"Hello,World"});//数据插入覆盖
db.foo.insert({id:1,value:"Hello,World"}); //插入
db.foo.insert({id:2,value:1}); //插入
db.foo.update((_id:2).($inc:(value:1)))//$inc相当于+=
db.foo.update((_id:2).($set:(x:1)))//set新增属性,unset移除属性
show collections
db.foo.find({_id:($lt:10)},{_id:1,value:1}).sort({id:-1});//查询语句,$lt小于显示id小于10的数据,sort排序
db.foo.remove({})//删除数据
db.foo.drop();//删除collections
ASP.Net Core MVC
使用Html辅助方法和TagHelper生成网页链接
@Html.ActionLink("Homepage","Index","Home",new{One=1,Two=2},new {@class="btn btn-defualt"});//要显示的文本,Action,Controller,传参数,样式
@Html.ActionLink("在本Controller内跳转","hashcode");
@Html.ActionLink("生成查询字符串","para","new{Name="张三"}");
@Html.ActionLink("跳转到另一个控制器的Action","index","mySimplest");
TagHelper生成跳转到另一个控制器Action链接
使用条件,project.json中添加“Microsoft.AspNetCore.Mvc.TagHelpers”
视图中添加@addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers
处理管线
public void Configure(...){
app.use(async(context)=>{//lambda表达式定义的中间件
context.Response.ContentType="text/html;charset=utf-8";
context.Item["info"]=$"1";//中间件间传值
await context.Response.WriteAsync($"
");
await next();//调用下一个中间件
});
app.Run(async(content)=>{
await context.Response.WriteAsync($"
");
})
}
通过Map实现分支管线
public ConfigureMapping(... app){//修改环境变量为Mapping
app.Map("/branch",ConstrunctBranchPipeline)
app.Run(asyn context=>{
await context.Response.WriteAsync($"
");
})
}
通过MapWhen实现分支管线
public ConfigureMapWhen(... app){
app.MapWhen(context=>{
return context.Response.Query.ContainsKey("branch");
}
,ConstrunctBranchPipeline)
app.Run(asyn context=>{
await context.Response.WriteAsync($"
");
})
}
依赖注入与loC容器
ASP.NET core中能被loC容器创建的对象称为服务对象(Service)
Startup类中ConfigureService()方法完成对象的注册工作
pulic interface IDateTime{//接口
DateTime Now{get;}
}
pulic class SystemDateTime:IDatetTime{//实现接口的类
public DateTime Now{
get{return DateTime.Now;}
}
}
public void ConfigureServices(IServiceCollection services){
services.AddMvc();
services.AddTransient();
}
构造方法注入设计控制器
public class HomeController:Controller{
Private readonly IDateTime _dateTime;
public HomeController(IDateTime dateTime){
_dateTime=dateTime;
}
}
[FromService]方法注入
public IActionResult About([FromService]IDateTime dateTime){
ViewData["Message"]="当前时间:"+dateTime.Now
return View();
}
视图注入
@inject DenpendecyInejctofController.IdateTime dateTimeObj
依赖注入对象的生命周期
Transient:每次刷新,总得到两个全新的对象
Scope:每次刷新,只得到一个对象,但与上次刷新的对象不同
Singleton:每次刷新,只访问相同的对象,由loC容器创建
让ASP.NET core项目支持静态文件访问
1、在project.json中添加项目依赖 "Microsoft.AspNetCore.StaticFiles"
2、向处理管线中添加中间件
public void Configure(...){
...
app.UseStaticFiles();
...
}
路由系统
1、依据Http请求的URL确定应该创建哪个Controller的实例,并调用哪个Action
2、提供一些辅助方法用于生产Web页面中用户可点击的链接
1、在Startup类的Configure()方法中定义的“全局性”的路由
public Configure(... app){
app.UseDeveloperExceptionPage();
app.UseStaticFiles();//支持静态文件访问
app.UseMvc(routes=>{
routes.MapRoute("Default","{controller=Home}/{action=Index}/{id?}");//等于号赋默认值,?表示可有可无
});
app.Run(asyn context=>{
await context.Response.WriteAsync($"
");
})
}
2、以Attribute形式定义的“局部性”路由
[Route("api/[controller]")]
public class MyServiceController:Controller{
[HttpGet("[Action]/{value:int:min(100)}/{argu}")]//value为参数名
public string GetValue(int value,int argu){
return $"value={value}";
}
}
对应的URL:http://网站名称/api/myservice/GetValue/105 //控制器实例/方法名/参数
Action参数绑定
public class MyPoint{
public double x{get;set;}
public double y{get;set;}
}
[HttpGet("point")]
public IActionResult GetPoint(MyPoint point){
return ok(point);
}
示例:http://localhost:9303/api/argument/point?x=100&y=200
Action的返回值
1、返回void的Action
[HttpGet("void")]
public void testReturnVoid(){
}
2、返回IActionResult的Action
[HttpGet("json")]
public void IActionResult GetJson(){
Response.StatusCode=200;//设置状态码为200
return Json({name="xyz",age=40});//若状态码为200,则可用Ok()方法生成Json字符串
}
3、返回集合对象的Action
[HttpGet("mydata")]
public IEnumerable testotherReturnType(){
return MyData.CreateMyDataobjects();//默认情况下,ASP.NET core会将集合对象序列化为Json数组
}
4、返回HTML的Action
[HttpGet("html")]
public IActionResult testHtml(){
Response.ContentType="text/html";
return Content("
");
}
编写异步Action
耗时较长,访问网络和磁盘,I/O密集操作的
设计视图,Razor引擎
ViewData、ViewBag、ViewModel(Action to View)
ViewData:
public IActionResult useViewData(){
ViewData["CompanyName"]="百度";
}
@ViewData["CompanyName"]
ViewBag:
public IActionResult useViewData(){
ViewBag.CompanyName="百度";
}
@ViewBag.CompanyName
ViewModel:
public class informatonViewModel(){
[Display(Name="一个Int值")]
public int IntValue{get;set;}
public string StringValue{get;set;}
public DateTime datetime{get;set;}
}
public IActionResult useViewData(){
var info=new informatonViewModel(){
IntValue=1,
StringValue="Hello",
datetime=DateTime.Now
};
return View(info);
}
@model informatonExchange.informatonViewModel
@Model.IntValue
模型绑定机制(Action接收外部传入的数据)
@Html.ActionLink("Homepage","Index","Home",new{One=1,Two=2},new {@class="btn btn-defualt"});//要显示的文本,Action,Controller,传参数,样式
Post方式提交信息(定义两个Action,一个响应Get,一个响应Post)
[HttpGet]
public IActionResult GetAndPost(){
var vm=new MyViewModel(){//设置初始值
Value=1,
Information="初始化对象",
};
return View(vm);
}
public IActionResult GetAndPost(MyViewModel model){
return View("GetAndPostResult",model);//传到视图GetAndPostResult
}
TempData与Session
TempData(重定向后数据不丢失)
跳转时将数据保存到Session,进入控制器从Session移除
TempData["user"]="xyz"
启用Session的条件
project.js中添加"Microsoft.AsoNETCore.Session"
在Startup.cs中配置Session
public void ConfigureServices(){
service.AddMvc();
service.AddSession();
}
public void Configure(){
app.UseSession();
app.UseMvcWithDefualtRoute();
}
Action中通过HttpContext.Session.GetInt32("value")获取数据,HttpContext.Session.SetInt32("value",0)保存数据
服务端数据验证
Controller中的数据验证
public class ExpressionInfo(){
[Required(ErrorMessage="必须输入一个操作数")]
[Display(Name="第一个操作数")]
public int Operand1{get;set;}
[Required(ErrorMessage="必须输入一个操作数")]
[Display(Name="第二个操作数")]
public int Operand2{get;set;}
[Required(ErrorMessage="必须选择一个操作类型")]
[Display(Name="选择操作类型")]
public string MathOperator{get;set;}
}
public ActionResult Calculate(){
Listoperators=new List{"加","减","乘","除"};
ViewBag.operators=new SelectList(operators,"加");
return View();
}
@using(Html.BeginForm()){//生成Html的
元素
@Html.ValidationSummary(true)//生成一个
,汇总那些与单个属性值无关的数据验证信息
输入表达式信息
@Html.LabelFor(model=>model.Operand1)
@Html.EditorFor(model=>model.Operand1)
@Html.ValidationMessageFor(model=>model.Operand1)
@Html.DropDownList("MathOperator",
(IEnumerable)ViewBag.operators)
}
[HttpPost]
public IActionResult Calculate(ExpressionInfo express){
if(ModelState.IsValid){//数据验证
ViewBag.result=expr+result;
return View("CalculateResult");
}
return View();
}
MVC实现CRUD
使用Bower安装前端框架
MVC(直接访问类下面的某个方法)
M:Model模型,存储并处理用户请求的数据
V:View视图,显示数据接受用户请求
C:Controller控制器,获取用户的请求,处理请求
如何请求?在B/S结构用户请求Server的唯一途径就是URL
请求什么?请求一个服务,对于程序来讲就是请求一个或多个功能。对于C#来讲就是请求类中的一个方法
类名和方法在url上怎么体现? http://localhost:8080/类名(Controller)/方法名(Action)
不是所有的类都能访问(控制器)
1、类名必须以Controller结尾 2、必须继承Controller抽象类 3、类必须是Public的
不是所有方法都能访问(Action)
1、方法必须是Public的 2、方法不能加持性([NonAction]) 3、方法不能重载 4、重载的方法需要通过特性来区分([HttpGet],[HttpPost],[NoAction],[ActionName("reg")]等等)
控制器默认代码分析
控制新建的默认代码
View()方法是什么?怎么用?
View方法是从Controller类继承下来的方法,有8个重载
1、View():返回与Action名相投的视图文件(文件搜索路径,/Views/Account/UserList.aspx,/Views/Account/UserList.ascx,/Views/Shared/UserList.aspx,/Views/Shared/UserList.ascx,/Views/Account/UserList.cshtml,/Views/Account/UserList.vbhtml,/Views/Shared/UserList.cshtml,/Views/Shared/UserList.vbhtml),假设控制器为Accont,Action为UserListView()等价于View("UserList")文件名搜索顺序如右侧
2、View(String)呈现指定的视图文件
3、View(Object)文件查找同上,将Model传递到页面
4、View(IView)呈现自定义视图
5、View(String,Object)在指定页面传递Model对象
6、View(String,String)在视图页面室友母版页,第二个参数为母版页路径
7、View(Iview,Object)在自定义视图中传递Model对象
8、View(String,String,Object)是在使用母版页的视图中传Model对象
ActionResult是什么
1、System.Web.Mvc.ContentResult:返回一串字符串
例:return Content("ASP.NET MVC")
2、System.Web.Mvc.EmptyResult:返回空内容
例:return new EmptyResult()
3、System.Web.Mvc.FileResult 返回一个文件,应用做验证码
1)return new FileContentResult(new byte[1024],"image/jpeg")
例:Image img=Image.FromFile(Server.MapPath("~/Image/logo.gif"));
MemoryStream ms=new MemoryStream();
img.Save(ms,System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] data=ms.GetBuffer();
return new FileContentResult(data,"image/jpeg")
2)return new FilePathResult("logo.jpg","image/jpeg");
3)return new FileStreamResult(new FileStream("logo.jpg",FileMode.Open),"image/jpeg")
例:FileStream fs=new FileStream(Server.MapPath("~/Image/logo.gif"),FileMode.Open)
return new FileStreamResult(fs,"image/jpeg")
4、System.Web.Mvc.HttpUnauthorizedResult:未经授权的请求结果
例:return new HttpUnauthorizedResult();
5、System.Web.Mvc.JavaScriptResult:返回JS脚本
例:return JavaScript("");
6、Ststem.Web.Mvc.JsonResult:返回Json格式,常用于Ajax的应用
例:return Json(new {Name="jack",Age=20},JsonRequestBehavior.AllowGet)
7、System.Web.Mvc.RedirectResult:重定向站内或站外
例:return Redirect("url");
8、System.Web.Mvc.RedirectToRouteResult:站内请求
例:return RedirectToAction("Login");//根据控制器和Action
return RedirectToRoute("Default");根据路由名称
9、System.Web.Mvc.PartialViewResult:部分视图
例:return PartialView("~/Views/UserControl/UserLogin.ascx")
ULR匹配模式
ULR是如何匹配的?
根据键入的URL在路由列表搜索,如果没有匹配的项则返回404错误
路由信息在哪里添加?
在APP_Start目录下的RouteConfig.cs文件添加
routes.MapRoute()方法有什么用?怎么用?
routes.MapRoute(
name:"Default";//必须唯一,可以为null
url:"{controller}/{action}/{id}";
defaults:new {controller="Home",action="index",id=UrlParameter.Optional,age=20}
constraints:new {age=@"[1-9]|\d{2}"};
namespaces:new string[]{"BookShopMVC.Controllers"}
)
1、占位符controller和action必须有
2、url中要特别注意空格
3、分隔符:/表示路径的分隔符,分隔Url不同的信息
4、特殊的匹配符:*匹配剩余的部分
例:URL:“{controller}/{action}/{*name}”
url为:Account/Register/a/b/c 则name=a/b/c
贪婪匹配:优先匹配完前面的占位符所有可能性,再匹配后面的占位符
defaults默认值:
例1:url:"{controller}/{action}/{*name}-{age}",
defaults:new {controller="Home",action="index",name="jack",age=20} 默认值无效
例2:url:"{controller}/{action}/{*name}-{age}/{addr}",
defaults:new {controller="Home",action="index",name="jack",age=20,addr="wuhan"} 仅addr为默认值
客户端数据获取
1、表单数据
Request.Form["name"]
2、URL上的数据
Request.QueryString["name"]
3、路由数据
object obj=RouteData.Values["name"]
4、方法上的参数
public ActionResult Register(string name),可映射为表单元素,URL参数,路由数据,如果有name有冲突,优先级顺序为:表单元素>路由数据>URL参数
控制器数据如何传递到视图
ViewBag和ViewData(两者处理的是一个数据源,作用于ViewResult指向的视图)
ViewBag是dynamic类型
ViewBag.data="ASP";
@ViewBag.data
ViewData是Object类型
ViewData["data"]="JSP"
例:return Redirect("/Home/Login")//重定向ViewData丢失
return RedirectToAction("Login")//转发到另一个Action
TempData(重定向后数据不丢失)
跳转时将数据保存到Session,进入控制器从Session移除
TempData["user"]="xyz"
强类型视图
1、控制器参数为Model类型
通过泛型类ViewPage指定Model类型
ViewData.Model=类;
1)Model为一个对象
1、只显示不能给Model赋值,StudentName为属性名2、 适用于表单元素,能够赋值给Model,StudentName为属性名,不区分大小写3、 适用于表单元素,能够赋值给Model,stu为Action参数名,StudentName为属性名,不区分大小写
4、建议前缀统一,要么统一带前缀,要么都不带
2)Model为一个集合
1、通过索引获取对象,以下案例使用索引0获取集合的第一个对象
2、已get方式第一次请求时Model为null,所以要非空判断
或
3、直接使用索引或通过方法参数名带索引,两种方式建议统一使用第一种,因为不能互相访问各自设置的数据
4、建议:属性前缀要统一,要么都带前缀,要么都不带
数据绑定(了解)
1、Bind设置Model绑定的方式
Exclude:不进行模型映射的操作
Include:进行模型映射的操作
Prefix:Model使用的前缀
表单数据验证
Model类型数据上编写
Inherits="System.Web.Mvc.ViewPage"
1、引入组件System.ComponentModel.DataAnnotations
2、引入命名空间using System.ComponentModel.DataAnnotations;
3、在属性上,使用特性编写验证规则
1)Required(必填信息)
[Require(ErrorMessage="请输入学生姓名")]
2)Compare(比较验证,与其他的属性进行比较)
[Compare("PassWord2")]
3)StringLength:字符串的长度验证
[StringLength(5)]
4)Range(数字,整数,浮点,时间等数据范围)
[Range(10,20)]
5)RegularExpression(正在表达式验证)
[RegularExpression(@"\d{2}")]
6)EmailAddress(Email格式)
4、在UI上使用验证规则
1)使用强类型视图模型
Inherits="System.Web.Mvc.ViewPage"
2)引用样式Content/Site.css
3)在要验证的控件后面添加显示指定属性的错误信息
4)建议:使用
样式更丰富,表单数据不丢失
5、控制器对Model的处理
1、Action方法上参数传入Model数据
Public ActionResult Register(Student stu)
2、业务操作前判断Model数据是有效
if(ModelState.IsValid)
1)ModelState是模型状态信息
2)可以使用ModelState添加业务逻辑错误信息
ModelState.AddModelError("errPwd","密码错误")
3)在客户端使用显示错误信息
3、返回Model
return View("~/Views/Home/Index.aspx",stu)