C#7 入门实用指南(二)
原文:
zh.annas-archive.org/md5/0D2F44FACA4630D8785DF55498F3E611译者:飞龙
第十章:使用 C#和 LINQ 以及自定义数据类型
在本章中,我们将讨论如何使用 LINQ 与自定义类型。
在 HTML 中添加一个显示人员的按钮
打开一个项目。转到Default.aspx,并在以<form id=...开头的行下面放置一个按钮。要做到这一点,转到工具箱,获取一个Button控件,并将其拖放到那里。更改按钮上的文本以显示“显示人员”:
<asp:Button ID="Button1" runat="server" Text="Show People" />
设置数据库
我们将有一个数据库,我们将对其进行查询,并且我们将展示那些,例如,名字中有某个字母的人,赚取一定数量的钱,并以某种方式进行排序。
为了实现这一点,转到设计视图,并双击“显示人员”按钮。这将带我们进入Default.aspx.cs。删除Page_Load块。该项目的起始代码的相关部分应该看起来像图 10.5.1:
图 10.5.1:该项目的起始代码部分
在下一阶段,首先转到文件顶部,并在using System之后输入以下内容:
using System.Linq;
接下来,我们将创建一个类。我们将其称为Person。因此,在以public partial class...开头的行上面插入以下内容:
public class Person
使用 LINQ 制作自定义类型
现在,在上一行下面的花括号集之间,您将声明两个自动属性,如下所示:
public string Name { get; set; }
public decimal Salary { get; set; }
然后,为了创建一个构造函数,请在这些行下面输入以下内容:
public Person(string name, decimal salary)
接下来,您将在构造函数内设置属性的值。因此,请在以下这些行下面的一组花括号之间输入以下内容:
Name = name; Salary = salary;
这是我们简单的自定义类型Person,具有两个自动属性和一个带参数的构造函数。
设置一个人员数组
在下一阶段,您将创建一个人员数组;请在以下以protected void Button1_Click....开头的行下面的一组花括号之间输入以下内容:
Person[] people = new Person[] { new Person("John", 76877), new Person("Bobby", 78988), new Person("Joan", 87656) };
查询数组
现在,要查询这个,请在此行下面输入以下内容:
IEnumerable<Person> peopleWithN = people.Where(per => per.Name.EndsWith("n")).OrderByDescending(per => per.Salary);
当您输入时,注意到IEnumerable不会显示出来,因此您必须再次转到文件顶部,并在using System.Linq之后输入以下内容:
using System.Collections.Generic;
现在让我们在下面使用它;因此,在以Person[] people...开头的行下面,输入前面提到的IEnumerable<Person>...行。
在这里,Person是可以从人员列表中枚举的对象类型。peopleWithN表示我们将搜索名字中有字母n的人。实际上,代码搜索名字以n结尾的人。(请注意,per代表列表中的每个人。)此外,我们按工资降序排序。
因为人们有时会不一致地输入信息,所以您首先必须将所有内容转换为等效的情况,但这是您自己要解决的问题。
请记住,在这一行中,我们有people,这是某种对象的名称,以及Where,一个扩展方法,后跟一个 Lambda。接下来,我们使用OrderByDescending,您可以从方法列表中选择它,以按降序排序值,例如人的工资。
因此,此行的目的是选择每个名字以n结尾的人,然后按工资排序结果。这将产生一个IEnumerable对象,现在您当然可以逐步进行,并在下一行中说以下内容:
foreach(Person p in peopleWithN)
现在,为了打印所有内容,请在此行下面的一组花括号之间输入以下内容:
sampLabel.Text += $"<br>{p.Name} {p.Salary:C}";
在这里,我们首先放置了Name变量,然后是格式化为货币的Salary变量。
运行程序
这是我们程序的核心。在浏览器中启动它。单击“显示人员”按钮,结果将显示如图 10.5.2所示:
图 10.5.2:运行程序的结果
所以,琼赚了76,877.00。他们被选中是因为他们的名字都以小写字母n结尾,正如您所看到的,然后按工资降序排序。所以,它的运行结果符合预期。正如您所看到的,您还可以使用 LINQ 定义自定义类型,比如在public class Person下面的花括号中。它非常强大并且运行良好。
章节复习
为了复习,包括注释在内的本章的Default.aspx.cs文件的完整版本如下所示:
//using is a directive
//System is a name space
//name space is a collection of features that our needs to run
using System;
using System.Linq;
using System.Collections.Generic;
//public means accessible anywhere
//partial means this class is split over multiple files
//class is a keyword and think of it as the outermost level of grouping
//:System.Web.UI.Page means our page inherits the features of a Page
public class Person
{
public string Name { get; set; } //auto implemented properties
public decimal Salary { get; set; }
public Person(string name, decimal salary)
{
Name = name; Salary = salary;//set values of properties
}
}
public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
//make array of people
Person[] people = new Person[] { new Person("John", 76877),
new Person("Bobby",78988),
new Person("Joan", 87656) };
//find all people with "n" as the last letter, and then display
//the results sorted from high to low salary
IEnumerable<Person> peopleWithN =
people.Where(per => per.Name.EndsWith("n")).OrderByDescending
(per => per.Salary);
//display name and salary formatted as currency
foreach (Person p in peopleWithN)
{
sampLabel.Text += $"<br>{p.Name} {p.Salary:C}";
}
}
}
摘要
在本章中,我们讨论了如何将 LINQ 与自定义类型一起使用。您设置了一个数据库,使用 LINQ 创建了一个自定义类型,设置了一个人员数组,并对数组进行了查询。
在下一章中,您将学习如何使用查询语法编写查询。
第十一章:使用查询语法构造查询
在本章中,您将学习如何使用查询语法编写查询,例如方法链接,就像我们以前做过的那样。
向 HTML 添加显示按钮
打开一个项目,唯一放入中的是一个按钮,没有其他内容。为此,请转到工具箱,获取一个Button控件,并将其拖放到以<form id=...开头的行下方。将按钮上的文本替换为显示:
<asp:Button ID="Button1" runat="server" Text="Show" />
现在,切换到设计视图,并双击显示按钮。这将带我们进入Default.aspx.cs。删除事件处理存根。此项目的起始代码的相关部分应如图 11.6.1所示:
图 11.6.1:此项目的起始代码部分
接下来,转到文件顶部,在using System下输入以下内容:
using System.Collections.Generic;
using System.Linq;
为了利用这一点,我们将做如下操作。这是例行代码;这是机械的。首先,当有人单击显示按钮时,您希望创建一个标签,以便始终有一个累积输出。为此,请在以protected void Button1_Click...开头的行下的大括号之间输入以下内容:
sampLabel.Text = "";
创建一个 decimal 工资数组
接下来,在上一行下面,您将创建一个名为salaries的decimal数组,自然而然地。因此,输入以下内容:
decimal[] salaries = new decimal[] { 56789, 78888, 35555, 34533, 75000 };
这就是您可以查询decimal数组的方法。这是一个特定的decimal数组,但基本上可以是任何数组。我们加入了一些值,就这样。
使用范围变量
接下来,在此行下面输入以下内容:
IEnumerable<string> salResults = from salary in salaries
请注意,返回或结果集将是string类型,而不是decimal类型。在salResults =之后,您希望定义 LINQ 查询的主体,因此您说from salary in salaries。如果您将鼠标悬停在此处的salary上,您会看到它被称为范围变量,如图 11.6.2所示。因此,您要求它查看salaries。作为范围变量,它是逐个遍历所有条目的数量。
图 11.6.2:范围变量
选择工资范围并按降序排列
现在,您将指定某种逻辑条件。例如,以某种方式过滤结果。因此,在此行下缩进输入以下内容:
where 35000 <= salary && salary <= 75000
select $"<br>{salary:C}";
接下来,您可以对结果集进行orderby,以按降序列出工资,例如;因此,请直接在此行下面输入以下内容:
orderby salary descending
默认情况下是按升序排列的,从小到大,您希望将其反转。当您加入descending关键字时,它就会从大到小排列。
接下来,记住目标是获得一个填充有字符串的IEnumerable构造。因此,最后,请在此代码块的一部分中输入以下内容:
select $"<br>{salary:C}";
您还可以在原地格式化结果,就像我在这一行中所做的那样,例如,以货币格式。
显示结果
有了这个代码块,当然,下一阶段是迭代并显示结果。为此,您可以进行转换为列表并打印,或者您可以接下来做以下操作:
foreach(string formattedSalary in salResults)
我们怎么知道在这一行中应该说string?记住,前面的IEnumerable行填充有字符串,对吗?如果您将鼠标悬停在IEnumerable上,它会说IEnumerable<out T>,而T是字符串。
现在,为了显示结果,请在上一行下面的一对大括号之间输入以下内容:
sampLabel.Text += formattedSalary;
在这里,formattedSalary是要显示的内容。
现在,在浏览器中打开此内容。单击显示按钮。结果将显示如图 11.6.3所示:
图 11.6.3:运行程序的初始结果
这里的工资按降序排列,从高到低,并且在 75000 美元到 35000 美元的范围内。所以,这正如预期的那样工作。
观察延迟执行
现在,你应该知道的一件事是所谓的延迟执行的概念。所以,为了了解这意味着什么,看看接下来的内容。
想象一下,我在foreach(string formattedSalary in salResults)行右边放了一个断点。然后,从调试菜单中选择单步执行,并点击显示按钮。注意每一行是如何连续运行的(每行后面都会显示毫秒数)。你应该看到它是如何进入的;它是如何运行的。
你应该注意的一件事是 LINQ 的延迟执行的概念,这意味着salResults实际上是运行的,正如你所看到的,所以它本质上是一个查询变量。它在你使用foreach循环迭代时运行,就像下面的代码块中所示的那样,而不是在你在前面的IEnumerable块中编写时运行。它不会在那时运行。它会在你迭代它时运行。否则,你的程序可能会在这些查询结果中携带大量信息。所以,这就是延迟执行的内容:
IEnumerable<string> salResults = from salary in salaries
where 35000 <= salary && salary <= 75000
orderby salary descending
select $"<br>{salary:C}";
foreach(string formattedSalary in salResults)
{
//display formatted salaries one at a time
sampLabel.Text += formattedSalary;
}
在下一阶段,我们将看一个可能能做的另一个实际例子。我们还想显示水平线,所以在sampLabel.Text += formattedSalary行下面的闭合大括号之后,输入以下内容:
sampLabel.Text += "<br><hr/>";
<hr/>标签将在输出中添加一条水平线。
创建一个字典
接下来,我们将创建一个Dictionary;为此,请在下面输入以下行:
Dictionary<string, decimal> nameSalaries = new Dictionary<string, decimal>();
在这里,<string,decimal>代表键值对。
处理键值对
现在,让我们添加一些键值对。所以,从输入以下内容开始:
nameSalaries.Add("John Jones", 45355);
在这一行中,John Jones是键,值是他的薪水,或者$45,355。
然后,你可以重复这个几次,所以直接在它下面再粘贴这一行三次。比如,约翰·史密斯,76900;约翰·詹金斯,89000;史蒂夫·乔布斯,98000:
nameSalaries.Add("John Smith", 76900);
nameSalaries.Add("John Jenkins", 89000);
nameSalaries.Add("Steve Jobs", 98000);
请注意,我在这里重复了名字约翰几次,因为我想简要说明一个概念。最后一个列出的是史蒂夫·乔布斯,当然他的薪水远远超过了 98000!
查询键值对中的数据
现在,我们将再次查询这个。这是我们在键值对中拥有的数据,我们将对其进行查询。所以,在这些行下面输入以下内容:
var dictResults = from nameSalary in nameSalaries
在这里,nameSalary是一个范围变量,它指的是约翰·琼斯、约翰·史密斯、约翰·詹金斯、史蒂夫·乔布斯等等,而nameSalaries是字典本身。nameSalary是键和值的特定组合。
然后,在这一行下面,缩进以下代码:
where nameSalary.Key.Contains("John") && nameSalary.Value >= 65000
在这里,我们说键包含名字约翰,薪水大于或等于$65,000。如果你愿意,你可以添加OrderBy等等,但对于我们的目的,直接在这一行下面输入以下内容:
select $"<br>{nameSalary.Key} earns {nameSalary.Value:C} per year.";
在这一行中,我们将选择那些符合这两个条件的记录:姓名是约翰,薪水超过$65,000。所以,在我们的情况下,肯定是约翰·史密斯和约翰·詹金斯。为了使输出看起来好看,我们说nameSalary.Value:C以货币格式化,然后添加per year。
现在,将鼠标悬停在dictResults上。你看到弹出提示中说IEnumerable吗?现在,var被称为隐式类型。我们之前见过var。有时很难从像我们创建的这样的查询中判断输出会是什么,因为它足够复杂。所以,如果你使用隐式类型,它会告诉你输出应该是什么,所以是string类型的IEnumerable。现在,如果你愿意,你可以将这一行改为:
IEnumerable<string> dictResults = from nameSalary in nameSalaries
现在我们也知道这一点,因为在这个查询的末尾,你看到了字符串,对吧?这些是包含格式化信息的字符串,当然,你可以像往常一样迭代它们;所以,接下来输入以下内容:
foreach(string nameSal in dictResults)
然后,在这一行下面的一对大括号之间,以以下内容结束:
sampLabel.Text += nameSal;
运行程序
在浏览器中运行这个,确保它按预期工作。点击显示按钮。结果显示在图 11.6.4中:
图 11.6.4:运行我们程序的结果
现在你已经得到了我之前描述的第一组结果,第二组结果显示在它们下面,显示两个名字都包含 John,金额为$65,000 或更多。
章节回顾
作为回顾,包括注释的本章Default.aspx.cs文件的完整版本如下所示:
//using is a directive
//System is a name space
//name space is a collection of features that our needs to run
using System;
using System.Collections.Generic;
using System.Linq;
//public means accessible anywhere
//partial means this class is split over multiple files
//class is a keyword and think of it as the outermost level of grouping
//:System.Web.UI.Page means our page inherits the features of a Page
public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
//clear label on button click
sampLabel.Text = "";
//make array of salaries
decimal[] salaries =
new decimal[] { 56789, 78888, 35555, 34533, 75000 };
//construct Linq query, which produces a collection of
//formatted strings
IEnumerable<string> salResults = from salary in salaries
where 35000 <= salary && salary <= 75000
orderby salary descending
select $"<br>{salary:C}";
foreach(string formattedSalary in salResults)
{
//display formatted salaries one at a time
sampLabel.Text += formattedSalary;
}
//show horizontal rule on screen
sampLabel.Text += "<br><hr/>";
//make dictionary to hold names and salaries as key/value pairs
Dictionary<string, decimal> nameSalaries =
new Dictionary<string, decimal>();
nameSalaries.Add("John Jones", 45355);
nameSalaries.Add("John Smith", 76900);
nameSalaries.Add("John Jenkins", 89000);
nameSalaries.Add("Steve Jobs", 98000);
//query below represents all people named John who make 65000
//and more
//this query gives back a formatted string for each key/value
//pair that
//satisfies the condition
IEnumerable<string> dictResults = from nameSalary in nameSalaries
where nameSalary.Key.Contains("John") &&
nameSalary.Value >= 65000
select $"<br>{nameSalary.Key} earns
{nameSalary.Value:C} per year.";
foreach(string nameSal in dictResults)
{
sampLabel.Text += nameSal;//display named and salaries
}
}
}
总结
在本章中,你学会了如何使用查询语法编写查询。你创建了一个十进制薪水数组,使用了范围变量,观察了延迟执行,创建了一个字典,使用了键值对,查询了键值对中的数据,并学习了隐式类型。
在下一章中,我们将进一步探讨 LINQ。具体来说,我们将研究 LINQ 的一些强大功能,如平均、求和和计数等聚合函数。此外,我们还将讨论列表的列表,这是非常实用的东西。
第十二章:执行聚合函数的查询
在本章中,我们将进一步探讨 LINQ。具体来说,我们将看看 LINQ 执行聚合函数的能力,比如平均值、求和、计数等。此外,我们还将讨论列表的列表,这是一个非常实用的东西。
在 HTML 中添加显示按钮
打开项目,并且为了保持简洁,我们将在以<form id=....开头的行下面放一个按钮。要做到这一点,去工具箱,拖动一个Button控件,并把它拖到那里。将按钮上的文本更改为Show。
现在,切换到设计视图,并双击显示按钮。这将带我们进入Default.aspx.cs。删除Page_Load块。我们不需要那个。这个项目的起始代码的相关部分应该看起来像图 12.7.1:
图 12.7.1:这个项目的起始代码部分
在下一阶段,转到文件顶部,在using System下面输入以下内容:
using System.Collections.Generic;
using System.Linq;
创建一个数组
在本章中有很多要输入的代码,但这是机械的。首先,我们将创建一个数组,所以在以protected void Button1_Click...开头的行的大括号之间输入以下内容:
IEnumerable<int> scores = new int[] { 45, 98, 99, 78, 89, 87, 77, 67, 71, 81 };
在这里,IEnumerable是数据类型,scores是数组的名称。你放入数组的值并不重要。
对列表中的值求平均值
现在,首先我们将找到这个列表的平均值。所以,输入以下内容:
var goodStudentAverage = (from score in scores where score >= 90 select score).Average();
我们将选择得分为90或以上的学生。想象一下,这些是几个学生的学期成绩。因此,在前一行中,我们说得分是>=90,选择那个分数。这是一个你可以在一行中写的查询。在这个上下文中,score是范围变量,scores是数组,选择的条件是where score=>90。然后,你输入.(点)Average()来平均整个东西。换句话说,这样写的方式是,括号中的查询将运行,然后平均数组中的值。如果你将鼠标悬停在这一行的var上,你会看到它说double,因为,如果你将鼠标悬停在Average上,你也会看到它返回一个double。因此,这个Average()函数作用于IEnumerable类型的列表,但它返回一个double数据类型给我们。
显示结果
现在,你当然可以显示结果,因为记住它只是一个单一的数值,一个聚合数量。你现在可以在这一行下面说以下内容:
sampLabel.Text = $"<br>The average for great students is {goodStudentAverage}";
使用 Count 函数
现在,如果你愿意,你也可以使用Count函数,所以你可以说下面的内容:
var averageStudentCount = scores.Where(grade =>70 <= grade && grade <80).Count();
在以var开头的前一行中,我们在单行中使用了查询语法,或者内联查询语法,因为我们使用了from和where。现在,我们可以使用方法链接和其中的 Lambda 表达式来表达相同的事情。所以,这里我们说scores.Where,然后我们说grade是这样的,即70 <=grade,但grade <80。因此,我们正在定义那些得分在70和80之间的人,不包括80的分数,并且我们将它们标记为平均学生。然后我们将Count它们。这将告诉我们有多少这样的人,然后我们可以显示那个数字。例如,你可以输入以下内容:
sampLabel.Text += $"<br>There are {averageStudentCount} average students.";
记住,averageStudentCount产生一个数字,所以,例如,结果可能是,有 25 个平均学生。
使用列表的列表
现在,这个概念的一个非常现实的应用可能是有一个列表的列表。首先输入以下内容:
List<int> firstStudent = new List<int> { 90, 89, 92 };
想象一下,您有一个学生firstStudent。然后,他或她有一些成绩,所以您创建了整数的new List,然后在一对大括号中初始化了这个列表。因此,按照所示的方式添加一些值。(请注意,我输入的值在90 +/-范围内。)这是您可以以前未见过的方式初始化列表。
现在,让我们再为另一个学生做一个整数的列表。为此,输入以下内容以为secondStudent创建new List。同样,使用另一组值初始化此列表。(请注意,在这一行中,我将输入的值在80 +/-范围内。)现在,当您有一个完整的班级时,您将有这样的列表,对吧?这是因为您有多个学生在一个班级中:
List<int> secondStudent = new List<int> { 78, 81, 79};
因此,现在您可以创建构造函数。接下来输入以下内容:
List<List<int>> classList = new List<List<int>>();
向 classList 添加学生
在这里,我们有一个整数的列表列表——您可以在其他列表中嵌入列表。然后,我们会说,例如,classList列表等于一个新的列表列表。要初始化此列表,您可以使用Add。在下一行,输入以下内容:
classList.Add(firstStudent);
classList.Add(secondStudent);
这是如何将第一个学生、第二个学生等添加到班级列表中的方法。
总结 classList 中的信息
在下一阶段,您希望能够获得一些有用的信息。例如,想象一下您有这样一个列表的列表,您希望进行总结。因此,接下来输入以下内容:
var avgPerStudent = classList.Select(student => student.Average());
现在,以avgPerStudent为例,表示平均学生分数。现在,在输入classList.Select()之后,要选择的数量是代表每个学生的列表,由(student => student.Average())捕获。现在,请确保您理解student参数是什么。在这里,您选择一个学生并平均他们的成绩。将鼠标悬停在student上,您会看到该数量代表与第一个学生对应的整数列表。然后,student.Average表示对该学生进行平均,然后对下一个学生重复此过程。如果将鼠标悬停在var上,您会看到在这种情况下返回的是IEnumerable类型。您可以迭代这些值。要做到这一点,您将输入以下内容:
foreach(var studentAvg in avgPerStudent)
现在,在此行下方,输入以下内容以在一对大括号中显示结果:
sampLabel.Text += $"<br>Average grade={studentAvg}";
运行程序
现在,构建此程序并在浏览器中运行它。单击“显示”按钮:
图 12.7.2:运行我们程序的结果
现在这些是一些专业的结果。优秀学生的平均分是 98.5。有三个平均学生。两个列表的扩展平均成绩显示在最后。
因此,您学到了更多关于 LINQ 的知识——Average函数和Count函数,还学会了如何制作一个列表的列表。您可以使用Select等语句对这些列表进行操作,然后可以嵌入 Lambda 表达式以单独处理列表中的每个列表。
章节回顾
回顾一下,包括注释在内的本章的Default.aspx.cs文件的完整版本如下所示:
//using is a directive
//System is a name space
//name space is a collection of features that our needs to run
using System;
using System.Collections.Generic;
using System.Linq;
//public means accessible anywhere
//partial means this class is split over multiple files
//class is a keyword and think of it as the outermost level of grouping
//:System.Web.UI.Page means our page inherits the features of a Page
public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
IEnumerable<int> scores =
new int[] { 45, 98, 99, 78, 89, 87, 77, 67, 71, 81 };
//array of integers
//line 17 below selects all scores 90 or above, and averages them,
//giving back a double value
var goodStudentAverage = (from score in scores where score >= 90 select score).Average();
//line 19 below displays the average
sampLabel.Text = $"<br>The average for great students is {goodStudentAverage}";
//line 21 below selects all students below 70 and 80,
//and counts them
var averageStudentCount = scores.Where(grade => 70 <= grade && grade < 80).Count();
//line 23 below displays the student count
sampLabel.Text += $"<br>There are {averageStudentCount} average students.";
//lines 25 and 26 create two new lists with initializer lists
List<int> firstStudent = new List<int> {90,89,92};
List<int> secondStudent = new List<int> { 78, 81, 79 };
//line 28 creates a list of lists
List<List<int>> classList = new List<List<int>>();
classList.Add(firstStudent);
classList.Add(secondStudent);
//line 32 below find the average for each list, and
//stores the averages
//so avgPerStudent is of type IEnumerable
var avgPerStudent = classList.Select(student => student.Average());
//lines 35-38 display the averages
foreach(var studentAvg in avgPerStudent)
{
sampLabel.Text += $"<br>Average grade={studentAvg}";
}
}
}
摘要
在本章中,我们进一步探讨了 LINQ。具体来说,我们研究了 LINQ 执行聚合函数(如平均、求和和计数)的能力。此外,我们还讨论了列表的列表。您对列表中的值进行了平均,使用了Count函数,处理了列表的列表,向classList添加了学生,并总结了classList中的信息。
在下一章中,您将学习关于元组的知识,它们基本上是包含多个值的集合。
第十三章:使用 LINQ 对元组进行总结
在本章中,您将了解元组。这些基本上是几个值的集合。
在 HTML 中添加显示元组摘要值按钮
打开一个项目,并在以<form id=....开头的行下放置一个按钮。将按钮文本替换为显示元组摘要值。
现在,切换到设计视图,并双击显示元组摘要值按钮。这将带我们进入Default.aspx.cs。删除Page_Load块。该项目的起始代码的相关部分应该看起来像图 13.8.1:
图 13.8.1:该项目的起始代码部分
介绍元组
现在,首先我们将创建一个返回元组值的函数。那么,什么是元组?让我们定义它们。正如我之前所说,它基本上是几个值的集合。现在,在 C#中,这意味着您将在以public partial class...开头的行下的封闭大括号下方输入以下内容:
private static Tuple<double, double, double, double> SummarizeList(List<double> listDoubles)
在前一行中,Tuple是一个类。然后,要定义元组存储的值的数量,请记住我们与向量的工作。我们向向量添加了两个或三个值。这是一个类似的概念。如果您将鼠标悬停在Tuple上,它会说Tuple 表示 n 元组,其中n是八或更多,因此 T1、T2、T3,一直到 TRest。哇,所以您可以创建八个或更多的元组!
添加命名空间
在我们的案例中,我们放置了<double, double, double, double>。所以,这是一个可以容纳四个值的元组。请注意,当您输入时,List<double>没有显示,因此您需要添加一些命名空间。在文件顶部的using System下,输入以下内容:
using System.Collections.Generic;
using System.Linq;
在这里,我们使用了通用集合和 LINQ,现在List<double>显示为高亮显示,应该称为listDoubles。
使用元组制作列表
在过程的下一个阶段,您将创建此列表。因此,在以下行的大括号之间输入以下内容:
Tuple<double, double, double, double> summary = Tuple.Create(listDoubles.Sum(),listDoubles.Average(),listDoubles.Max(), listDoubles.Min());
要形成元组,您说Tuple.Create(listDoubles.Sum()。Tuple是类的名称,该类内的成员之一是Create函数,因此选择它。现在,我们可以创建一个具有四个条目的元组。接下来,我们说listDoubles.Sum()。请注意,当您输入Sum时,它是一个扩展方法。如果您删除Sum,您会注意到Linq变为灰色。这再次确认了为什么需要Linq——用于Sum函数。
这个元组中的第一个条目是列表的总和。记住,我们正在调用summary。所以,这就像是列表中条目的统计摘要。除了listDoubles.Sum(),您当然也可以有其他一些。您可以有一个平均值,listDoubles.Average(),您还可以添加listDoubles.Max()和listDoubles.Min()。
返回元组
最后,您可以返回元组。为此,请在以下行下输入以下内容:
return summary;
在您之前写的第一行中,记住private表示只有在那里可访问,static表示它在类级别上运行,这意味着您可以直接使用名称调用SummarizeList——您不需要将其放在对象上。
现在,在这种特殊情况下,它将返回这个结构,Tuple<double, double, double, double>,称为元组,在这里只是一种存储四个双精度值的方式。然后,要为第一个条目创建一个元组,您使用 LINQ。然后您使用 LINQ 来获取第二个条目,LINQ 来获取第三个条目,最后,LINQ 来获取第四个条目。因此,Sum,Min,Max和Average都是扩展方法,然后您将其return。
创建一个双精度列表
现在,对于下一阶段,看一下按钮单击事件。这里的代码非常简单。首先,在以protected void Button1_Click...开头的行下的大括号内输入以下内容。您将创建一个名为lst的双精度列表,如下所示:
List<double> lst = new List<double> { 1, 2, 5, 68, 899, 1, -989, 0.1143, 98, 2553 };
在new List of double值之后,通过在大括号中添加一些数字来指定初始化程序——不管它们是什么,对吧?放一些负数、一些小数、一些整数等等。
总结列表
接下来,我们将调用SummarizeList。因此,请在此行下面输入以下内容:
var results = SummarizeList(lst);
在这种情况下,老实说,var很容易,对吧?如果你不使用它,你将不得不输入Tuple<double, double, double, double>,这将是数据类型。换句话说,那真的很啰嗦,而且占用了很多空间。所以,请记住,var表示隐式数据类型,但它足够聪明,知道数据类型是什么。
显示结果
然后一旦你返回它,你可以去那里的项目商店。因此,你可以输入以下内容:
sampLabel.Text = $"Sum={results.Item1}";
在下一阶段,复制这行并直接粘贴到下面。编辑Average函数的文本如下:
sampLabel.Text += $"<br>Average={results.Item2}";
确保你调用这些行的方式与函数对应;所以,Sum、Average、Max和Min。再次复制上一行并直接粘贴到下面,这样你就不必追加了。由于下一个是为Max,编辑文本如下:
sampLabel.Text += $"<br>Max={results.Item3}";
这将是Item3和你可以提取的元组。
最后,让我们再做一次。因此,再次复制上一行并直接粘贴到下面。由于最后一个是为Min,编辑文本如下:
sampLabel.Text += $"<br>Min={results.Item4}";
当然,这是Item4和你可以提取的元组。
运行程序
现在,让我们在浏览器中加速。点击显示元组摘要值按钮。结果显示在图 13.8.2中:
图 13.8.2:运行本章程序的结果
你看到了 Sum、Average、Max 和 Min,所以它的工作符合预期。
现在,作为对此更现实的扩展,想象一个元组列表。你肯定可以做到,所以你可以在最后一行下面添加类似以下内容:
List<Tuple<string, double, double, decimal>>;
你可以有一个元组列表。每个元组代表,例如,关于一个人的信息,然后你会有一个人的列表。这是让你思考的事情:如何构建它并为自己做一个项目。然而,这些都是基础。
章节回顾
回顾一下,包括注释的本章Default.aspx.cs的完整版本在以下代码块中显示:
//using is a directive
//System is a name space
//name space is a collection of features that our needs to run
using System;
using System.Collections.Generic;
using System.Linq;
//public means accessible anywhere
//partial means this class is split over multiple files
//class is a keyword and think of it as the outermost level of grouping
//:System.Web.UI.Page means our page inherits the features of a Page
public partial class _Default : System.Web.UI.Page
{
private static Tuple<double, double, double , double> SummarizeList(List<double> listDoubles)
{
Tuple<double, double, double, double> summary =
Tuple.Create(listDoubles.Sum(),
listDoubles.Average(), listDoubles.Max(), listDoubles.Min());
return summary;
}
protected void Button1_Click(object sender, EventArgs e)
{
List<double> lst =
new List<double> { 1, 2, 5, 68, 899, 1, -989, 0.1143, 98, 2553 };
var results = SummarizeList(lst);
sampLabel.Text = $"Sum={results.Item1}";
sampLabel.Text += $"<br>Average={results.Item2}";
sampLabel.Text += $"<br>Max={results.Item3}";
sampLabel.Text += $"<br>Min={results.Item4}";
}
}
总结
在本章中,你学习了关于元组的知识,它基本上是几个值的集合。你创建了一个带有元组的列表,返回了元组,并对列表进行了总结。
在下一章中,我们将讨论使用 LINQ 来对相关结果进行分组。分组是在数据库中对结果进行分类的基本操作。
第十四章:用分组总结结果
在本章中,我们将讨论使用 LINQ 对相关结果进行分组。分组是在数据库中对结果进行分类的基本操作。
在 HTML 中添加一个显示结果按钮
打开一个项目。首先,我们将在 HTML 中放置一个按钮,上面写着“显示结果”;要做到这一点,在以<form id=....开头的行下面放置一个按钮。
<asp:Button ID="Button1" runat="server" Text="Show Results" /<br />
接下来,切换到设计视图,并双击“显示结果”按钮。这将带我们进入Default.aspx.cs。删除Page_Load块。这个项目的起始代码的相关部分应该看起来像图 14.9.1:
图 14.9.1:这个项目的起始代码部分
添加命名空间
首先,我们需要添加一些命名空间。要做到这一点,在文件顶部的using System下面输入以下内容:
using System.Linq;
using System.Collections.Generic;
创建学生类并定义字段
接下来,我们将创建一个名为Student的类。在以public partial class _Default...开头的行之上输入以下内容:
public class Student
接下来,要定义字段,在这一行下面的一对大括号之间输入以下内容:
public string Name { get; set; }
这里有一些属性,然后让我们再添加一个。在这一行下面输入以下内容:
public List<int> Grades;
在这里,List<int>是学生的成绩,让我们称之为Grades。
制作学生名单
现在,在下一个阶段,我们将制作一个学生名单。要做到这一点,首先在以protected void Button1_Click...开头的行之后的一对大括号之间输入以下内容:
List<Student> students = new List<Student>
在这里,students是列表的名称。然后我们有一个新的学生列表。接下来,为了初始化列表,我们将把所有新学生放在这一行下面的一对大括号之间,开始如下:
new Student {Name="Smith, John", Grades=new List<int> {78,98,67,87 } },
在前一行的new Student之后,你需要在一对大括号中分别放入每个学生的所有信息。首先,你需要定义Name的值,所以你将其设置为Smith,例如,John,插入一个逗号,然后在一个新的整数列表中放入 John 的Grades,将这些值设置为78,98,67和87。
接下来,我们需要为其他学生重复几次这个过程;所以,复制这一行,然后粘贴到下面。编辑这一行,将Name变量的值更改为Adams,Amy,然后将成绩更改为91,99,89和93:
new Student {Name="Adams, Amy", Grades=new List<int> {91,99,89,93 } },
这种编码水平非常实用和现实。在编码了五年之后,我可以告诉你,事情总是更有趣和更具挑战性。
现在,再重复一次这个过程。复制前面的行,然后粘贴到下面。编辑这一行,将Name变量的值更改为Smith,Mary,然后将成绩更改为89,87,84和88。确保在最后一个new Student类的下一行插入一个闭合的大括号和一个分号:
new Student {Name="Smith, Mary", Grades=new List<int> {89,87,84,88 }};
分组名称
同样,因为我们想要按姓和名分组,所以我使用了两个相同的姓。我们将以姓的首字母和名字的顺序进行分组显示结果。
接下来,我们将编写 LINQ 查询来完成分组。同样,这可以以更复杂的方式完成,但这是一个相对简单的例子。所以,在列表中最后一个new Student类的下一行输入以下内容:
var groupsByFirstLetters = from student in students group student by student.Name[0];
记住,groupsByFirstLetters表示姓的第一个字母。所以,要编写查询,你说fromstudent在students中,然后在下一行你group学生按student.Name分组。因为Name是一个字符串,你可以通过使用方括号提取第一个字符,然后在字符串中获取索引为0的值。这就是你可以写的原因。否则,它会显得有点神秘。
显示分组结果
现在,要以分组的方式显示结果,你必须使用嵌套的foreach循环。所以,接下来输入以下内容:
foreach(var studentGroup in groupsByFirstLetters)
在这里,情况变得更有趣。如果你将鼠标悬停在var上,它会告诉你var代表什么。它说,它是一个字符和学生的分组。它代表具有共同键的对象集合。
现在,我们可以按以下方式使用它。在前一行下面的一对大括号之间输入以下内容:
sampLabel.Text += $"<br>{studentGroup.Key}";
首先,我们想显示键,也就是每个姓氏的第一个字母,然后所有内容将在该姓氏的第一个字母下进行总结。所以,我们说studentGroup.Key。这里有一个叫做Key的属性,它是每个组的分组键。请记住,这里我们是按姓氏的第一个字母进行分组。所以,键就是那个数量。
接下来,一旦你修复了该组内的第一个字母,通常会有几个学生或几个项目,对吧?所以,现在你需要逐个显示这些项目。在下面输入以下内容:
foreach(var st in studentGroup)
注意一下关于foreach循环嵌套的情况。你看到了吗,在foreach (var studentGroup in groupsByFirstLetters)这一行中,外部的for循环获取了studentGroup变量,然后该组的键通过sampLabel.Text += $"<br>{studentGroup.Key}"这一行显示出来了?接下来,你将遍历每个组内的学生。这就是为什么在下一阶段,如果你将鼠标悬停在前一行的var上,你会看到它显示student st在studentGroup中。这就是细节。
接下来,为了显示它,输入以下内容在前面foreach行的大括号中:
sampLabel.Text += $"<br>{st.Name}";
这就是核心。现在记住,我们从一个叫做Student的类开始。然后我们有一个学生列表。请注意,在学生列表中,你也可以使用一种语法,即属性的名称,然后是属性的值,不需要括号。你可以直接使用大括号在学生列表的定义中创建对象。
以var groupsByFirstLetters...开头的代码块为我们分组。然后我们需要外部循环foreach (var studentGroup...来显示每个组的键。然后内部的foreach循环foreach (var st in studentGroup)显示该组内的学生。所以,这两个循环是必需的,它们有不同的作用。
现在在浏览器中运行一下,看看结果。点击“显示结果”按钮。如你所见,在图 14.9.2中,你有字母 S,这是第一组的键,组内有 Smith, John 和 Smith, Mary。接下来,你有字母 A,这是第二组的键,组内有 Adams, Amy:
图 14.9.2:运行本章程序的结果
当然,这些可以进行排序,还可以做各种其他操作。但这些只是基础知识。所以,你看到这里可以做什么;还有许多更复杂的事情是可能的。
章节回顾
回顾一下,包括注释在内的本章Default.aspx.cs文件的完整版本如下所示:
//using is a directive
//System is a name space
//name space is a collection of features that our needs to run
using System;
using System.Linq;
using System.Collections.Generic;
//public means accessible anywhere
//partial means this class is split over multiple files
//class is a keyword and think of it as the outermost level of grouping
//:System.Web.UI.Page means our page inherits the features of a Page
public class Student //define student class
{
public string Name { get; set; }
public List<int> Grades;
}
public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
//create list of students
List<Student> students = new List<Student>
{
new Student {Name="Smith, John",
Grades=new List<int> {78,98,67,87}},
new Student {Name="Adams, Amy",
Grades=new List<int> {91,99,89,93}},
new Student {Name="Smith, Mary",
Grades=new List<int> {89,87,84,88}}
};
//create query that groups students by first letter of last name
//Name is a string, so student.Name[0] means grab the
//first character for grouping
var groupsByFirstLetters =
from student in students group student by student.Name[0];
//the outer loop is needed to display the "Key",
//which is the first letter for each group
foreach(var studentGroup in groupsByFirstLetters)
{
sampLabel.Text += $"<br>{studentGroup.Key}";
//the inner loop is needed to display the students
//within each group
foreach(var st in studentGroup)
{
sampLabel.Text += $"<br>{st.Name}";
}
}
}
}
总结
在本章中,我们讨论了使用 LINQ 对相关结果进行分组。你创建了一个学生类并定义了字段,创建了一个学生列表,对名字进行了分组,最后显示了分组结果。
在下一章中,你将学习如何使用 LINQ 编写查询,以连接不同的结果集或不同的数据集。
第十五章:使用内连接加入数据集
在本章中,您将学习如何使用 LINQ 编写查询,以连接不同的结果集或不同的数据集。本章的代码并不是很复杂,只有一点点。
在 HTML 中添加一个连接类的按钮
打开一个项目。在 HTML 页面中放置一个按钮,上面写着连接类,放在以<form id=....开头的行下面。因此,我们将有两个不同的类,然后将它们连接在一起,产生一些结果,然后显示它们。这是目标:
<asp:Button ID="Button1" runat="server" Text="Join Classes" />
接下来,切换到设计视图,并双击连接类按钮。这将带我们进入Default.aspx.cs。删除Page_Load块。该项目的起始代码的相关部分应如图 15.10.1所示:
图 15.10.1:该项目的起始代码部分
添加命名空间
现在,我们将编写以下代码。我们需要 LINQ 和泛型集合命名空间;因此,在文件顶部的using System下面输入以下内容:
using System.Linq;
using System.Collections.Generic;
创建人员和汽车类
我们将创建两个类。一个是person,另一个是car类。为此,请在以public partial class _Default...开头的行的正上方直接输入以下内容:
public class Person
现在,我们只需要一个名字;因此,在此行下面的一对大括号之间输入以下内容:
public string Name { get; set; }
然后,我们还需要创建一个名为Car的类。因此,在前一行下面的封闭大括号下面输入以下内容:
public class Car
接下来,在此行下面的一对大括号之间输入以下内容:
public Person Owner { get; set; }
如您现在所见,public Person 被定义为类内字段的数据类型。例如,一辆车有一个所有者。
现在,在前一行下面添加另一个数据类型,如下所示:
public string Maker { get; set; }
显然,您可以看到Car类内有Person字段。这些类之间存在连接。我们很快就会用到这个。现在,让我们来构建。
创建人员对象
首先,我们必须创建一些Person对象,否则我们将没有任何东西可以连接。因此,在以protected void Button1_Click...开头的行下面的一对大括号之间输入以下内容:
Person per1 = new Person() { Name = "Mark Owens" };
现在,复制此行并将其直接粘贴到下面。编辑该行,将其更改为Person per2,并将Name变量的值更改为Jenny Smith:
Person per2 = new Person() { Name = "Jenny Smith" };
最后,复制前面的行并将其粘贴到下面。编辑该行,将其更改为Person per3,并将Name变量的值更改为John Jenkins:
Person per3 = new Person() { Name = "John Jenkins" };
所以,现在我们有一些人将成为汽车的所有者。
创建汽车对象
现在,让我们创建一些car对象。跳过一行,然后输入以下内容:
Car car1 = new Car() { Owner = per1, Maker = "Honda" };
要初始化car1,您可以从Owner = per1开始。这建立了两个类之间的连接;也就是说,car1的所有者是per1,即Mark Owens。然后,您添加制造商,我们将说car1的制造商是Honda。
再次复制此行,并将其直接粘贴到前一行下面。编辑该行,将其更改为Car car2,所有者更改为per2,但制造商保持为Honda:
Car car2 = new Car() { Owner = per2, Maker = "Honda" };
有时,不幸的是,为了阐明一个概念,我必须写相当多的代码,否则很难阐明这个概念。
再次复制前面的行并将其粘贴到下面。编辑该行,将其更改为Car car3,Owner更改为per1,但这次将Maker更改为Toyota:
Car car3 = new Car() { Owner = per1, Maker = "Toyota" };
最后,复制前面的行并将其粘贴到下面。编辑该行,将其更改为Car car4,Owner更改为per4,并将Maker更改为Tesla:
Car car4 = new Car() { Owner = per2, Maker = "Tesla" };
当然,要注意的是,per3变量并未被用作汽车的所有者,对吧?因此,当我们进行连接时,连接这两个数据集的查询将返回共享的记录。这意味着,例如,没有一辆车是由per3拥有的。
创建所有者及其汽车的列表
接下来,跳过一行,输入以下内容:
List<Person> people = new List<Person> { per1, per2, per3 };
在这里,我们说一个人的列表,people,等于一个新的人的列表,然后,我们把这些个体——per1,per2和per3放进去。接下来,你要对汽车做同样的事情,所以输入以下内容:
List<Car> cars = new List<Car> { car1, car2, car3, car4 };
再次,要初始化汽车列表,你说car1,car2,car3和car4。
连接所有者和汽车列表
现在,你可以连接这些列表。要做到这一点,略过一行,然后输入以下内容:
var carsWithOwners = from person in people
对于有所有者的汽车,你可以编写查询:from person in people。接下来,继续输入以下内容:
join car in cars on person equals car.Owner
在这里,我们正在连接这两个列表。我们使用person来join people,并将其设置为car.Owner。然后,一旦它们连接起来,拥有汽车的人基本上,你可以接着说以下内容:
select new{ OwnerName = person.Name, CarMake = car.Maker };
在这一行中创建了一个匿名类型。因此,如果你将鼠标悬停在var上,它会说 T 是'a。这是一个匿名数据类型。因此,carsWithOwners基本上是一个匿名类型的列表,但因为它是一个列表,而且是IEnumerable,你可以使用foreach循环逐步进行遍历。
获取并显示结果
现在我们需要获取结果。所以,略过一行,说以下内容:
foreach(var ownedCar in carsWithOwners)
接下来,在这条线下面的花括号之间输入以下内容:
sampLabel.Text += $"<br>Owner={ownedCar.OwnerName} Car Make={ownedCar.CarMake}";
这将为我们显示结果。
运行程序
现在在浏览器中打开这个,然后点击连接类按钮。看一下结果,也显示在图 15.10.2中:
图 15.10.2:这个项目的结果
所以,马克·欧文斯有两辆车。接下来,珍妮·史密斯有一辆本田和一辆特斯拉。对吗?
现在,因为约翰·詹金斯是per3,他不会出现在汽车列表中作为所有者。这意味着per3和Car列表之间没有连接。换句话说,在 LINQ 中进行连接时,会使用per1,因为它是按所有者Car.Owner进行的。因此,将使用per1和per2,但不会使用per3。然后,显示结果。
章节回顾
回顾一下,包括注释在内的本章的Default.aspx.cs文件的完整版本如下所示:
//using is a directive
//System is a name space
//name space is a collection of features that our needs to run
using System;
using System.Linq;
using System.Collections.Generic;
//public means accessible anywhere
//partial means this class is split over multiple files
//class is a keyword and think of it as the outermost level of grouping
//:System.Web.UI.Page means our page inherits the features of a Page
public class Person
{
//define Person class
public string Name { get; set; }
}
public class Car
{
//define Car class, using field of type Person
public Person Owner { get; set; }
public string Maker { get; set; }
}
public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
//make three new people
Person per1 = new Person() { Name = "Mark Owens" };
Person per2 = new Person() { Name = "Jenny Smith" };
Person per3 = new Person() { Name = "John Jenkins" };
//make four new cars
Car car1 = new Car() { Owner = per1, Maker = "Honda" };
Car car2 = new Car() { Owner = per2, Maker = "Honda" };
Car car3 = new Car() { Owner = per1, Maker = "Toyota" };
Car car4 = new Car() { Owner = per2, Maker = "Tesla" };
//make lists of people and cars
List<Person> people = new List<Person> { per1, per2, per3 };
List<Car> cars = new List<Car> { car1, car2, car3, car4 };
//use linq to write a query that joins the two lists by car Owner
//here, the type of var is an enumerable list of anonymous
//data types
var carsWithOwners = from person in people join car in cars on person equals car.Owner
select new { OwnerName = person.Name, CarMake = car.Maker };
//foreach loops iterates over carsWithOwners
foreach(var ownedCar in carsWithOwners)
{
sampLabel.Text += $"<br>Owner={ownedCar.OwnerName} Car Make= {ownedCar.CarMake}";
}
}
}
总结
在本章中,你学会了如何使用 LINQ 编写查询,连接不同的结果集或不同的数据集。你创建了Person和Car类,制作了Person和Car对象,制作了所有者及其汽车的列表,并连接了所有者和汽车列表。
在下一章中,你将使用 SQL Server 2017 Express。
第十六章:下载、安装和运行 SQL Server 2017
在本章中,您将下载、安装和运行 SQL Server 2017 Express。
下载 SQL Server 2017 Express
单击以下链接,将您带到可以下载 SQL Server 2017 Express 的网站,如图 16.1.1所示:
www.microsoft.com/en-us/sql-server/sql-server-editions-express
图 16.1.1:SQL Server 2017 Express 版下载屏幕
接下来,单击“立即下载”按钮。下载完成后,双击SQL Server2017-SSEI-Expr.exe。在用户账户控制屏幕上选择“是”。
选择安装类型
接下来,您需要选择安装类型,如图 16.1.2所示。选择基本安装并接受许可条款协议:
图 16.1.2:从安装类型屏幕中选择基本安装
安装程序
接下来,要么接受默认的安装位置,要么选择自己的位置。然后安装程序将下载并安装。在安装过程中要耐心等待,因为这是一个很大的程序,可能需要一点时间。
安装完成后,您将看到一个类似于图 16.1.3的屏幕:
图 16.1.3:安装已成功完成
在 Visual Studio 中使用 SQL Server
一旦我们下载并安装了 SQL Server,让我们在 Visual Studio 中查看它。转到“视图”,然后选择“SQL Server 对象资源管理器”;它会在左侧打开一个小窗格,如图 16.1.4所示:
图 16.1.4:Visual Studio 中的 SQL Server 对象资源管理器窗格
接下来,单击“添加 SQL Server”按钮,如图 16.1.5所示:
图 16.1.5:添加 SQL Server 按钮
现在,出现了图 16.1.6中显示的对话框。注意其中写着 Windows 身份验证。这被称为集成安全性。您不必指定不同的用户名和密码。只需填写服务器名称字段,单击“连接”,然后您就可以登录:
图 16.1.6:连接对话框
您拥有的具体版本将与图 16.1.7中显示的版本不同,但这些是适用于许多不同版本的基本内容:
图 16.1.7:特定于 SQL Server 2017 Express 版本的数据库文件夹
创建 SQL Server 数据库
现在,我们将创建一个数据库。要做到这一点,展开“数据库”文件夹,右键单击它。选择“添加新数据库”,如图 16.1.8所示,并将数据库命名为People。
图 16.1.8:添加新数据库
添加和定义表
现在,展开 People 节点,然后在其中,您将看到一个名为 Tables 的文件夹。再次展开 Tables 节点,因为您需要添加自己的表。您的 SQL Server 对象资源管理器窗格应该看起来像图 16.1.9中显示的那样:
图 16.1.9:SQL Server 对象资源管理器窗格,其中 People 节点和 Tables 节点已展开
现在,右键单击“Tables”文件夹,然后选择“添加新表”。这将打开表定义阶段,如图 16.1.10所示:
图 16.1.10:表定义屏幕
这是您定义表的地方。看一下截图中左上角附近的第一个带有小键的字段。此键将用于标识表的记录或行,以及当您想要启用自动生成时。换句话说,您希望为每条记录分配的编号自动生成,这样您就不必跟踪它。
因此,如果右键单击键并选择属性,它会在屏幕右侧显示图 16.1.11中显示的面板:
图 16.1.11:表属性面板
现在,看标识规范的位置。展开该节点,然后在标识处,从下拉菜单中选择 True。如图 16.1.12所示,标识增量为 1,标识种子为 1,这很好。因此,它从 1 开始,每次添加新记录时,记录编号只增加 1。请注意,它还会自动更改代码视图。
图 16.1.12:设置标识规范
现在,在屏幕底部的 T-SQL 窗口中,它说CREATE TABLE [dbo].[Table]。如果您将[Table]更改为[People],那么现在就是表名,如图 16.1.13所示:
图 16.1.13:更改表的名称
在以下行中,[Id]是列或字段的名称;INT是数据类型,NOT NULL表示必须指定条目,PRIMARY KEY用于标识记录;您现在知道IDENTITY是什么了,因为您在之前的步骤中已经看到了它。
向表中添加字段
现在,您将添加自己的字段和列。因此,接下来,输入以下内容:
[NAME] VARCHAR(100) NOT NULL
这是NAME字段,数据类型是varchar,代表可变字符。这基本上是一个文本字段,因此将长度指定为100,并且因为条目应该被指定,我们将使其为NOT NULL。
让我们再添加一个字段。要做到这一点,请输入以下内容:
[DATEADDED] DATE NULL
在这里,DATE ADDED是记录添加的日期,DATE是数据类型。您的屏幕应该看起来像图 16.1.14中显示的那样。
同样,如果您不想要空值,可以在设计窗口中取消选中。因此,设计选项卡中的表视图和代码视图是相互交互的:
图 16.1.14:已向表中添加了两个字段,NAME 和 DATEADDED
这里需要注意的一点是选项卡上标有 T-SQL。嗯,SQL 是结构化查询语言,微软版本是 T-SQL 或Transact 结构化查询语言。核心基本相同,但在微软版本中有一些附加功能。
更新数据库的结构
现在,您必须更新事物的结构,因此点击屏幕右上角的更新按钮。等待一会儿更新所有内容,然后点击更新数据库按钮。
如图 16.1.15所示,更新已成功完成:
图 16.1.15:数据库更新已完成并成功!
现在,在左侧的 SQL Server 对象资源管理器窗格中,如果展开 dbo.People,然后展开列,您可以看到默认字段或列以及您创建的新字段,如图 16.1.16所示:
图 16.1.16:我们在 dbo.People 中创建的列
章节回顾
在上面的截图中,从顶部开始,第二项表示服务器(SQL Server)。然后,在其中,您有数据库,如 People 图标所示。当您展开数据库时,会有一个表的图标,一个列的图标,以及表示主键的关键图标。因此,使用的小图标代表不同级别的嵌套在这个数据库结构中。
这些是基础知识。
因此,请确保您可以重新创建所有这些。退出此窗口,然后单击“查看”,转到“起始页”。然后,执行以下操作:
-
打开 SQL Server 对象资源管理器窗格。
-
右键单击 SQL Server。
-
从下拉菜单中选择“断开连接”。
-
右键单击,然后从下拉菜单中选择“添加 SQL Server”。
-
浏览您的服务器。
-
单击连接屏幕底部的“连接”按钮。
-
展开服务器以显示“数据库”文件夹。
-
展开“数据库”文件夹,您可以看到
People数据库。 -
打开“表”文件夹,那里有
dbo.People表。
摘要
在本章中,您下载、安装并运行了 SQL Server 2017 Express。您在 Visual Studio 中使用 SQL Server,连接了两者,创建了一个 SQL Server 数据库,添加并定义了一个表,向表中添加了字段,并更新了数据库的结构。
在下一章中,您将学习如何连接到 SQL Server,然后在网页中显示数据库表中的记录。
第十七章:编写手动连接到表并检索记录的代码
在本章中,您将学习如何连接到 SQL Server,然后在网页中显示数据库表dbo.People中的记录。
在 HTML 中添加显示记录按钮
打开一个项目,并在页面中,在以<form id=....开头的行下放置一个按钮。为此,转到工具箱,获取一个Button控件,并将其拖放到那里。将按钮上的文本更改为显示记录。记住,记录只是一行信息,而一行当然是表中的一行,例如,从左到右横跨的一行。
现在,切换到设计视图,并左键双击显示记录按钮。这将带我们进入带有事件处理程序的Default.aspx.cs。删除Page_Load块。该项目的起始代码的相关部分应该如图 17.2.1所示:
图 17.2.1:该项目的起始代码部分
添加一个命名空间
要使其与 SQL Server 一起工作,您必须添加一个命名空间。因此,转到文件顶部,并在using System下输入以下内容:
using System.Data.SqlClient;
创建连接字符串
现在,除此之外,我们将逐行构建代码。您需要的第一件事是连接字符串。因此,让我们做以下事情:
-
打开 SQL Server 对象资源管理器。
-
右键单击数据库的名称,例如 People,在此处查看其属性。
-
然后,要获取连接字符串,请确保展开属性窗格中的 General 节点,然后转到称为 Connection string 的节点,并双击它以选择它及其长描述。
-
接下来,右键单击长描述并复制它。(手工构造很难准确,最好直接从那里复制)。此过程显示在图 17.2.2中:
图 17.2.2:复制连接字符串
- 现在,在以
protected void Button1_Click...开头的行下的大括号集合中输入以下内容:
string connString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
在这里,输入string connString =后,放置@符号以指示它是一个文字字符串或逐字字符串,应该准确解释。然后,在""符号中粘贴长字符串。因此,在这一行中,您当然有Data Source,计算机的名称,Initial Catalog作为数据库,Integrated Security是True,因为我们是这样设置的,以及一些其他现在并不是很重要的信息。
连接到 SQL Server
要通过页面连接到 SQL Server,我们将尝试以下操作。首先,您必须创建一个要发给 SQL Server 的命令。为此,请输入以下内容:
string commandText = "Select * from dbo.People";
在这里,Select *表示从dbo.People中选择所有内容。请记住,我们称我们的数据库为People;因此,这意味着从People数据库中的表中选择所有内容。这就是它的意思:从该表中选择所有内容。
现在,还有一件事。当您处理低级资源时,特别是读取硬盘时,例如,您必须建立与硬盘的通信通道。因此,因为这是这种情况,接下来键入以下内容:
using (SqlConnection conn = new SqlConnection(connString))
在这里,using是一个很好的构造,因为它允许您获取资源,使用资源,然后为您处理资源-非常好地和非常干净地。例如,SqlConnection就是这样一种东西。
现在,如果右键单击SqlConnection并从菜单中选择转到定义,并滚动到底部,您将看到有一行说 Dispose-protected override void Dispose。现在,如果展开protected override void Open()行,它说,使用由 system.Data.SqlClient.SqlConnection.ConnectionString 指定的属性设置打开数据库连接,如图 17.2.3所示:
图 17.2.3:protected override void Open 的扩展定义
如果你想知道可能会抛出哪些异常,所有的都列在protected override void Open()的定义中,同样也是protected override void Close()。
构造函数是定义中列出的第一个函数。所以,现在让我们关闭它。
捕获异常
在下一阶段,因为可能会抛出错误,我们将使用try和catch,这样我们就可以捕获它们并显示给用户。首先在以using (SqlConnection conn...开头的行下面的开花括号下面的一行中输入try:
try
接下来,在try下面插入一对花括号,然后在那里的闭合花括号下面输入以下内容:
catch (Exception ex)
显示错误
现在,如果生成错误,我们将显示它;因此在此行下面的一对花括号中输入以下内容:
sampLabel.Text = $"{ex.Message}";
如果数据库连接出现问题,将显示一个有用的消息。
打开连接
现在让我们继续连接。首先,让我们尝试打开它。在try下面的一对花括号中输入以下内容:
conn.Open();
这打开了一个连接。然后你将制作一个 SQL 命令,所以接下来输入以下内容:
SqlCommand sqlComm = new SqlCommand(commandText, conn);
这需要命令的文本。所以,我们将从前一行写的Select * from dbo.People中选择它;选择所有人,然后你说(command,conn),这是连接的名称。
请记住,在以string commandText...开头的行中,参数是command,在下面的行中是connection。这是两件不同的事情。
使用 SQL Server 数据读取器
现在,在下一阶段,输入以下内容:
using (SqlDataReader reader = sqlComm.ExecuteReader())
在这里,SqlDataReader是一个类。如果你将鼠标悬停在它上面,弹出的工具提示会告诉你这个东西究竟能做什么。现在,如果你右键单击SqlDataReader并选择“转到定义”,它特别实现了一个叫做IDisposable的接口,以及你可以在下拉时看到的所有函数。此外,如果你右键单击IDisposable并选择“转到定义”,那么就会有void Dispose(),展开后会说,执行与释放、释放或重置非托管资源相关的应用程序定义的任务。这特指低级磁盘写入和读取等操作。
接下来,你会看到前一行中的reader变量和sqlComm.ExecuteReader(),它返回一个SqlDataReader类,正如你在工具提示中所看到的。
现在在此行下面的一对花括号中输入以下内容:
while(reader.Read())
现在,为什么这是合法的?将鼠标悬停在Read上,你会看到它返回一个布尔值,并且它说,将 SqlDataReader 推进到下一条记录。它返回true或false,无论是否还有记录可以读取。因此,在此行下面的一对花括号中输入以下内容:
sampLabel.Text += $"<br>{reader[0]}, {reader[1]}, {reader[2]}";
一定要放入<br>标签,因为可能会返回多个项目,所以你希望它们垂直堆叠。
在前一行中,0、1、2是索引;reader[0]、reader[1]和reader[2]表示列 1、列 2和列 3。这与索引为0的数组相同。
运行程序
现在在浏览器中启动这个程序。点击“显示记录”按钮,然后你会看到记录——Id、名称和日期,如图 17.2.4所示:
图 17.2.4:运行我们的程序的结果
如果你右键单击屏幕并选择查看源代码,如图 17.2.5中所示的高亮区域,它会生成一个 span。退出这个屏幕并关闭你不再需要的窗口。
图 17.2.5:如果查看源代码,你会看到它生成了一个 span
章节回顾
为了复习,本章的 HTML 文件的完整版本如下所示:
<!DOCTYPE html>
<html >
<head runat="server">
<title>Our First Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Button ID="Button1" runat="server" Text="Show Records"
OnClick="Button1_Click" />
<div style="text-align:center;">
<asp:Label ID="sampLabel" runat="server"></asp:Label>
</div>
</form>
</body>
</html>
本章的default.aspx.cs文件的完整版本,包括注释,如下所示:
//using is a directive
//System is a name space
//name space is a collection of features that our needs to run
using System;
using System.Data.SqlClient;//needed for SQL commands and connections
//public means accessible anywhere
//partial means this class is split over multiple files
//class is a keyword and think of it as the outermost level of grouping
//:System.Web.UI.Page means our page inherits the features of a Page
public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
//make connection string
string connString = @"Data Source=DESKTOP-4L6NSGO\SQLEXPRESS;Initial Catalog=People;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;Mu
ltiSubnetFailover=False";
//this is the SQL that runs against the table
string commandText = "Select * from dbo.People";
//using statement here helps to ensure connection is properly
//disposed of here
using (SqlConnection conn = new SqlConnection(connString))
{
try
{
conn.Open(); //open connection
//make command object
SqlCommand sqlComm = new SqlCommand(commandText, conn);
//using here helps to ensure data reader is properly
//disposed of also
using (SqlDataReader reader = sqlComm.ExecuteReader())
{
//Read returns true while there are records to read
while(reader.Read())
{
//reader[0] is column 1, and so on for the
//other two
sampLabel.Text += $"<br>{reader[0]}, {reader[1]}, {reader[2]}";
}
}
}
//a common exception occurs when the server is down and cannot
//be reached
catch(Exception ex)
{
sampLabel.Text = $"{ex.Message}";
}
}
}
}
您可以查看代码并注意以下内容,这是您在本章中学到的:
-
首先是连接字符串
connString。 -
然后是
CommandText。 -
获取
SqlConnection。 -
使用
conn.Open()打开它。 -
创建一个命令:
SqlCommand(commandText, conn)。 -
使用
SqlDataReader数据读取器。 -
读取值:
sampLabel.Text += $"<br>{reader[0]}, {reader[1]}, {reader[2]}";。 -
如果有任何异常,您可以使用
catch (Exception ex)捕获它们。
总结
在本章中,您学会了如何连接到 SQL Server,然后在网页中显示来自数据库表的记录。您创建了一个连接字符串,连接到 SQL Server,编写了捕获异常和显示错误的代码,打开了连接,并与 SQL Server 的DataReader一起工作。
在下一章中,您将制作一个表,编写一个过程,并使用该过程将记录插入到表中。
第十八章:使用存储过程将记录插入表中
在本章中,您将学习如何使用存储在 SQL Server 的Programmability文件夹中的存储过程直接将记录插入表中。我们将通过 HTML 页面中的文本框进行操作。
在 HTML 中添加文本框和按钮
启动一个项目。首先,在页面中放入一对框。为此,请在以<form id= ....开头的行下输入以下内容:
Enter Name:<asp:TextBoxID="TextBox1" runat="server"></asp:TextBox><br />
Enter Date:<asp:TextBoxID="TextBox2" runat="server"></asp:TextBox><br />
对于Name字段,它只是一个文本框。因此,对于文本,换句话说,我们将使用一个字符串。转到工具箱,获取一个TextBox控件,并将其拖放到那里。对于日期,我们将尝试从框中解析为日期时间。
您的Default.aspx屏幕现在应该看起来像图 18.3.1中显示的屏幕。
图 18.3.1:本章的 Default.aspx 屏幕中的屏幕
请记住,我们有两个框,我们输入值,并将它们保存到表中。这是这里的目标。
接下来,让我们也在那里放一个按钮。因此,再次转到工具箱,获取一个按钮,并将其拖放到这些行的正下方。更改按钮上的文本,以使其更有帮助,例如,说插入并显示。
因此,当您单击按钮时,您将插入新记录,并且还将显示记录以确认它与现有记录一起保存。
回顾在 SQL Server 中已经创建的内容
接下来,打开 SQL Server 对象资源管理器屏幕。现在,请记住,您创建了一个名为People的数据库,然后在其中还有一个名为People的表。此外,在其中,您有一个名为Id的列。这是主键。请记住,它是自动递增的,因此您不必指定 ID。也就是说,它会自动为您完成。
接下来,有两个字段:一个是NAME,另一个是DATEADDED;NAME是varchar(100),DATEADDED的类型是date。两个值都必须提供,这就是为什么它说not null。到目前为止,SQL Server 对象资源管理器屏幕显示在图 18.3.2中:
图 18.3.2:数据库 People 的 SQL Server 对象资源管理器屏幕
创建新的存储过程
现在,展开Programmability文件夹。有一个名为存储过程的文件夹。右键单击它,然后选择添加新存储过程...。这是基本的存储过程代码:
图 18.3.3:默认存储过程屏幕
要使用存储过程,您首先需要将其重命名。为此,请将顶行中的[Procedure]更改为[AddName],如下所示:
CREATE PROCEDURE[dbo].[AddName]
正如您所看到的,它只是驻留在 SQL Server 中的一些代码。然后,您可以,例如,执行该代码以在数据库表中执行某些操作。
在我们的情况下,我们将使用此过程将记录插入表中。我们需要参数,因为我们将输入两个值。因此,按照以下方式编辑存储过程的下两行:
首先,将param1更改为Name,将默认值更改为int = 0,并将数据类型分配为varchar(100)。
下一行,将param2更改为DateAdded,类型为date。因此,这是两个参数:
@Name varchar(100),
@DateAdded date
现在,因为您不会选择记录,而是会插入记录,所以,我们将输入一个insert语句,然后在SELECT行的位置键入以下内容:
insert into People (NAME,DATEADDED) values (@Name,@DateAdded)
在这里,您insert into People数据库,然后列出应接收新信息的字段,即NAME和DATEADDED。然后,您输入values,然后是参数列表—@Name和@DateAdded。
记住,这行中的参数类似于之前创建的函数。通过它们,在你在 C#中编写函数时,将值传递给函数。同样的原理也适用于这里。通过参数,值被传递到存储过程的主体中,在这种特殊情况下,直接将字段值插入到表中。在这里,@Name和@DateAdded的值被传递到NAME和DATEADDED中。这就是目标。
完整的存储过程显示在图 18.3.4中:
图 18.3.4:存储过程,dbo.AddName
更新数据库结构
现在,让我们更新一下结构;所以,单击“更新”按钮,然后在弹出的对话框中单击“更新数据库”,如图 18.3.5所示。
图 18.3.5:预览数据库更新对话框
更新后,展开“可编程性”文件夹,然后展开“存储过程”文件夹。在那里,你会看到dbo.AddName。现在,如果你展开dbo.AddName,会看到一系列参数:@Name和@DateAdded。
现在,让我们利用到目前为止所做的事情。单击Default.aspx选项卡,然后进入设计视图,双击“插入并显示”按钮。这将带我们进入Default.aspx.cs。删除Page_Load存根。我们将从图 18.3.6中显示的代码开始这个项目:
图 18.3.6:这个项目的起始代码
添加一个命名空间
再次,要使这个与 SQL Server 一起工作,你必须添加一个命名空间。所以,转到文件顶部,在using System下输入以下内容:
using System.Data.SqlClient;//commands and connections
当然,这将用于诸如命令和连接之类的东西,你可以将其填写为注释。我们将在这一行下面再做一个,所以输入以下内容:
using System.Data;
这行也将为我们服务。会有相当多的代码,但它是高度顺序的——它从上到下自然而然地进行,它会为你完成工作。
现在,每次单击按钮时,你都希望清除标签,以便输出不会继续累积;所以,在以protected void Button1_Click...开头的行下的一对大括号之间,输入以下内容:
sampLabel.Text = "";
构建连接字符串
在下一阶段,你想要获取连接字符串;所以,在下一行开始时输入string connString =,然后跟上@符号使其成为逐字字符串,然后放入""符号。现在,要获取连接字符串,做以下操作:
-
单击菜单栏中的“查看”,然后选择“SQL Server 对象资源管理器”。
-
右键单击
People数据库,然后选择“属性”。 -
在属性窗格中,双击连接字符串以选择它及其长描述。
-
然后,右键单击长描述并复制它。
-
在一对
""符号之间粘贴描述。
连接字符串行应该看起来像下面这样:
string connString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=People;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
现在可以关闭 SQL Server 对象资源管理器和属性窗格。
初始化连接
在下一阶段,因为我们正在访问硬盘来读取和保存记录,输入以下内容:
using (SqlConnection conn = new SqlConnection(connString))
这就是如何初始化连接。如果右键单击SqlConnection并选择“转到定义”,它会说它是DbConnection类型,并且继承自SqlConnection。现在,如果你右键单击DbConnection并选择“转到定义”,它会说它实现了IDisposable。然后,如果你右键单击IDisposable并选择“转到定义”,它会说,执行与释放、释放或重置非托管资源相关的应用程序定义的任务。因此,例如,对于从硬盘获取信息的低级通道,你必须确保它们被正确清理。现在可以关闭这个窗口。
捕获异常
接下来,因为在与数据库一起工作时可能会出现各种问题,您需要先try,然后捕获任何异常。为此,在前一行下的开放大括号下面,输入以下内容:
try
{
}
catch (Exception ex)
在这里,我真的只是为了能够显示一些诊断信息而添加了catch (Exception ex)。接下来,在此下面的一对大括号之间,输入以下内容:
sampLabel.Text = $"{ex.Message}";
我们使用这行只是为了显示诊断信息。
尝试命令
现在,让我们进入try部分。这是一切都可能发生的地方。首先,让我们创建一个命令。在try下面的大括号之间输入以下内容:
SqlCommand cmd = new SqlCommand();
接下来,您将设置命令的类型,因此请输入以下内容:
cmd.CommandType = CommandType.StoredProcedure;
这行说明了它自己。
现在,为了实际获取文本以选择要调用的特定存储过程,您需要输入以下内容:
cmd.CommandText = "AddName";
记住,AddName是我们在 SQL Server 中称之为的存储过程。
添加参数
现在,对于下一个阶段,我们将添加所谓的参数。换句话说,您必须确保实际传递值到存储过程中,以便您可以将它们保存在表中。因此,请接下来输入以下内容:
cmd.Parameters.AddWithValue("@Name", TextBox1.Text);
在这里,我们从参数的名称开始:@Name,然后它的值将来自第一个框:TextBox1.Text。
接下来,您将重复此逻辑,因此请输入以下内容:
cmd.Parameters.AddWithValue("@DateAdded", DateTime.Parse(TextBox2.Text));
在这里,@DateAdded是参数的名称,下一个阶段来自第二个框:TextBox2.Text。这行将转换框中的值,假设它可以转换为DateTime对象,以便它与数据库中的@DateAdded类型匹配。这就是为什么我们要采取这一步骤。
当然,在更现实的情况下,您可能想尝试DateTime.TryParse。为了避免过多的复杂性,我们将只使用DateTime.Parse。
接下来输入以下内容:
cmd.Connection = conn;
您必须设置conn属性。我们在文件顶部创建了这个属性,以using(SqlConnection conn...开头的行。
对于下一行,请输入以下内容以打开连接:
conn.Open();
保存信息以便以后检索
在下一个阶段,我们将执行NonQuery。为此,请输入以下内容:
cmd.ExecuteNonQuery();
这行将保存信息。现在,从那里开始,当您想要检索信息时,请确保它按预期工作。我们只需将命令类型切换为Text类型的CommandType,因此请接下来输入以下内容:
cmd.CommandType = CommandType.Text;
接下来,我们将指定文本,因此请输入以下内容:
cmd.CommandText = "select * from dbo.People";
在这里,select *表示从People数据库中选择所有内容。
之后,输入以下内容:
using (SqlDataReader reader = cmd.ExecuteReader())
认识索引器的作用
现在,我将向您展示一些以前没有向您展示的东西。将鼠标悬停在ExecuteReader上。这将返回一个SqlDataReader类。现在,在前一行中右键单击SqlDataReader,然后选择“转到定义”。您还记得我们之前学到的索引器吗?看看它说的 public override object this[string name]。如果您展开它,它说它获取指定列的值及其本机格式,给定列名。如果您返回,下一个定义是 public override object this[int i]。如果您展开这个,它说,获取指定列的值及其本机格式,给定列的顺序,这里是列的编号。因此,public override object...行是指当前的SqlDataReader对象。这基本上是一个索引器。现在您可以看到索引器确实发挥了作用。现在您可以关闭这个。
为了利用这些信息,请在前面using行下的一对大括号之间输入以下内容:
while(reader.Read())
然后,在这行下面的一对大括号之间,输入以下内容:
sampLabel.Text += $"<br>{reader[0]}, {reader[1]}, {reader[2]}";
在这里,在sampLabel.Text...之后,您指定reader[0],{reader[1]}和{reader2},这是通过索引访问的三列。
您现在已经输入了程序的核心。
运行程序
现在,让我们来看看结果。在浏览器中打开这个。首先,输入一些值:Berry Gibbs作为Name,一个日期,然后点击“插入并显示”按钮。结果显示在图 18.3.7中:
![图 18.3.7:运行我们的程序后的初始结果所以,就是这样——它按预期工作。现在,让我们再试一个。输入Mark Owens作为Name,添加一个日期,然后再次点击“插入并显示”按钮。正如您在图 18.3.8中所看到的,它已经被自动添加了。这证实它已保存到表中,然后我们可以检索它:
图 18.3.8:运行程序后修改的结果
所以,这些是建立连接的基础知识。
现在考虑这一点。想象一下,在前一行中,我写成了cmd.CommandText = "AddNames"而不是AddName。换句话说,我拼错了存储过程的名称。如果我在浏览器中打开这个,就像图 18.3.9中所看到的那样,它会显示“字符串无法识别为有效的日期时间”。这很有用,对吧?我没有填写Name或Date。所以,它无法转换为DateTime:
图 18.3.9:未输入值运行程序的结果
现在,即使我为Name和Date输入值,它也会显示“找不到存储过程'AddNames'”,如图 18.3.10所示,因为我拼错了存储过程的名称:
图 18.3.10:拼错存储过程名称后运行程序的结果
所以,使用try行,因为之后的所有命令都可能产生某种错误,至少您可以捕获它并显示错误消息,这样您就能知道发生了什么。所以,这非常有用。
章节回顾
为了回顾,包括注释的本章Default.aspx.cs文件的完整版本如下所示:
//using is a directive
//System is a name space
//name space is a collection of features that our needs to run
using System;
using System.Data.SqlClient;//commands and connections
using System.Data;
//public means accessible anywhere
//partial means this class is split over multiple files
//class is a keyword and think of it as the outermost level of grouping
//:System.Web.UI.Page means our page inherits the features of a Page
public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
sampLabel.Text = "";
string connString = @"Data Source=DESKTOP-4L6NSGO\SQLEXPRESS;Initial Catalog=People;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False";
//put conn in a using so it can be properly closed and disposed of
using (SqlConnection conn = new SqlConnection(connString))
{
try
{
//make sql command
SqlCommand cmd = new SqlCommand();
//specify type
cmd.CommandType = CommandType.StoredProcedure;
//write name of stored procedure inside SQL Server as
//the name here
cmd.CommandText = "AddName";
//read the field box 1, and pass in through @Name
cmd.Parameters.AddWithValue("@Name", TextBox1.Text);
//pass in date through @DateAdded
cmd.Parameters.AddWithValue("@DateAdded",
DateTime.Parse(TextBox2.Text));
//set connection property of command object
cmd.Connection = conn;
//open connection
conn.Open();
//execute the stored procedure
cmd.ExecuteNonQuery();
//change command type to just plain text
cmd.CommandType = CommandType.Text;
//write a simple SQL select statement
cmd.CommandText = "select * from dbo.People";
//execute reader
using (SqlDataReader reader = cmd.ExecuteReader())
{
//Read() returns true while it can read
while(reader.Read())
{
//reader[0] means get first column,
//reader uses an indexer to do this
sampLabel.Text += $"<br>{reader[0]}, {reader[1]}, {reader[2]}";
}
}
}
catch(Exception ex)
{
sampLabel.Text = $"{ex.Message}";
}
}
}
}
总结
在本章中,您学习了如何使用存储过程直接将记录插入到表中,并存储在 SQL Server 的可编程文件夹中。您创建了一个新的存储过程,更新了数据库结构,构建了连接字符串,初始化了连接,尝试了命令并捕获了异常,添加了参数,保存了信息以供以后检索,并认识到了索引器的作用。
在下一章中,您将学习如何使用nullable关键字来确保具有缺失值的记录仍然可以被引入应用程序中。