• ASP.NET数据绑定


      数据绑定是ASP.NET提供的另一种访问数据库的方法。与ADO.NET数据库访问技术不同的是:数据绑定技术可以让程序员不关注数据库连接、数据库命令以及如何格式化这些数据以显示在页面上等环节,而是直接把数据绑定到HTML元素和Web控件。

      数据控件则是用来显示从数据库中获取的数据。

      数据绑定的原理:

        首先要设置控件的数据源和数据的显示格式,设置完成后,控件就会自动处理剩余的工作以把要显示的数据按照要显示的格式显示在页面上。

    数据绑定的类型 

      1. 单值绑定。

           可以通过单值绑定的方式把数据添加到ASP.NET页面的任何地方。可以把数据放在一个控件的属性定义标记里,也可以直接以纯文本的形式放置在HTML标记里。

      2. 多值绑定。

           多值绑定可以显示一个表中的所有内容。同单值绑定不一样,这种类型的数据绑定需要支持它的特殊控件。

    数据绑定的工作方式

      单值数据绑定和多值数据绑定的工作方式不太一样。使用单值数据绑定时,需要把数据绑定表达式插入到.aspx文件的标记中。使用多值数据绑定时,必须设置一个数据控件的单个或多个属性。

      一旦指定了数据绑定,就需要激活它,可以通过调用控件或页面对象的DataBind方法来激活数据绑定。

      在页面的Load事件中调用DataBind方法。如果没有在Load事件中调用DataBind方法的话,ASP.NET将忽略数据绑定表达式,在页面上将以空值的形式呈现。 

    单值绑定

      单值绑定其实就是实现动态文本的一种的方式,为了实现单值数据绑定,可以向ASP.NET页面文件中添加特殊的数据绑定表达式。

      单值绑定主要有四种数据绑定表达式:

        1.<%=XXX %>,它是内联引用方式,可以引用C#代码。

        2.<%# XXX %>,它可以引用.cs文件中的代码的字段,但这个字段必须初始化后,在页面的Load事件中使用Page.DataBind方法来实现。

        3.<%#$ XXX %>,它可以引用Web.config文件中预定义的字段或者已注册的类。

        4.<%# Eval(XXX) %>,它类似于JavaScript,数据源也需要绑定。

      单值绑定的使用:

        在Default.aspx中加入如下代码:

     1 <div>
     2         <div>
     3             <%# projectName %>
     4             <br />
     5             <br />
     6             <%= DateTime.Now %>
     7         </div>
     8         <br />
     9         <div>
    10         </div>
    11         <asp:TextBox ID="bindTest" runat="server" Text="<%$ AppSettings:test %>" />
    12         <br />
    13         <br />
    14         <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
    15             <Columns>
    16                 <asp:TemplateField>
    17                     <HeaderTemplate>
    18                         <asp:Label runat="server" Text="姓名:" />
    19                     </HeaderTemplate>
    20                     <ItemTemplate>
    21                         <%#Eval("Name") %>
    22                         <br />
    23                     </ItemTemplate>
    24                 </asp:TemplateField>
    25                 <asp:TemplateField>
    26                     <HeaderTemplate>
    27                         <asp:Label runat="server" Text="性别:"></asp:Label>
    28                     </HeaderTemplate>
    29                     <ItemTemplate>
    30                         <%#Eval("Sex") %>
    31                     </ItemTemplate>
    32                 </asp:TemplateField>
    33                 <asp:TemplateField>
    34                     <HeaderTemplate>
    35                         <asp:Label runat="server" Text="年龄:"></asp:Label>
    36                     </HeaderTemplate>
    37                     <ItemTemplate>
    38                         <%#Eval("Age") %>
    39                     </ItemTemplate>
    40                 </asp:TemplateField>
    41             </Columns>
    42         </asp:GridView>
    43 </div>
    View Code

        

        修改Default.aspx.cs中的代码,代码如下:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Data;
     4 using System.Linq;
     5 using System.Web;
     6 using System.Web.UI;
     7 using System.Web.UI.WebControls;
     8 
     9 namespace WebApplication1
    10 {
    11     public partial class Default : System.Web.UI.Page
    12     {
    13         public string projectName;
    14         protected void Page_Load(object sender, EventArgs e)
    15         {
    16             ini_table();
    17             projectName = "单值绑定";
    18             Page.DataBind();
    19         }
    20         void ini_table()
    21         {
    22             DataTable dataTable = new DataTable();
    23             //用于显示的Name列
    24             dataTable.Columns.Add("Name");
    25             dataTable.Columns.Add("Sex");
    26             dataTable.Columns.Add("Age");
    27             DataRow dataRow = dataTable.NewRow();
    28             dataRow[0] = "周周";
    29             dataRow[1] = "";
    30             dataRow[2] = 23;
    31             dataTable.Rows.Add(dataRow);
    32 
    33             dataRow = dataTable.NewRow();
    34             dataRow[0] = "芳芳";
    35             dataRow[1] = "";
    36             dataRow[2] = 22;
    37             dataTable.Rows.Add(dataRow);
    38             //数据源绑定
    39             this.GridView1.DataSource = dataTable.DefaultView;
    40             this.GridView1.DataBind();
    41         }
    42     }
    43 }
    View Code

      单值数据绑定的使用非常简单,且比较灵活,可以随意的在页面添加数据绑定,也可以对控件进行数据绑定,但是在使用过程中需要考虑以下单值数据绑定的缺点:

        1.数据绑定的代码和定义用户界面的代码混合在一起。ASP.NET的一个优势就是把定义用户界面的代码同数据访问代码和其他操作任务的代码分开,但是单值数据绑定却把数据绑定的代码同定义用户界面的代码混合在一起,这样不方便页面和代码的管理,容易引起混乱。

        2.代码过于分散,使得不同的程序员很难在同一个项目上协同工作。

      单值数据绑定的替代方法:

        为了避免代码与HTML混用,也为了方便代码的管理,可以在后台代码中以赋值的方式替代单值数据绑定,在页面的Load事件中对控件进行赋值。

    多值绑定

      多值绑定使程序员不用编写循环语句就能把ArrayDataTable中的数据添加到控件中。简化了支持复杂格式和模板选择的数据显示,使得数据能够自动被配置为控件中要显示的格式。

      创建多值绑定,需要使用支持数据绑定的控件,ASP.NET提供一系列这类控件:

        1. 列表控件,诸如ListBoxDropDownListCheckBoxListRadioButtonList等。

        2. HtmlSelect,它是一个HTML控件,类似于ListBox控件。

        3. GirdViewDetailsViewFormViewListView等复杂的数据控件。

      多值绑定的使用:

        在Default.aspx中添加以下代码:

     1     <div>
     2         <table>
     3             <tr>
     4                 <td align="Top" colspan="2">
     5                     <asp:Label ID="Label1" runat="server" Text="新上架的水果:" />
     6                 </td>
     7             </tr>
     8             <tr>
     9                 <td>
    10                     <asp:ListBox ID="ListBox1" runat="server" />
    11                 </td>
    12             </tr>
    13         </table>
    14     </div>
    View Code

        在Default.aspx中的Load事件中添加以下代码:

    1             ArrayList arrayList = new ArrayList();
    2             arrayList.Add("香蕉");
    3             arrayList.Add("苹果");
    4             arrayList.Add("橘子");
    5             this.ListBox1.DataSource = arrayList;
    6             this.ListBox1.DataBind();
    View Code

    强类型集合

      在.NET框架中的命名空间System.Collections.Generic中,存在与哈希表和ArrayList不同的集合,这些集合只能存储单一类型的对象,这些集合被称为泛型集合。

      使用泛型集合可以创建强类型集合。

      创建泛型集合时,需要指定存储项的类型,这样就确定了集合要存储的对象的类型。当在集合中添加不同类型的对象时,就会产生编译错误。

      使用泛型集合存储数据时,不用担心存入不安全的数据类型,且在访问的时候也不需要再进行数据转换,可以提高数据访问的速度。

      使用泛型集合必须在代码文件中引用命名空间:Using System.Collections.Generic。

      强类型集合的使用:

        在Default.aspx中添加以下代码:

     1     <div>
     2         <table>
     3             <tr>
     4                 <td align="Top" colspan="2">
     5                     <asp:Label ID="Label1" runat="server" Text="新上架的水果:" />
     6                 </td>
     7             </tr>
     8             <tr>
     9                 <td>
    10                     <asp:ListBox ID="ListBox1" runat="server" />
    11                 </td>
    12             </tr>
    13         </table>
    14     </div>
    View Code

        在Default.aspx中的Load事件中添加以下代码:

    1             List<string> list = new List<string>();
    2             list.Add("香蕉");
    3             list.Add("苹果");
    4             list.Add("橘子");
    5             this.ListBox1.DataSource = list;
    6             this.ListBox1.DataBind();
    View Code

    字典集合

      Dictionary类位于System.Collections.Generic命名空间下。Dictionary类表示键和值的集合,它提供了从一组键到一组值的映射。字典中的每个添加项都由一个值及其相关联的键组成。通过键来检索值的速度是非常快的,因为Dictionary类是作为一个哈希表来实现的。在使用HashTable来存储将要写入到数据库或者返回的信息时,在这之间要不断地进行类型的转换,增加了系统装箱和拆箱的负担,如果操作的数据类型相对确定的话,而用Dictionary<TKey,TValue>集合类来存储数据就方便多了。

      字典集合的使用:

        在Default.aspx中添加以下代码:

     1     <div>
     2         <table>
     3             <tr>
     4                 <td align="Top" colspan="2">
     5                     <asp:Label ID="Label1" runat="server" Text="新上架的水果:" />
     6                 </td>
     7             </tr>
     8             <tr>
     9                 <td>
    10                     <asp:ListBox ID="ListBox1" runat="server" />
    11                 </td>
    12             </tr>
    13         </table>
    14     </div>
    View Code

        在Default.aspx中的Load事件中添加以下代码:

    1             Dictionary<int, string> fruit = new Dictionary<int, string>();
    2             fruit.Add(1, "香蕉");
    3             fruit.Add(2, "苹果");
    4             fruit.Add(3, "橘子");
    5             this.ListBox1.DataSource = fruit;
    6             this.ListBox1.DataTextField = "Value";
    7             this.DataBind();
    View Code

    数据源控件

      数据源控件用于连接数据源、从数据源中读取数据以及把数据写入数据源。数据源控件不呈现任何用户界面,而是充当特定数据源(如数据库、业务对象或XML文件)与ASP.NET网页上的其他控件之间的桥梁。数据源控件实现了丰富的数据检索和修改功能,其中包括查询、排序、分页、筛选、更新、删除以及插入。

      使用数据源控件可以不用编写任何代码就可以实现页面的数据绑定。.NET Framework包含支持不同数据绑定方案的数据源控件,这些控件可以使用不同的数据源。此外,数据源控件模型是可扩展的,因此用户还可以创建自己的数据源控件,实现与不同数据源的交互,或为现有的数据源提供附加功能。

      .NET框架提供了七个数据源控件,如下:

        1.ObjectDataSource控件:表示具有数据检索和更新功能的中间层对象,允许使用业务对象或其他类,并可创建依赖中间层对象管理数据库的Web应用程序。

        2.SqlDataSource控件:用来访问存储在关系数据库中的数据。这些数据库包括Microsoft SQL Server以及OLE DB和ODBC数据源。它与SQL Server一起使用时支持高级缓存功能。当数据作为DataSet对象返回时,此控件还支持排序、筛选和分页。

        3.AccessDataSource控件:主要用来访问Microsoft Access数据库。当数据作为DataSet对象返回时,此控件还支持排序、筛选和分页。

        4.XmlDataSource控件:主要用来访问XML文件,特别适用于分层的ASP.NET服务器控件,如TreeView控件、Menu控件。它支持使用XPath表达式来实现筛选功能,并允许对数据应用XSLT转换。它允许通过保存更改后的整个XML文档来更新数据。

        5.SiteMapDataSource控件:结合ASP.NET站点导航使用。

        6.EntityDataSource控件:支持基于实体数据库模型(EDM)的数据绑定方案。此数据规范将数据表示为实体和关系集。它支持自动生成更新、插入、删除、和选择命令以及排序、筛选和分页。

        7.LinqDataSource控件:使用LinqDataSource控件,可以在ASP.NET网页中使用LINQ,从数据表或内存数据集合中检索数据。使用声明性标记,可以编写对数据进行检索、筛选、排序和分组操作所需的全部条件。从SQL数据库表中检索数据时,也可以配置LinqDataSource控件来处理更新、插入和删除操作。

      SqlDataSource控件

        可以将 SqlDataSource控件和用于显示数据的其他控件(如GridView、FormView和DetailsView控件)结合使用,使用很少的代码或不使用代码就可以在ASP.NET网页中显示和操作数据。

        SqlDataSource控件使用ADO.NET类与ADO.NET支持的任何数据库进行交互。SqlDataSource控件使用ADO.NET类提供的提供器访问数据库。 

        使用SqlDataSource控件连接SQL Server数据库。

          在配置文件中添加:

    1 <connectionStrings>
    2     <add name="ConnectionString" connectionString="Data Source=追风的蜗牛;Initial Catalog=Adrotator;Integrated Security=True"/>
    3 </connectionStrings>
    View Code

          在Default.aspx中添加以下代码:

     1     <div>
     2         <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ConnectionStrings:ConnectionString %>"
     3             ProviderName="System.Data.SqlClient" DataSourceMode="DataReader" SelectCommand="select * from Advertisements">
     4         </asp:SqlDataSource>
     5         <table>
     6             <tr>
     7                 <td align="Top" colspan="2">
     8                     <asp:Label ID="Label1" runat="server" Text="广告:" />
     9                 </td>
    10             </tr>
    11             <tr>
    12                 <td>
    13                     <asp:ListBox ID="ListBox1" runat="server" DataSourceID="SqlDataSource1" AutoPostBack="true"
    14                          DataTextField="AlternateText" DataValueField="ID" Width="200px" Height="200px">
    15                         </asp:ListBox>
    16                 </td>
    17             </tr>
    18         </table>
    19     </div>
    View Code

        SqlDataSource控件的功能:

          1.执行数据库操作命令。

            SelectCommand、UpdateCommand、DeleteCommand 和 InsertCommand四个属性对应数据库操作的四个命令:选择、更新、删除和插入,可以通过设置这四个属性来执行相应的数据库操作命令。只需要把对应的SQL语句赋予这四个属性,SqlDataSource控件即可完成对数据库的操作。

          2.返回DataSet或DataReader对象。

            SqlDataSource控件可以返回两种格式的数据:作为DataSet对象或ADO.NET数据读取器。通过设置数据源控件的DataSourceMode属性,可以指定要返回的格式。 

          3.进行缓存。

            SqlDataSource控件可以缓存它已检索的数据,可以避免开销很大的查询操作,从而增强应用程序的性能。只要数据相对稳定,且缓存的结果小得足以避免占用过多的系统内存,就可以使用缓存。

            默认情况下不启用缓存,将EnableCaching属性设置为true,便可以启用缓存。

          4.筛选。

            如果已为SqlDataSource控件启用缓存,并且已将数据集指定为Select查询返回的数据格式,则还可以筛选数据,不需重新运行该查询。

            SqlDataSource控件支持FilterExpression属性,可以使用该属性指定应用于由数据源控件维护的数据的选择条件。还可以创建特殊的FilterParameters对象,这些对象在运行时为筛选表达式提供值,从而对筛选表达式进行参数化。

        使用SqlDataSource控件检索数据:

          使用SqlDataSource控件从数据库中检索数据,要设置一下属性:

            1.ProviderName:设置为ADO.NET提供程序的名称,该提供程序表示正在使用的数据库。

            2.ConnectionString:设置为用于数据库的连接字符串。

            3.SelectCommand:设置为从数据库中返回数据的SQL查询或存储过程。

          使用SqlDataSource控件从数据库中检索数据。

            在配置文件中添加:

    1   <connectionStrings>
    2     <add name="ConnectionString" connectionString="Data Source=追风的蜗牛;Initial Catalog=Adrotator;Integrated Security=True"/>
    3   </connectionStrings>
    View Code

            在Default.aspx中添加以下代码:

     1     <div>
     2         请选择:
     3         <asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="true">
     4             <asp:ListItem>冰山</asp:ListItem>
     5             <asp:ListItem>荷塘</asp:ListItem>
     6             <asp:ListItem>落日</asp:ListItem>
     7         </asp:DropDownList>
     8         <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ConnectionStrings:ConnectionString %>"
     9             ProviderName="System.Data.SqlClient" DataSourceMode="DataReader" SelectCommand="select * from Advertisements where AlternateText=@text">
    10             <SelectParameters>
    11                 <asp:ControlParameter Name="text" ControlID="DropDownList1" PropertyName="SelectedValue" />
    12             </SelectParameters>
    13         </asp:SqlDataSource>
    14         <br />
    15         <asp:Label Text="导航链接:" runat="server" />
    16         <br />
    17         <asp:ListBox ID="ListBox1" runat="server" DataSourceID="SqlDataSource1" AutoPostBack="true"
    18                          DataTextField="NavigateUrl" DataValueField="ID" Width="200px" Height="200px">
    19                         </asp:ListBox>
    20     </div>
    View Code

        使用参数:

          SqlDataSource控件可以使用参数执行下列操作:

            1.提供用于数据检索的搜索条件;

            2.提供要在数据存储区中插入、更新或删除的值;

            3.提供用于排序、分页和筛选的值。

            4.借助参数,使用少量自定义代码或不使用自定义代码就可筛选数据和创建主/从应用程序。

          可以从各种源中获取参数值。通过Parameter对象,可以从Web服务器控件属性、Cookie、会话状态、QueryString字段、用户配置文件属性及其他源中提供值给参数化数据操作。

          SqlDataSource控件的参数类型

            1.ControlParameter:将参数设置为ASP.NET网页中的Control的属性值。

            2.CookieParameter:将参数设置为HttpCookie对象的值。

            3.FormParameter:将参数设置为HTML窗体字段的值。

            4.ProfileParameter:将参数设置为当前用户配置文件(Profile) 中的属性的值。

            5.QueryStringParameter:将参数设置为QueryString字段的值。使用QueryStringField属性指定QueryString字段的名称。

            6. SessionParameter:将参数设置为Session对象的值。使用SessionField属性指定Session对象的名称。

          使用SqlDataSource控件。该控件使用参数化命令查询和修改数据绑定控件中的数据。

            在配置文件中添加:

    1   <connectionStrings>
    2     <add name="ConnectionString" connectionString="Data Source=追风的蜗牛;Initial Catalog=Adrotator;Integrated Security=True"/>
    3   </connectionStrings>
    View Code

            在Default.aspx中添加以下代码:

     1     <div>
     2         <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ConnectionStrings:ConnectionString %>"
     3             ProviderName="System.Data.SqlClient" DataSourceMode="DataReader" SelectCommand="select AlternateText,ID from Advertisements">
     4         </asp:SqlDataSource>
     5         <br />
     6         <asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ConnectionStrings:ConnectionString %>" 
     7             ProviderName="System.Data.SqlClient" DataSourceMode="DataReader" 
     8             SelectCommand="select * from Advertisements where ID=@ID" 
     9             InsertCommand="insert into Advertisements(ID,ImageUrl,NavigateUrl,Impressions,AlternateText) 
    10             values(@ID,@ImageUrl,@NavigateUrl,@Impressions,@AlternateText); select @ID=scope_Identity()" 
    11             UpdateCommand="update Advertisements set ImageUrl=@ImageUrl,NavigateUrl=@NavigateUrl,Impressions=@Impressions,AlternateText=@AlternateText where ID=@ID" 
    12             DeleteCommand="delete Advertisements where ID=@ID" OnInserted="SqlDataSource2_Inserted">
    13             <SelectParameters>
    14                 <asp:ControlParameter Name="ID" ControlID="DropDownList1" PropertyName="SelectedValue" Type="String" DefaultValue="1" />
    15             </SelectParameters>
    16             <InsertParameters>
    17                 <asp:Parameter Name="ImageUrl" Type="String" />
    18                 <asp:Parameter Name="NavigateUrl" Type="String" />
    19                 <asp:Parameter Name="Impressions" Type="String" />
    20                 <asp:Parameter Name="AlternateText" Type="String" />
    21                 <asp:Parameter Name="ID" Type="String" DefaultValue="1" />
    22             </InsertParameters>
    23             <UpdateParameters>
    24                 <asp:Parameter Name="ImageUrl" Type="String" />
    25                 <asp:Parameter Name="NavigateUrl" Type="String" />
    26                 <asp:Parameter Name="Impressions" Type="String" />
    27                 <asp:Parameter Name="AlternateText" Type="String" />
    28                 <asp:Parameter Name="ID" Type="String" DefaultValue="1" />
    29             </UpdateParameters>
    30             <DeleteParameters>
    31                 <asp:Parameter Name="ID" Type="String" DefaultValue="1" />
    32             </DeleteParameters>
    33         </asp:SqlDataSource>
    34         <br />
    35         <asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="true" DataSourceID="SqlDataSource1" 
    36             DataTextField="AlternateText" DataValueField="ID" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged">
    37         </asp:DropDownList>
    38         <br />
    39         <asp:DetailsView ID="DetailsView1" runat="server" DataSourceID="SqlDataSource2" AutoGenerateRows ="false" 
    40             AutoGenerateInsertButton="true" AutoGenerateEditButton="true" AutoGenerateDeleteButton="true" DataKeyNames="ID" 
    41             GridLines="Both" OnItemDeleted="DetailsView1_ItemDeleted" OnItemUpdated="DetailsView1_ItemUpdated">
    42             <HeaderStyle BackColor="Wheat" ForeColor="PaleGoldenrod" />
    43             <RowStyle BackColor="White" />
    44             <AlternatingRowStyle BackColor="Lavender" />
    45             <EditRowStyle BackColor="LavenderBlush" />
    46             <Fields>
    47                 <asp:BoundField DataField="ID" HeaderText="广告编号" InsertVisible="false" ReadOnly="true" />
    48                 <asp:BoundField DataField="ImageUrl" HeaderText="图片地址" />
    49                 <asp:BoundField DataField="NavigateUrl" HeaderText="链接地址" />
    50                 <asp:BoundField DataField="Impressions" HeaderText="显示频率" />
    51                 <asp:BoundField DataField="AlternateText" HeaderText="广告名称" />
    52             </Fields>
    53         </asp:DetailsView>
    54     </div>
    View Code

            Defau.aspx.cs中的代码如下:

     1 using System;
     2 using System.Collections;
     3 using System.Collections.Generic;
     4 using System.Data;
     5 using System.Linq;
     6 using System.Web;
     7 using System.Web.UI;
     8 using System.Web.UI.WebControls;
     9 
    10 namespace WebApplication1
    11 {
    12     public partial class Default : System.Web.UI.Page
    13     {
    14         //public string projectName;
    15         protected void Page_Load(object sender, EventArgs e)
    16         {
    17 
    18         }
    19 
    20         protected void SqlDataSource2_Inserted(object sender, SqlDataSourceStatusEventArgs e)
    21         {
    22             System.Data.Common.DbCommand command = e.Command;
    23             DropDownList1.DataBind();
    24             this.DetailsView1.DataBind();
    25         }
    26 
    27         protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
    28         {
    29             this.DetailsView1.DataBind();
    30         }
    31 
    32         protected void DetailsView1_ItemDeleted(object sender, DetailsViewDeletedEventArgs e)
    33         {
    34             DropDownList1.DataBind();
    35         }
    36 
    37         protected void DetailsView1_ItemUpdated(object sender, DetailsViewUpdatedEventArgs e)
    38         {
    39             this.DropDownList1.DataBind();
    40             DropDownList1.SelectedValue = e.Keys["ID"].ToString();
    41             DetailsView1.DataBind();
    42         }
    43     }
    44 }
    View Code
  • 相关阅读:
    mybatis和spring整合
    Freemarker教程1(基本使用)
    mybatis教程6(逆向工程)
    mybatis教程4(动态SQL)
    mybatis教程5(延迟加载和缓存)
    mybatis教程2(配置文件)
    python作用域
    软件测试基础面试题
    http协议
    selenium自动化测试
  • 原文地址:https://www.cnblogs.com/spilledlight/p/4869058.html
Copyright © 2020-2023  润新知