XSDL(XML Schema定义语言)由元素、属性、命名空间和XML文档种的其他节点构成的。
一、XSD中的元素
XSD文档至少要包含:schema根元素和XML模式命名空间的定义、元素定义。
1、schema根元素
语法如下:
<xsd:schema
xmlns:xsd=“http://www.w3.org/2001/XMLSchema”>
…
</xsd:schema>
在XSD中必须定义一个且只能定义一个schema根元素。根元素中包括模式的约束、XML模式命名空间的定义,其他命名空间的定义、版本信息、语言信息和其他一些属性。
2、元素
语法如下:
<xsd:element name=”user” type=”xsd:string”
/>
XSD中的元素是利用element标识符来声明的。其中name属性是元素的名字,type属性是元素值的类型。
例子:
<xsd:schema
xmlns:xsd=“http://www.w3.org/2001/XMLSchema”>
<xsd:element name=”user” type=”xsd:string”
/>
</xsd:schema>
以上文档对应的有效XML文档如下:
<?xml version=”1.0”?>
<user>string</user>
minOccurs和maxOccurs属性:
minOccurs定义了该元素在父元素中出现的最少次数(默认为1,值为大于等于0的整数),
maxOccurs定义了该元素在父元素中出现的最多次数(默认为1,值为大于等于0的整数)。在maxOccurs中可以把值设置为unbounded,表示对元素出现的最多次数没有限制。
例子:
<xsd:schema
xmlns:xsd=http://www.w3.org/2001/XMLSchema>
<xsd:element name=”user” type=”xsd:string”
minOccurs=”0” maxOccurs=”unbounded” />
</xsd:schema>
表示为元素user的类型为string,出现的次数最少为0(也就是可选),最多不限制。
3、引用元素和替代
语法如下:
<xsd:schema
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
<xsd:element name=”user” type=”xsd:string”
/>
<xsd:element name=”name”>
<xsd:complexType>
<xsd:sequence>
<xsd:element ref=”user” />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
引用是利用element标记符的ref属性实现的。主要适用于避免在文档中多次定义同一个元素,应当将经常使用的元素定义为根元素的子元素,以便在文档的任何地方引用它。
在这里还可以为某个定义的元素起一个别名(偶的理解^o^),方法如下:
<xsd:schema
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
<xsd:element name=”yonghu” type=”xsd:string”
substitutionGroup=”user” />
<xsd:element name=”user” type=”xsd:string”
/>
<xsd:element name=”name”>
<xsd:complexType>
<xsd:sequence>
<xsd:element ref=”user” />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
以上文档对应的有效XML文档如下:
<?xml version=”1.0”?>
<name>
<user>string</user>
</name>
或者:
<?xml version=”1.0”?>
<name>
<yonghu>string</yonghu>
</name>
主要是利用element标识符中的属性substitutionGroup实现的。
4、设置默认值和固定值属性 default\fixed
语法如下:
<xsd:element name=”city” type=”xsd:string”
default=”xian” />
<xsd:element name=”country” type=”xsd:string”
fixed=”china” />
5、利用组合器控制结构
sequence组合器,定义了一列元素必须按照模式中指定的顺序显示(如果是可选的,也可以不显示)。语法如下:
<xsd:schema
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
<xsd:element name=”name”>
<xsd:complexType>
<xsd:sequence>
<xsd:element name=”first” type=”xsd:string”
/>
<xsd:element name=”middle” type=”xsd:string”
/>
<xsd:element name=”last” type=”xsd:string”
/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
all组合器,允许所定义的元素可以按照任意顺序显示,all元素的子元素在默认情况下是必须的,而且每次最多显示一次。语法如下:
<xsd:schema
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
<xsd:element name=”name”>
<xsd:complexType>
<xsd:all minOccurs=”0”>
<xsd:element name=”first” type=”xsd:string”
/>
<xsd:element name=”middle” type=”xsd:string”
/>
<xsd:element name=”last” type=”xsd:string”
/>
</xsd:all>
</xsd:complexType>
</xsd:element>
</xsd:schema>
choice组合器,允许指定多组声明中的一个,用于互斥情况。语法如下:
<xsd:schema
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
<xsd:element name=”name”>
<xsd:complexType>
<xsd:choice>
<xsd:element name=”first” type=”xsd:string”
/>
<xsd:element name=”middle” type=”xsd:string”
/>
<xsd:element name=”last” type=”xsd:string”
/>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
二、定义属性
在XML
Schema文档中可以按照定义元素的方法定义属性,但受限制的程度较高。它们只能是简单类型,只能包含文本,且没有子属性。可以应用在attribute元素定义中的属性如下:
default 初始默认值
fixed 不能修改和覆盖的属性固定值
name 属性的名称
ref 对前一个属性定义的引用
type 该属性的XSD类型或者简单类型
use 如何使用属性
form 确定attributeFormDefault的本地值
id 模式文档中属性唯一的ID
Default、fixed、name、ref和type属性与在element标记中定义的对应属性相同,但type只能是简单类型。Use属性的值可以是:optional(属性不是必须的,此为默认属性)、prohibited或者required(属性是强制的)。
1、创建属性
语法如下:
<xsd:attribute name=”age” type=”xsd:integer”
/>
该语句定义了一个名为age的属性,它的值必须是一个整数。把它添加到模式中时,它必须时schema元素、complexType元素或者attributeGroup元素的子元素。
例子:
<xsd:schema
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
<xsd:element name=”name”>
<xsd:complexType>
<xsd:sequence>
<xsd:element name=”first” type=”xsd:string”
/>
</xsd:sequence>
<xsd:attribute name=”age” type=”xsd:integer”
use=”optional” />
</xsd:complexType>
</xsd:element>
</xsd:schema>
以上文档对应有效的XML文档如下:
<?xml version=”1.0”?>
<name age=”27”>
<first>string</first>
</name>
如上所示,要把属性附加在元素上,属性应该在complexType定义中的组合器之后定义或引用。
(2011-04-12 16:41)
SOAP ,WSDL 和UDDI 是Web Services 框架的核心技术..(1)SOAP 是Web
services 的通信协议。SOAP是一种简单的、轻量级的基于XML
的机制,用于在网络应用程序之间进行结构化数据交换。SOAP包括三部分:一个定义描述消息内容的框架的信封,一组表示应用程序定义的数据类型实例的编码规则,以及表示远程过程调用和响应的约定。 (2)WSDL表示WEB服务说明语言。WSDL文件是一个XML
文档,用于说明一组SOAP消息以及如何交换这些消息。 (3)UDDI(统一描述发现和集成)
提供一种发布和查找服务描述的方法。UDDI 数据实体提供对定义业务和服务信息的支持。WSDL
中定义的服务描述信息是UDDI注册中心信息的补充。
Web Services 的体系架构如图1 所示

Web Services 服务提供方通过WSDL(Web Services Description
Language) 描述所提供的服务,并将这一描述告知Web Services 注册服务器。注册服务器依据WSDL
的描述,依照UDDI (Universal Description Discovery and Integration)
的协定更新服务目录并在Internet 上发布。用户在使用Web Services 前先向注册服务器发出请求,获得Web
Services 提供者的地址和服务接口信息,之后使用SOAP 协议(Simple Object Access Protocol)
与Web Services 提供者建立连接,进行通信。Web Services 的技术主要建立在XML
的规范之上,这保证了这一体系结构的平台无关性、语言无关性和人机交互性能。
SOAP消息代理是整个框架的信息处理中心,它根据预先定义好的应用消息转换机制将消息转换成目标应用需要的数据并存储到应用消息仓库中。除了保存和转发应用消息之外,消息代理还负责将不同的企业应用所保存的应用数据通过企业模型转化为一致的企业数据,保存到数据库中。
客户端通过UDDI的标准和机制来搜寻需要的web服务,绑定找到的web服务并使用它提供的服务。另外,框架为企业进一步发展电子商务提供了接口,建立在UDDI基础上的Web
services可以完成这一功能。
.net Framework 2.0 专门提供了配置文件的操作。
namespace System.Configuration;
1. 创建配置节类
必须创建继承自ConfigurationSection的对象才能进行配置数据读写操作,ConfigurationSection提供了索引器用来获取和设置配置数据,需要注意的是拥有ConfigurationProperty特性的属性才会被存储,并且名称要保持大小写完全一致,如下面的代码中,所有的"id"必须保持一样。
class ConfigSectionData : ConfigurationSection
{
[ConfigurationProperty("id")]
public int Id
{
get { return
(int)this["id"]; }
set {
this["id"] = value; }
}
[ConfigurationProperty("time")]
public DateTime Time
{
get { return
(DateTime)this["time"]; }
set {
this["time"] = value; }
}
}
2. 创建配置文件操作对象
Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigSectionData data = new ConfigSectionData();
data.Id = 1000;
data.Time = DateTime.Now;
config.Sections.Add("add", data);
config.Save(ConfigurationSaveMode.Minimal);
上面的例子是操作 app.config,在根节点(configuration)下写入名称为"add"的配置数据。
<?xml version="1.0" encoding="utf-8"
?>
<configuration>
<configSections>
<section name="add"
type="ConsoleApplication1.ConfigSectionData,
/>
</configSections>
<add id="1000" time="02/18/2006
21:51:06" />
</configuration>
需要注意的 VS2005 在IDE模式下会将信息写入
*.vshost.exe.config,并且在程序关闭时覆写该文件,因此您可能看不到您写入的配置数据,只要在资源管理其中执行
*.exe 文件,您就可以在 *.exe.config 文件中看到结果了。
如果我们需要操作非缺省配置文件,可以使用ExeConfigurationFileMap对象。
ExeConfigurationFileMap file = new
ExeConfigurationFileMap();
file.ExeConfigFilename = "test.config";
Configuration config =
ConfigurationManager.OpenMappedExeConfiguration(file,
ConfigurationUserLevel.None);
ConfigSectionData data = new ConfigSectionData();
data.Id = 1000;
data.Time = DateTime.Now;
config.Sections.Add("add", data);
config.Save(ConfigurationSaveMode.Minimal);
如果我们不希望在根节点下写入配置数据,可以使用ConfigurationSectionGroup对象。
ExeConfigurationFileMap file = new ExeConfigurationFileMap();
file.ExeConfigFilename = "test.config";
Configuration config =
ConfigurationManager.OpenMappedExeConfiguration(file,
ConfigurationUserLevel.None);
ConfigSectionData data = new ConfigSectionData();
data.Id = 1000;
data.Time = DateTime.Now;
config.SectionGroups.Add("group1", new
ConfigurationSectionGroup());
config.SectionGroups["group1"].Sections.Add("add", data);
config.Save(ConfigurationSaveMode.Minimal);
下面就是生成的配置文件。
<?xml version="1.0"
encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="group1"
type="System.Configuration.ConfigurationSectionGroup,
>
<section name="add"
type="ConsoleApplication1.ConfigSectionData,
/>
</sectionGroup>
</configSections>
<group1>
<add id="1000" time="02/18/2006 22:01:02"
/>
</group1>
</configuration>
3. 读取配置文件
ExeConfigurationFileMap file = new ExeConfigurationFileMap();
file.ExeConfigFilename = "test.config";
Configuration config =
ConfigurationManager.OpenMappedExeConfiguration(file,
ConfigurationUserLevel.None);
ConfigSectionData data =
config.SectionGroups["group1"].Sections["add"] as
ConfigSectionData;
//ConfigSectionData data = config.Sections["add"] as
ConfigSectionData; // 从根节读取
if (data != null)
{
Console.WriteLine(data.Id);
Console.WriteLine(data.Time);
}
4. 写配置文件
在写入 ConfigurationSectionGroup 和 ConfigurationSection
前要判断同名配置是否已经存在,否则会写入失败。
另外如果配置文件被其他Configuration对象修改,则保存会失败,并抛出异常。建议采用Singleton模式。
ExeConfigurationFileMap file = new
ExeConfigurationFileMap();
file.ExeConfigFilename = "test.config";
Configuration config =
ConfigurationManager.OpenMappedExeConfiguration(file,
ConfigurationUserLevel.None);
ConfigSectionData data = new ConfigSectionData();
data.Id = 2000;
data.Time = DateTime.Now;
ConfigurationSectionGroup group1 =
config.SectionGroups["group1"];
if (group1 == null)
config.SectionGroups.Add("group1", new
ConfigurationSectionGroup());
ConfigurationSection data = group1.Sections["add"] as
config;
if (add == null)
config.SectionGroups["group1"].Sections.Add("add", data);
else
{
group1.Sections.Remove("add");
group1.Sections.Add("add", data);
// 或者直接修改原配置对象,前提是类型转换要成功。
//ConfigSectionData configData = add as
ConfigSectionData;
//configData.Id = data.Id;
//configData.Time = data.Time;
}
config.Save(ConfigurationSaveMode.Minimal);
5. 删除配置节
删除ConfigurationSectionGroup
config.SectionGroups.Remove("group1");
//config.SectionGroups.Clear();
config.Save(ConfigurationSaveMode.Minimal);
删除ConfigurationSection
config.Sections.Remove("add1");
//config.Sections.Clear();
if (config.SectionGroups["group1"] != null)
{
config.SectionGroups["group1"].Sections.Remove("add2");
//config.SectionGroups["group1"].Sections.Clear();
}
config.Save(ConfigurationSaveMode.Minimal);
6. 其他
可以使用 ConfigurationManager.OpenMachineConfiguration() 来操作
Machine.config 文件。
或者使用 System.Web.Configuration 名字空间中的 WebConfigurationManager 类来操作
ASP.net 配置文件。
ConfigurationManager还提供了AppSettings、ConnectionStrings、GetSection()等便捷操作。
【话务处理】
话务员只需直接调用本服务商的问题搜索,而不需要经过咨询调度以及答案融合
为了兼容,可以如此要求:
1,话务员ID只能对应一个服务商,这样得出的答案只有一个,即便是到了答案融全,也不作处理。
2,答案中含触发动作时,(注意,话务员搜索问题是同步进行的。)判断如果是话务员的搜索,则不向异步接出接口提交。
【客户管理】
1,客户申请如何处理。(看满意度调查的处理了)
2,如何查询客户信息?(如何设定维度)
【室、班、组的通和化】
1,多级组,有几级组对应几个表。表的设计如下:
第一级组表:
T1
id 室名 其他信息
1 室1 ..
2 室2 ..
第二级组表:
T2
id 班名 FK(外键)
1 班1 (2)T1_id
2 班2 (1)
第三级组表:
T3
id 组名 FK
1 组1
T2_id
话务员表:
C
c_id name other
1
a
..
2
c
..
c_T1_FK表:
c_id T1_FK
c_T2_FK表:
c_id T2_FK
c_T3_FK表:
c_id T3_FK
2,具体查询时,比如查询某组下的话务员可为:select name where Ti_FK=*
3,页面生成:
1>维度生成:读取有几级,在页面上显示有几个框。
2>维度选择:父子级之间的关联,即父级改变时,子级数据需要动态跟着改变。实现方法:
当父级改变时,查找下一级(因为是在一张表中)用where语句实现
c#网页开发二
【js文件】
存储javascript代码的文件,javascript代码应该放在.js文件中,便于管理。
【c#和javascript交互】
1,在后台C#代码中调用javascript方法:
* javascript代码:
<script type="text/javascript"
language="javascript">
function
test()
{
alert("oec2003");
return false;
}
</script>
* c#代码:
protected void Button1_Click(object sender, EventArgs e)
{
ClientScript.RegisterStartupScript(this.GetType(), "clear",
"<script>test()</script>");
}
2, javascript中调用c#方法
1>, 如果c#中的方法有返回值,可以用下面方法
c#代码
public string GetAuthStatus()
{
ViewState["Auth"] = "Red";
return ViewState["Auth"].ToString();
}
javascript代码
<script type="text/javascript"
language="javascript">
function
getAuth()
{
var authStatus =
"<%=GetAuthStatus()%>";
return authStatus;
}
</script>
2>,
(暂未明白)如果在javascript调用的c#方法没有返回值,可以在一面中放一个button,然后在button的单击事件中去写想做的事情,在客户端的脚本中写下如下代码就可以了document.all("button1").click();
【标准服务器控件】
【Label】 在页面上显示静态文本的标签控件 相当于HTML的<div>元素。
如:
<div>
<asp:Label ID="Label1"
runat="server"
Text="所要显示的文字"></asp:Label>
</div>
【Button】
1,相关代码
<div>
<asp:Button
ID="Button1" runat="server" Text="确定" />
</div>
2, click和Command事件
click事件:双击自动生成button_click事件,在里面添写代码即可。
command事件:通过关联按钮的CommandName属性,使按钮可以自动寻找并调用特定的方法,还可以通过CommandArgument属性向该方法传递参数。优点:当有多个Button按钮,按钮的操作相似,如果采用click事件,则每个button就必须单独实现click。采用Command事件用可通过一个公共的处理方法来完成。如:
<form id="form1"
runat="server">
<div>
<asp:Button ID="Button1" runat="server"
Text="递增显示数字" CommandName="ShowNumbers_Asc" CommandArgument="Asc"
/>
<asp:Button ID="Button3" runat="server"
Text="递减显示字母" CommandName="ShowLetters_Desc" CommandArgument="Desc"
/>
</div>
</form>
Command事件到Button_Command()方法:在属性里,将两个按钮的Command事件都关联到Button_Command方法即可。
上面两个按钮的公共command方法定义如下:
protected void Button_Command(object sender,
CommandEventArgs e)
{
//根据按钮的CommandName进行分支
switch (e.CommandName)
{
case "ShowNumbers_Asc":
//输出一行说明信息
Page.Response.Write("您单击了按钮“递增显示数字”!<br>");
//调用不同的方法,并传递CommandArgument参数
ShowNumbers(e.CommandArgument);
break;
case "ShowLetters_Desc":
Page.Response.Write("您单击了按钮“递减显示字母”!<br>");
ShowLetters(e.CommandArgument);
break;
default:
break;
}
}
【TextBox】
<form id="Form1" method="post"
runat="server">
<asp:TextBox id="TextBox1"
runat="server"></asp:TextBox>
</form>
【CheckBox】:复选框
<div>
<asp:CheckBox ID="CheckBox1" runat="server"
/>
</div>
事件,CheckedChanged:当Checked属性值更改时触发。
【属性和事件】
一般C#不需要直接手动写代码,右击在属性和事件中直接选择就行。
比如:上面button的单击事件,可以双击自动生成button_click,也可自己在属性中定义。Command事件也是这样。上面的CheckBox的CheckedChanged事件也是可选择的,不过一般会有默认的事件名,一般采用这些自动的事件名即可。
【RadioButton】:单选框
其它相应事件,可直接查相应文档即可。不再一一举出。
【容器控件】
【Panel】其他控件的容器。作为容器,它可以统一控制其内部的一组控件,比如隐藏/显示等。
<div>
<asp:Panel id="Panel1"
runat="server" >
<asp:Button
id="Button1" runat="server" Text="Button" />
</asp:Panel>
</div>
===属性===
DefaultButton 获取或设置Panel控件中包含的默认按钮的标识符
Direction 获取或设置在Panel控件中显示包含文本的控件的方向
ScrollBars 获取或设置Panel控件中滚动条的可见性和位置
HorizontalAlign 获取或设置面板中的控件的水平对齐方式
Visible 获取或设置一个值,该值指示面板,及其内部的所有控件是否呈现在页面上
==========
注,以下先设置一个控件,然后添加到面板中去。
label1.BorderWidth=1; //设置标签Border线条宽度
label1.BorderColor=System.Drawing.Color.Black;
Panel1.Controls.Add(label1); //将标签添加今Panel中
Panel1.Controls.Add(new
LiteralControl("<br>"));//在Panel中添加一个"<br>"
【Table】主要功能是控制页面上元素的布局。作为Web服务器控件,Table可以根据不同的用户响应,动态生成表格的结构。
<asp:Table id="Table1"
runat="server">
<asp:TableRow>
<asp:TableCell></asp:TableCell>
<asp:TableCell></asp:TableCell>
</asp:TableRow>
</asp:Table>
注,初始添加的Table不包含行,可以通过编辑其Rows属性来添加
【验证控件】
RequiredFieldValidator 要求用户必真,如“用户名”项
CompareValidator 将用户的输入与常数、另一个控件或数据库值进行比较,如:“重复密码”项
RangeValidator 确保用户输入的值在指定的上下限范围之内。如:“年龄”项
RegularExpressionValidator 确保用户输入匹配正则表达式定义的模式,如:“电子邮件”项
CustomValidator 确保用户输入的内容符合自定义的验证逻辑,如:“同意”单选按钮
ValidationSummary 在网页、消息框或在这两者中内联显示所有验证错误的摘要,如:“提交”按钮下面的所有出错信息
【RequiredFieldValidator】
<div>
<asp:TextBox ID="TextBox1"
runat="server"></asp:TextBox>
<asp:RequiredFieldValidator
ID="RequiredFieldValidator1" runat="server"
Text="TextBox1未填时的报错信息"
ControlToValidate="TextBox1">
</asp:RequiredFieldValidator>
</div>
属性:
ControlToValidate:选项可选择其监督的控件
ErrorMessage:必须结合ValidationSummary控件使用,验证失败时在后者中显示的错误信息的文本。
Text:获取或设置验证失败时验证控件中显示的文本。
【用户控件】
当一个Web窗体被当作Server控件使用时,这个Web窗体便是用户控件。
1,创建用户控件
【添加新项】命令,弹出的“添加新项”对话框,选择“Web用户控件”图标。然后在其中添加内置的服务器控件。
创建的用户控件此时还没有外观和属性。
2,添加用户控件
1>
首先在需要引入用户控件的Web页面内,使用“<%@
Register…%>”指令注册控件。在本例中,需要将下面的代码添加在Default的HTML代码的首行:
<%@ Register TagPrefix="uc" TagName="MyDataInput"
Src="~/MyDataInput.ascx" %>
其中,TagPrefix确定了用户控件的命名空间,TagName是这个用户的名字,它们的属性值可以随便取,但是一旦取定
2> 引用控件:
在Web页面中引入用户控件后,就可以以同Web服务器控件同样的方式使用了,方法是在需要使用控件的地方写入代码:
<TagPrefix : TagName runat="server"
/>
本例中,将在Default的HTML中添加如下代码:
<div>
<uc:MyDataInput ID="MyDataInput1" runat="server"
/>
</div>
3, 定义用户控件属性
(1)添加一个Text属性,这个属性的值即是用户控件中文本框中的值。
首先在.cs文件中写入如下代码:
public string Text
{
get{ return this.TextBoxDate.Text; }
set{ this.TextBoxDate.Text= value; }
}
(2)在Default.aspx页面引用用户控件的地方,可以直接声明其属性的值:
<div>
<uc:MyDataInput ID="MyDataInput1" Text="2007-2-2"
runat="server" />
</div>
(3)在Default.aspx页面的加载事件中,利用Text属性输出文本框中的日期数据:
protected void Page_Load(object sender, EventArgs
e)
{
Page.Response.Write(this.MyDataInput1.Text + "<br
/>");
}
4,定义用户控件事件
(1)首先定义按钮单击事件。双击用户控件MyDateInput.ascs中的Button1控件,在其单击事件中输入以下代码:
代码(MyDateInput用户控件的按钮单击事件:MyDateInput.ascx.cs)
/// <summary>
/// “...”按钮单击事件
///
</summary>
protected void OnClick(object sender, EventArgs
e)
{
PanelCalendar.Visible = true;
}
代码功能很简单,就是显示包含日期控件的Panel控件,这样的效果就是当单击“…”按钮时,日期控件就会跳出来,以供用户选择日期。
(2)下面实现日历控件的选择事件。双击日历控件,在其选择事件中实现如下代码:
代码10-4
MyDateInput用户控件的日历选择事件:MyDateInput.ascx.cs
/// <summary>
/// 日期选择事件
///
</summary>
protected void Calendar1_SelectionChanged(object
sender, EventArgs e)
{
TextBoxDate.Text =
Calendar1.SelectedDate.ToShortDateString();
PanelCalendar.Visible = false;
}
代码一方面隐藏掉日期控件,同时为Text赋值为选中的日期。
【注意】通过上面的例子可以看出,在用户控件中处理事件的方法,同在普通的Web页面中处理方法是一样的。
【数据绑定控件】
【GridView】
<div>
<asp:GridView ID="GridView1"
runat="server">
</asp:GridView>
</div>
1, GridView中的列类型
BoundField:GridView控件中作为文本显示的字段。GridView为数据源中的每一列自动创建一个BoundField列,其顺序与数据源中的字段顺序相同,并且列的标题即为该列在数据源中的列名。
CheckBoxField:GridView控件中以复选框显示的布尔型字段。
HyperLinkField:控件中显示为超链接的字段,超级链接列。
ImageField:图象列,列中显示对应数据库中的图象。
ButtonField:按钮列,按钮的类型可以为Button、Image、Link。
CommandField:一个特殊列,其中显示了用于在GridView控件中执行选择、编辑、插入或删除操作的命令按钮。这些命令按钮不需要编写任何代码就可以实现相应的操作。
TemplateField:模板列,列中的各项内容根据按照指定的模板显示,常用于显示控件等。
2, 自动生成数据列:操作相关,很麻烦。
【列表控年】
1,把DataSet绑定到DropDownList
2,把DataReader绑定到ListBox
3,把DataTable绑定到CheckBoxList
4,把HashTable绑定到RadioButtonList
【站点导航控件】
【登陆控件】
【HTML 服务器控件】:
属于 System.Web.UI.HtmlControls 命名空间的 ASP.NET 服务器控件。HTML 服务器控件直接映射到
HTML 元素,并在 ASP.NET 页中声明为一个由 runat="server" 属性标记的 HTML 元素,例如
<button runat="server" />。与 Web
服务器控件相比,HTML 服务器控件没有 asp 标记前缀。
--?还是没搞懂什么意思?与直接拖个框,再点击生成Button1_Click,其aspx中代码也是:<asp:Button
ID="Button1" runat="server" OnClick="Button1_Click" Text="Button"
/>也同样有: runat="server" ???
按王石的意思,估计是:与标准控件很像,只是兼容了javascript方法,直接点击能生成javascript点击方法,如果不想生成javascript方法,可以右击进行生成???
注意在html中,只有javascript脚本,而没有<asp>等这种asp代码,这些代码都经过服务器解析后,生成具体的代码发送到客户端。
??ajax如何与C#绑定在一起?
参见:http://www.w3school.com.cn/html/html_paragraphs.asp
一,【名词解释】
【标签tag】:像<head>
<body>这些都是标签。
二,【基本功能】
1, 图片:<img src="w3school.jpg" width="104"
height="142" />
2, <p> 元素定义了 HTML 文档中的一个段落。
3, 空的 HTML 元素:<br />
就是没有关闭标签的空元素,用于定义换行。
4, 【注意】:HTML的标签对大小写不敏感
5, HTML 属性:
1> href属性
<a
href="www.3.com.cn">This
is a link</a>
--网页上就会出现This is a
link.点击这串文字就会跳到www.3.com.cn上面。注意a标签的用法。
2>
<h1> 定义标题的开始。<h1
align="center">...</h1>
中间这部分文字就会居中作为标题显示
3>
<body bgcolor="yellow"> 定义背景颜色
6, 列表:
1> 无序列表
<ul>
<li>Coffee</li>
<li>Milk</li>
</ul>
2> 有序列表
将上面的<ul>换成<ol>
7, 定义列表: 自定义列表以 <dl> 标签开始。每个自定义列表项以
<dt> 开始。每个自定义列表项的定义以
<dd> 开始。
8,
表单<form>:表单是一个包含表单元素的区域。允许用户在表单中(比如:文本域、下拉列表、单选框、复选框等等)输入信息的元素。【注意:】其中输入标签一般为<input>,其类型由type指定。
8, 文本域: <input type="text" name="firstname"
/>
9, 单选按钮: 比如:从male和female两项中选择一个:
<form>
<input type="radio" name="sex"
value="male" /> Male
<br />
<input type="radio" name="sex"
value="female" /> Female
</form>
10, 复选框,将上面的"radio"改成"checkbox"即可
11, 提交:type="submit"
12, 表格
<table
border="1">
<tr>
<td>row
1, cell 1</td>
<td>row
1, cell 2</td>
</tr>
<tr>
<td>row
2, cell 1</td>
<td>row
2, cell 2</td>
</tr>
</table>
13,布局:使用表格来完成
14,框架:将多个页面放在一个窗口里。
1>
<frameset>定义如何将窗口分割为框架;每个 frameset
定义了一系列行或列,rows/columns 的值规定了每行或每列占据屏幕的面积
<frameset
cols="25%,75%">
//有两列,各对应一个框架,第一个框架占25%,第二个占75%
<frame src="frame_a.htm">
<frame src="frame_b.htm">
</frameset>
2> 导联框架:
<frameset
cols="120,*">//旁边出现三个链接,点击哪个,出现哪个。
<frame
src="/example/a.html">
<frame
src="/example/b.html" name="showframe">
</frameset>
【注意】:1,假如一个框架有可见边框,用户可以拖动边框来改变它的大小。为了避免这种情况发生,可以在
<frame>
标签中加入:noresize="noresize"。
2,不能将
<body></body>
标签与
<frameset></frameset>
标签同时使用!不过,假如你添加包含一段文本的 <noframes>
标签,就必须将这段文字嵌套于
<body></body>
标签内。
15, <meta>:永远位于 head 元素内部。meta
的属性有两种:name和http-equiv。name属性主要用于描述网页,对应于content(网页内容),以便于搜索引擎机器人查找、分类。
比如:
HTML meta charset 定义网页编码信息
HTML meta content-language 定义页面语言
HTML meta refresh 刷新与跳转(重定向)页面
HTML meta expires 网页缓存过期时间
HTML meta pragma no-cache 页面缓存
HTML meta keywords 网页关键词
HTML meta description 网页简短描述
HTML meta author 网页作者
HTML meta copyright 网页版权
HTML meta date 网页生成时间
HTML meta robots 搜索引擎索引方式
16, <font>:字体标签
17, 样式CSS:
1>,
<head>
<link rel="stylesheet"
type="text/css" href="test.css" >
//指定用test.css样式表中的样式。
</head>
<body>
<h1>我通过外部样式表进行格式化。</h1>
<p>我也一样!</p>
</body>
</html>
2>,
写样式表:【详见相关css教程】,此处知道有这东西,即可。
18, 实体:用&, #,分号";"进行连接。常用实体如下:
显示结果 描述 实体名称 实体编号
空格
 
<
小于号
<
<
>
大于号
>
>
&
和号
&
&
"
引号
"
"
'
撇号 '
(IE不支持) '
19, 脚本:<script>
20, 属性:
1> 核心属性:
属性
值
描述
class
classname
规定元素的类名(classname)
id
id
规定元素的唯一 id
style
style_definition 规定元素的行内样式(inline style)
title
text
规定元素的额外信息(可在工具提示中显示)
2> 键盘属性:
属性
值
描述
accesskey character
设置访问元素的键盘快捷键。
tabindex
number 设置元素的
Tab 键控制次序。
21, 事件,参见jsp学习
【参考javascript教程 从入门到精通】
JavaScript,介于Java与HTML之间、基于对象事件驱动的编程语言
弱变量:JavaScript提供了string(字符串)、math(数值计算)和Date(日期)
简单例子:
<html><head>
<Script Language-"JavaScript">
alert("JavaScript代码");//alert是javascript窗口对象方法
var x=1234; //数值型
var y="4321";//字符串型
</Script></head><html>
另外还有:
document.Write("...")//输出方法
document.close();
【JavaScript函数定义】
Function 函数名 (参数,变元){
函数体;.
return 表达式;
}
Function function_Name(exp1,exp2,exp3,exp4)
Number =function _Name.arguments.length;//可通过arguments
.Length来检查参数的个数。
【事件驱动及事件处理】
通常鼠标或热键的动作我们称之为事件(Event),而由鼠标或热键引发的一连串程序的动作,称之为事件驱动(Event
Driver)。而对事件进行处理程序或函数,我们称之为事件处理程序(Event Handler)。
Function 事件处理名(参数表){
事件处理语句集;
……
}
主要事件:
【单击事件onClick】
<Form>
<Input type="button" Value=“ ”
onClick="change()">
</Form>
在上面onClick=后,可以使用自己编写的函数作为事件处理程序,也可以使用JavaScript中内部的函数。还可以直接使用JavaScript的代码等。如:<Input
type="button" value=" " onclick=alert("例子");从而实现事件和处理函数的关联。
【onChange改变事件】
当利用text或texturea元素输入字符值改变时,或select表单状态变化时发该事件。
<Form>
<Input type="text" name="Test" value="Test"
onCharge="check('this.test)">
</Form>
【选中事件onSelect】
【失去焦点onBlur】
【载入文件onLoad】
【卸载文件onUnload】
【几个常用的语法】
for(var prop in object)
...
//在该语句体内,任何对变量的引用被认为是这个对象的属性,以节省一些代码。(类似结构体)
with object{
...}
this关键字: 对当前的引用
【创建自己的对象】:Newobject=NEW Object(Parameters table);
比如,创建一个University对象,包含字段name, city, creatDate,
url,以及方法:show()。定义如下:
Function university(name,city,creatDate URL)
This.name=name
This.city=city
This.creatDate=New Date(creatDate)
This.URL=URL
This.show=showuniversity;
其中showuniversity为一方法showuniversity(),在别处定义:function
showuniversity(){...}
定义完后,创建实例:U1=New university(“云南省”,“昆明市”,"January
05,199712:00:00","http://www.YN.KM")
【JavaScript中的数组 】
JavaScript中没有提供像其它语言具有明显的数组类型,但可以通过function定义一个数组,创建一个具有下标的数组。从而可以实现任何数据类型的存储。
定义对象的数组
Function arrayName(size){
This.length=Size;
for(var X=; X<=size;X++)
this[X]=0;
Reture this;
}
创建:Myarray=New arrayName(n);
【浏览器对象层次及其主要作用】
Navigator浏览器中提供了:
【浏览器对象(Navigator)】
提供有关浏览器的信息
【窗口对象(Windows)】
Window对象处于对象层次的最顶端,它提供了处理Navigator窗口的方法和属性。
【位置对象(Location)】
Location对象提供了与当前打开的URL一起工作的方法和属性,它是一个静态的对象。
【历史对象(History)】
History对象提供了与历史清单有关的信息。
【文档对象(Document)】
document对象包含了与文档元素(elements)一起工作的对象,它将这些元素封装起来供编程人员使用。
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://xxinside.blogbus.com/logs/47617134.html
WaitHandle一家
在前一篇我们已经提到过Mutex和本篇的主角们直接或间接继承自WaitHandle:
WaitHandle提供了若干用于同步的方法。上一篇关于Mutex的blog中已经讲到一个WaitOne(),这是一个实例方法。除此之外,WaitHandle另有3个用于同步的静态方法:
- SignalAndWait(WaitHandle,
WaitHandle):以原子操作的形式,向第一个WaitHandle发出信号并等待第二个。即唤醒阻塞在第一个WaitHandle上的线程/进程,然后自己等待第二个WaitHandle,且这两个动作是原子性的。跟WaitOne()一样,这个方法另有两个重载方法,分别用Int32或者TimeSpan来定义等待超时时间,以及是否从上下文的同步域中退出。
- WaitAll(WaitHandle[]):这是用于等待WaitHandle数组里的所有成员。如果一项工作,需要等待前面所有人完成才能继续,那么这个方法就是一个很好的选择。仍然有两个用于控制等待超时的重载方法,请自行参阅。
- WaitAny(WaitHandle[]):与WaitAll()不同,WaitAny只要等到数组中一个成员收到信号就会返回。如果一项工作,你只要等最快做完的那个完成就可以开始,那么WaitAny()就是你所需要的。它同样有两个用于控制等待超时的重载。
线程相关性(Thread Affinity )
EventWaitHandle和Mutex两者虽然是派生自同一父类,但有着完全不同的线程相关性:
- Mutex与Monitor一样,是“线程相关(Thread
Affinity)”的。我们之前已经提到过,只有通过Monitor.Enter()/TryEnter()获得对象锁的线程才能调用Pulse()/Wait()/Exit();同样的,只有获得Mutex拥有权的线程才能执行ReleaseMutex()方法,否则就会引发异常。这就是所谓的线程相关性。
-
相反,EventWaitHandle以及它的派生类AutoResetEvent和ManualResetEvent都是线程无关的。任何线程都可以发信号给EventWaitHandle,以唤醒阻塞在上面的线程。
- 下一篇要提到的Semaphore也是线程无关的。
Mutex与Event
我们在Mutex一篇中没有具体提到Mutex是否能发送信号,只是简单说Mutex不太适合有相互消息通知的同步,它仅有的一些同步方法是来自其父类的静态方法。那么现在我们可以仔细来看看Mutex到底能不能用于关于Monitor那篇提到的生产者、消费者和糖罐的场景。
回过头来仔细查看Mutex的所有方法,除了一个我们已经提到的WaitHandle上的静态方法SingnalAndWait(toSingnal,
toWaitOn),我们找不到任何“属于Mutex自己”的、用于发送信号的方法。退而求其次吧,我们就来看看这个静态方法是否可以让Mutex具有通知的能力。
如果toSignal是一个Mutex,那么收到“信号”就等效于ReleaseMutex()。而由于Mutex的线程相关性,只有拥有当前Mutex的线程才能够发送这个信号(ReleaseMutex),否则会引发异常。也就是说如果要用这个方法来通知其它线程同步,Mutex只能自己发给自己。与之相反,如果第二个参数toWaitOn也是个Mutex,那么这个Mutex不能是自己。因为前篇已经讲过,Mutex的拥有者可以多次WaitOne()而不阻塞,这里也是一样。所以如果Mutex一定要使用这个方法,准确的说是只是成为这个方法的参数,那只能是WaitHandle.SignalAndWait(它自己,另一个Mutex)。
试想,如果有人试图只使用Mutex来进行同步通知。假设生产者线程通过Mutex上的WaitOne()获得了mutexA的拥有权,并且在生产完毕后调用了SingnalAndWait(mutexA,mutexB),通知由于当前mutexA而阻塞的消费者线程,并且将自己阻塞在mutexB上。那么被唤醒的消费者线程获得MutexA的拥有权吃掉糖后,也只能调用SingnalAndWait(mutexA,mutexB)释放它获得的mutexA且阻塞于MutexB。问题来了,此时的生产者是阻塞在mutexB上……也许,我们可以设计一段“精巧”的代码,让生产者和消费者一会儿阻塞在mutexA,一会儿阻塞在mutexB上……我不想花费这个力气去想了,你可以试试看:)。不管有没有这样的可能,Mutex很明显就不适用于通知的场景。
EventWaitHandle的独门秘笈
正因为Mutex没有很好地继承父辈的衣钵,EventWaitHandle以及它的儿子/女儿们便来到了这个世界上。
EventWaitHandle、AutoResetEvent、ManualResetEvent名字里都有一个“Event”,不过这跟.net的本身的事件机制完全没有关系,它不涉及任何委托或事件处理程序。相对于我们之前碰到的Monitor和Mutex需要线程去争夺“锁”而言,我们可以把它们理解为一些需要线程等待的“事件”。线程通过等待这些事件的“发生”,把自己阻塞起来。一旦“事件”完成,被阻塞的线程在收到信号后就可以继续工作。
为了配合WaitHandle上的3个静态方法SingnalAndWait()/WailAny()/WaitAll(),EventWaitHandle提供了自己独有的,使“Event”完成和重新开始的方法:
- bool:Set():英文版MSDN:Sets the state of the event
to signaled, allowing one or more waiting threads to
proceed;中文版MSDN:将事件状态设置为终止状态,允许一个或多个等待线程继续。初看“signaled”和“终止”似乎并不对应,细想起来这两者的说法其实也不矛盾。事件如果在进行中,当然就没有“终止”,那么其它线程就需要等待;一旦事件完成,那么事件就“终止”了,于是我们发送信号唤醒等待的线程,所以“信号已发送”状态也是合理的。两个小细节:
-
无论中文还是英文版,都提到这个方法都是可以让“一个”或“多个”等待线程“继续/Proceed”(注意不是“唤醒”)。所以这个方法在“唤醒”这个动作上是类似于Monitor.Pulse()和Monitor.PulseAll()的。至于什么时候类似Pulse(),又在什么时候类似PulseAll(),往下看。
-
这个方法有bool型的返回值:如果该操作成功,则为true;否则,为false。不过MSDN并没有告诉我们,什么时候执行会失败,你只有找个微软MVP问问了。
- bool:Reset():Sets
the state of the event to nonsignaled, causing threads to block.
将事件状态设置为非终止状态,导致线程阻止。同样,我们需要明白“nonsignaled”和“非终止”是一回事情。还同样的是,仍然有个无厘头的返回值。Reset()的作用,相当于让事件重新开始处于“进行中”,那么此后所有WaitOne()/WaitAll()/WaitAny()/SignalAndWait()这个事件的线程都会再次被挡在门外。
来看看EventWaitHandle众多构造函数中最简单的一个:
- EventWaitHandle(Boolean
initialState, EventResetMode
mode):初始化EventWaitHandle类的新实例,并指定等待句柄最初是否处于终止状态,以及它是自动重置还是手动重置。大多数时候我们会在第一个参数里使用false,这样新实例会缺省为“非终止”状态。第二个参数EventResetMode是一个枚举,一共两个值:
-
EventResetMode.AutoReset:当Set()被调用当前EventWaitHandle转入终止状态时,若有线程阻塞在当前EventWaitHandle上,那么在释放一个线程后EventWaitHandle就会自动重置(相当于自动调用Reset())再次转入非终止状态,剩余的原来阻塞的线程(如果有的话)还会继续阻塞。如果调用Set()后本没有线程阻塞,那么EventWaitHandle将保持“终止”状态直到一个线程尝试等待该事件,这个该线程不会被阻塞,此后EventWaitHandle才会自动重置并阻塞那之后的所有线程。
-
EventResetMode.ManualReset:当终止时,EventWaitHandle
释放所有等待的线程,并在手动重置前,即Reset()被调用前,一直保持终止状态。
好了,现在我们可以清楚的知道Set()在什么时候分别类似于Monitor.Pulse()/PulseAll()了:
-
当EventWaitHandle工作在AutoReset模式下,就唤醒功能而言,Set()与Monitor.Pulse()类似。此时,Set()只能唤醒众多(如果有多个的话)被阻塞线程中的一个。但两者仍有些差别:
-
Set()的作用不仅仅是“唤醒”而是“释放”,可以让线程继续工作(proceed);相反,Pulse()唤醒的线程只是重新进入Running状态,参与对象锁的争夺,谁都不能保证它一定会获得对象锁。
-
Pulse()的已被调用的状态不会被维护。因此,如果在没有等待线程时调用Pulse(),那么下一个调用Monitor.Wait()的线程仍然会被阻塞,就像Pulse()
没有被被调用过。也就是说Monitor.Pulse()只在调用当时发挥作用,并不象Set()的作用会持续到下一个WaitXXX()。
-
在一个工作在ManualReset模式下的EventWaitHandle的Set()方法被调用时,它所起到的唤醒作用与Monitor.PulseAll()类似,所有被阻塞的线程都会收到信号被唤醒。而两者的差别与上面完全相同。
来看看EventWaitHandle的其它构造函数:
好啦,都差不多了,可以写一个例子试试了。让我们回到Monitor一篇中提到的生产者和消费者场景,让我们看看EventWaitHandle能不能完成它兄弟Mutex没有能完成的事业。不过,即便有强大通信能力的EventWaitHandle出马,也避免不要使用lock/monitor或是Mutex。原因很简单,糖罐是一个互斥资源,必须被互斥地访问。而EventWaitHanldle跟Mutex相反,能通信了但却完全失去了临界区的能力。所以,这个例子其实并不太适合展示EventWaitHandle的通信机制,我只是为了想用同样的例子来比较这些同步机制间的差异。
EventWaitHandle虽然还必须借助lock/Monitor/Mutex来实现这个例子(仅仅是临界区部分),但是它终究有强于Monitor的通信能力,所以让我们来扩展一下这个例子:现在有一个生产者,有多个消费者。
-
我们让消费者在没有糖吃或吃完一块糖后阻塞在一个工作在ManualReset模式下的EventWaitHandle,生产者在生产完毕后就通过这个事件唤醒所有消费者吃糖。由于我们使用了lock的关系,虽然所有消费者都被唤醒,但是他们还是因为争夺糖罐的关系只有一个能进入临界区吃糖。不过此时阻塞的原因并不是因为我们的通知时间,而是临界区的问题。
-
每个消费者有一条专线,即一个工作在AutoRest模式下的EventWaitHandle,用于在吃完糖后通知生产者。而生产者用WaitAny()来等待消费者吃糖时间的发生,只要有任一消费者吃完糖,那么生产者就试图争夺对糖罐的拥有权,把糖罐塞满(一人一颗的标准)。消费者这里使用了WaitAndSignal给生产者发消息,并等待生产者进入临界区生产糖后通知他们。在这样的设计逻辑下,可能糖罐中的糖还没有全部吃完生产者就有机会再次把糖罐装满。当然,你也可以使用了WaitAll()来等待所有消费者吃完再进行生产。
using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading;
class WaitEventHandleSample:IDisposable
{
private
volatile bool _shouldStop = false; //用于控制线程正常结束的标志
private
const int _numberOfConsumer = 5; //消费者的数目
//容器,一个只能容纳一块糖的糖盒子。PS:现在MS已经不推荐使用ArrayList,
//支持泛型的List才是应该在程序中使用的,我这里偷懒,不想再去写一个Candy类了。
private
ArrayList _candyBox = null;
private EventWaitHandle _EvntWtHndlProduced = null;
//生产完成的事件,ManualReset,用于通知所有消费者生产完成
private
EventWaitHandle[] _EvntWtHndlConsumed = null;
//消费完成的事件,AutoReset,每一个消费线程对应一个事件,用于通知生产者有消费动作完成
///
<summary>
///
用于结束Produce()和Consume()在辅助线程中的执行
///
</summary>
public void
StopThread()
{
_shouldStop = true;
//叫醒阻塞中的消费者,让他们看到线程结束标志
if (_EvntWtHndlProduced != null)
{
_EvntWtHndlProduced.Set();
};
//叫醒阻塞中的生产者,让他看到线程结束标志
if (_EvntWtHndlConsumed != null)
{
for (int i = 0; i < _numberOfConsumer; i++)
{
if (_EvntWtHndlConsumed[i] != null)
{
_EvntWtHndlConsumed[i].Set();
};
}
}
}
///
<summary>
///
生产者的方法
///
</summary>
public void
Produce()
{
if (_candyBox == null)
{
Console.WriteLine("生产者:糖罐在哪里?!");
}
else if (_EvntWtHndlConsumed == null)
{
Console.WriteLine("生产者:消费者们在哪里?!");
}
else if (_EvntWtHndlProduced ==
null)
//这个事件用于唤醒所有消费者,因此象个喇叭
{
Console.WriteLine("生产者:喇叭坏啦,没办法通知消费者!");
}
else
{
//逐一检查消费者是否到位
for (int i = 0; i < _numberOfConsumer; ++i)
{
if (_EvntWtHndlConsumed[i] == null)
{
Console.WriteLine("生产者:消费者{0}在哪里?!", i);
return;
}
else
{
//什么也不做
};
};
int numberOfSugarProduced = 0; //本次一共生产了多少颗糖
while (!_shouldStop)
{
lock (_candyBox)
{
if (_candyBox.Count < _numberOfConsumer)
{
numberOfSugarProduced = 0;
while (_candyBox.Count <
_numberOfConsumer) //一共有多少个消费者就生产多少块糖
{
//生产一块糖
_candyBox.Add("A Candy");
++numberOfSugarProduced;
};
Console.WriteLine("生产者:这次生产了{0}块糖,罐里现在一共有{1}块糖!",
numberOfSugarProduced, _candyBox.Count);
Console.WriteLine("生产者:赶快来吃!!");
}
else //容器是满的
{
Console.WriteLine("生产者:糖罐是满的!");
};
};
//通知消费者生产已完成
_EvntWtHndlProduced.Set();
//只要有消费者吃完糖,就开始生产
EventWaitHandle.WaitAny(_EvntWtHndlConsumed);
Thread.Sleep(2000);
};
Console.WriteLine("生产者:下班啦!");
}
}
///
<summary>
///
消费者的方法
///
</summary>
///
<param
name="consumerIndex">消费者序号,用于表明使用哪个_EvntWtHndlConsumed成员</param>
public void
Consume(object consumerIndex)
{
int index = (int)consumerIndex;
if (_candyBox == null)
{
Console.WriteLine("消费者{0}:糖罐在哪里?!",index);
}
else if (_EvntWtHndlProduced == null)
{
Console.WriteLine("消费者{0}:生产者在哪里?!",index);
}
else if (_EvntWtHndlConsumed == null || _EvntWtHndlConsumed[index]
== null)
{
Console.WriteLine("消费者{0}:电话坏啦,没办法通知生产者!",
index); //由于每个消费者都有一个专属事件通知生产者,因此相当于电话
}
else
{
while (!_shouldStop || _candyBox.Count > 0)
//即便看到结束标致也应该把容器中的所有资源处理完毕再退出,否则容器中的资源可能就此丢失。需要指出_candybox.Count是有可能读到脏数据的
{
lock (_candyBox)
{
if (_candyBox.Count > 0)
{
if (!_shouldStop)
{
_candyBox.RemoveAt(0);
Console.WriteLine("消费者{0}:吃了1颗糖,还剩{1}颗!!", index,
_candyBox.Count);
Console.WriteLine("消费者{0}:赶快生产!!",index);
}
else
{
Console.WriteLine("消费者{0}:我来把剩下的糖都吃了!",index);
while (_candyBox.Count > 0)
{
_candyBox.RemoveAt(0);
Console.WriteLine("消费者{0}:吃了1颗糖,还剩{1}颗!!", index,
_candyBox.Count);
}
break;
}
}
else
{
Console.WriteLine("消费者{0}:糖罐是空的!",index);
Console.WriteLine("消费者{0}:赶快生产!!",index);
}
}
WaitHandle.SignalAndWait(_EvntWtHndlConsumed[index],
_EvntWtHndlProduced);
Thread.Sleep((index+1)*1500);
}
}
Console.WriteLine("消费者{0}:都吃光啦,下次再吃!",index);
}
///
<summary>
///
初始化所需的各EventWaitHandle和糖罐等
///
</summary>
public void
Initialize()
{
if (_candyBox == null)
{
_candyBox = new ArrayList(_numberOfConsumer);
//按有多少消费者最多生产多少糖的标准初始化糖罐大小
}
else
{
//什么也不做
}
if (_EvntWtHndlProduced == null)
{
_EvntWtHndlProduced = new EventWaitHandle(false,
EventResetMode.ManualReset);
}
else
{
//什么也不做
}
if (_EvntWtHndlConsumed == null)
{
_EvntWtHndlConsumed = new EventWaitHandle[_numberOfConsumer];
for (int i = 0; i < _numberOfConsumer; ++i)
{
_EvntWtHndlConsumed[i] = new EventWaitHandle(false,
EventResetMode.AutoReset);
}
}
else
{
//什么也不做
}
}
static void Main(string[] args)
{
WaitEventHandleSample ss = new WaitEventHandleSample();
try
{
ss.Initialize();
//Start threads.
Console.WriteLine("开始启动线程,输入回车终止生产者和消费者的工作……\r\n******************************************");
Thread thdProduce = new Thread(new ThreadStart(ss.Produce));
thdProduce.Start();
Thread[] thdConsume = new Thread[_numberOfConsumer];
for (int i = 0; i < _numberOfConsumer; ++i)
{
thdConsume[i] = new Thread(new
ParameterizedThreadStart(ss.Consume));
thdConsume[i].Start(i);
}
Console.ReadLine();
//通过IO阻塞主线程,等待辅助线程演示直到收到一个回车
ss.StopThread(); //正常且优雅的结束生产者和消费者线程
thdProduce.Join();
for (int i = 0; i < _numberOfConsumer; ++i)
{
thdConsume[i].Join();
}
Console.WriteLine("******************************************\r\n输入回车结束!");
Console.ReadLine();
}
finally
{
ss.Dispose();
ss = null;
};
}
#region IDisposable
Members
public void Dispose()
{
if (_candyBox != null)
{
_candyBox.Clear();
_candyBox = null;
}
else
{
//什么也不做
}
if (_EvntWtHndlProduced != null)
{
_EvntWtHndlProduced.Set();
_EvntWtHndlProduced.Close();
_EvntWtHndlProduced = null;
}
else
{
//什么也不做
}
if (_EvntWtHndlConsumed != null)
{
for (int i = 0; i < _numberOfConsumer; ++i)
{
if (_EvntWtHndlConsumed[i] != null)
{
_EvntWtHndlConsumed[i].Set();
_EvntWtHndlConsumed[i].Close();
_EvntWtHndlConsumed[i] = null;
};
}
_EvntWtHndlConsumed = null;
}
else
{
//什么也不做
};
}
#endregion
}
Produce()和Consum()中加入的Sleep代码仅仅是为了让线程更为随机的被调度,这样我们可以更容易观察到线程乱序执行的情况。另外,如果是一个需要跨进程同步的程序,那么你也可以用Mutext替换lock实现临界区。下面是某次执行的输出情况,你的结果当然会跟它不同(空行位置是我输入回车终止线程的时机):
开始启动线程,输入回车终止生产者和消费者的工作……
******************************************
生产者:这次生产了5块糖,罐里现在一共有5块糖!
生产者:赶快来吃!!
消费者0:吃了1颗糖,还剩4颗!!
消费者0:赶快生产!!
消费者1:吃了1颗糖,还剩3颗!!
消费者1:赶快生产!!
消费者2:吃了1颗糖,还剩2颗!!
消费者2:赶快生产!!
消费者3:吃了1颗糖,还剩1颗!!
消费者3:赶快生产!!
消费者4:吃了1颗糖,还剩0颗!!
消费者4:赶快生产!!
消费者0:糖罐是空的!
消费者0:赶快生产!!
生产者:这次生产了5块糖,罐里现在一共有5块糖!
生产者:赶快来吃!!
消费者1:吃了1颗糖,还剩4颗!!
消费者1:赶快生产!!
消费者0:吃了1颗糖,还剩3颗!!
消费者0:赶快生产!!
生产者:这次生产了2块糖,罐里现在一共有5块糖!
生产者:赶快来吃!!
消费者0:吃了1颗糖,还剩4颗!!
消费者0:赶快生产!!
消费者2:吃了1颗糖,还剩3颗!!
消费者2:赶快生产!!
消费者1:吃了1颗糖,还剩2颗!!
消费者1:赶快生产!!
生产者:这次生产了3块糖,罐里现在一共有5块糖!
生产者:赶快来吃!!
消费者0:吃了1颗糖,还剩4颗!!
消费者0:赶快生产!!
消费者3:吃了1颗糖,还剩3颗!!
消费者3:赶快生产!!
消费者4:吃了1颗糖,还剩2颗!!
消费者4:赶快生产!!
消费者0:吃了1颗糖,还剩1颗!!
消费者0:赶快生产!!
生产者:下班啦!
消费者1:我来把剩下的糖都吃了!
消费者1:吃了1颗糖,还剩0颗!!
消费者1:都吃光啦,下次再吃!
消费者0:都吃光啦,下次再吃!
消费者2:都吃光啦,下次再吃!
消费者3:都吃光啦,下次再吃!
消费者4:都吃光啦,下次再吃!
******************************************
输入回车结束!
AutoResetEvent &
ManuResetEvent
到此为止我们还没有提到过EventWaitHandle的这两个儿子,不过这就是一两句话的事:
- AutoResetEvent在功能上等效于用EventResetMode.AutoReset 创建的未命名的
EventWaitHandle。
- ManualResetEvent在功能上等效于用EventResetMode.ManualReset 创建的未命名的
EventWaitHandle。
好了,讲这么都就够了,这两个子类无非是为了方便使用而存在的。不过请记得这两个子类永远是局部/Local的,并不能象它们的父类一样用于进程间的通信。
还是给出一个简单的例子,这个例子只跟通知有关,不再涉及临界资源。假设一个跑步比赛的场景,我们用一个ManualResetEvent表示比赛,然后为每个运动员配备一个AutoResetEvent用于通知到起跑线或者是达终点。首先运动员需要到起跑线上就位,这个过程我们让运动员到达起跑线后调用AutoResetEvent上的Reset()发出信号,同时使用ManualResetEvent上的WaitOne()阻塞自己准备起跑。另一方面,我们在比赛线程上先用WaitHandle.WaitAll(AutoResetEvent[])等待所有运动员到位。WaitAll()完成后,使用ManualResetEvent上的Reset()发令开始比赛,再使用WaitHandle.WaitAny(AutoResetEvent[])等待第一个运动员冲线。而每个运动员到终点后会再次调用AutoResetEvent.Reset()表示到达。
using System;
using System.Threading;
using System.Linq;
using System.Text;
class Runner : IDisposable
{
//用于让所有运动员到达起跑线准备起跑
private
ManualResetEvent _mnlRstEvntStartLine = null;
//用于运动员到达终点时发出信号
private
static AutoResetEvent[] _mnlRstEvntRunner = null;
private
const int _numberOfRunner = 8;
private Random _rnd = new Random();
///
<summary>
///
构造函数
///
</summary>
public
Runner()
{
_mnlRstEvntStartLine = new ManualResetEvent(false);
_mnlRstEvntRunner = new AutoResetEvent[_numberOfRunner];
//请运动员就位
for (int i = 0; i < _numberOfRunner; ++i)
{
_mnlRstEvntRunner[i] = new AutoResetEvent(false);
}
}
///
<summary>
///
运动员方法
///
</summary>
///
<param
name="id">运动员序号</param>
public void
Run(object id)
{
int index = (int)id;
//等待信号准备起跑
Console.WriteLine("{0}号运动员就位。", index);
_mnlRstEvntRunner[index].Set();
//等待发令
_mnlRstEvntStartLine.WaitOne();
//随机睡眠,表示不同运动员跑的快慢
Thread.Sleep(_rnd.Next(2000));
Console.WriteLine("{0}号运动员到达终点!", index);
_mnlRstEvntRunner[index].Set();
}
///
<summary>
///
比赛开始
///
</summary>
public void
Start()
{
Thread[] runners = new Thread[_numberOfRunner];
//请运动员就位
for (int i = 0; i < _numberOfRunner; ++i)
{
runners[i] = new Thread(Run);
runners[i].Start(i);
}
//等待所有运动员就位
WaitHandle.WaitAll(_mnlRstEvntRunner);
//发令起跑
Console.WriteLine("***********************起跑!!!*************************");
_mnlRstEvntStartLine.Set();
//看看谁先到达终点
int index = WaitHandle.WaitAny(_mnlRstEvntRunner);
//等待所有运动员到达终点
//请运动员就位
for (int i = 0; i < _numberOfRunner; ++i)
{
runners[i].Join();
}
Console.WriteLine("**********************************************************");
Console.WriteLine("{0}号运动员夺得冠军!", index);
Console.WriteLine("***********************比赛结束***************************");
}
static void Main()
{
Runner ss = new Runner();
try
{
ss.Start();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
ss.Dispose();
ss = null;
Console.WriteLine("输入回车结束");
Console.ReadLine();
}
}
#region IDisposable Members
public void Dispose()
{
if (_mnlRstEvntStartLine != null)
{
_mnlRstEvntStartLine.Set();
_mnlRstEvntStartLine.Close();
}
else
{
//do nothing
}
if (_mnlRstEvntRunner != null)
{
for (int i = 0; i < _numberOfRunner; ++i)
{
if (_mnlRstEvntRunner[i] != null)
{
_mnlRstEvntRunner[i].Set();
_mnlRstEvntRunner[i].Close();
_mnlRstEvntRunner[i] = null;
}
else
{
//do nothing
}
}
_mnlRstEvntRunner = null;
}
}
#endregion
}
可能的执行结果:
0号运动员就位。
1号运动员就位。
2号运动员就位。
3号运动员就位。
4号运动员就位。
5号运动员就位。
6号运动员就位。
7号运动员就位。
***********************起跑!!!*************************
3号运动员到达终点!
1号运动员到达终点!
0号运动员到达终点!
4号运动员到达终点!
2号运动员到达终点!
5号运动员到达终点!
6号运动员到达终点!
7号运动员到达终点!
**********************************************************
3号运动员夺得冠军!
***********************比赛结束***************************
题外话:派生总是优雅的吗?
在WaitHandle家族这个继承关系里,我实在忍不住要说“丑陋”两个字。Mutex以及下篇将要讲到的信号量Semaphore,实在是太委屈地接受了来自WaitHandle上不相关的静态方法。WaitAll(),WaitAny(),SignalAndWait()完完全全就是为EventWaitHandle这一族定制的。继承本来想体现的多态性,也仅仅是体现在这几个方法的参数是WaitHandle上,不过有谁会真的在这几个方法上使用Mutex或者Semaphore实例呢?也许Mutex和Semaphore是WaitHandle“抱养”的吧,否则它怎么这么偏心?:)
Mutex与EventWaitHandle完全是站在同步的两个方向:Mutex是“锁”可以实现互斥访问但几乎不具有通信能力;而EventWaitHandle有强大的通信能力,但却不能实现对资源的互斥访问。从一个父类,派生出两个有如此大差异的子类实在不知道是为何。从这种意义上来讲,似乎Monitor比较“全面”,两边都能做一点。
在基础类库里出现这样的状况,似乎确实无法对此表示信服(这可能是有些Java程序员鄙视.Net一脉的原因之一吧,Java在语言规范和OO理论上的优雅的确有些让人着迷:))。不过,我们还是要体谅一下MS。它的产品线是那么庞大,产品生命周期是那么持久,你不可能期望Windows
API刚出现的时候就能够为.Net未来的优雅考虑。一代代的更替中,他们总需要面对之前实现的一些限制。毕竟这几个类的根源是比较直接地对Win32
API地封装。
C#线程同步(5)- 信号量 Semaphore
- [Tech]
预备知识:C#线程同步(1)-
临界区&Lock,C#线程同步(2)-
临界区&Monitor,C#线程同步(3)- 互斥量
Mutex,C#线程同步(4)-
通知&EventWaitHandle一家
这次终于不用说太多话了,某人看这一系列博客的反应总是“好长……”,以至于都不愿意看下去。在这一系列开篇之前,本想应该一、两个星期就能解决,结果每篇总要花上一星期左右。总想把涉及的所有方面都讲得尽量清楚明白,希望容易被看懂。于是总是不断陷于考虑如何串联各处的关系、要写个怎样的例子、细细考量MSDN的每一句话是否妥当……能做的无用的事情也就这点儿,所以还是努力地督促着自己要尽快完成。
呵呵,还是回到正题。信号量也算是个鼎鼎大名的东西吧,提到互斥量总会说起信号量。二者的差别很简单,互斥量、临界区是用于保护“一个”需要被互斥访问的资源,这个资源同时只有一个线程能被访问;而信号量可以被用于管理“资源池”。在.Net中Semaphore类就是对Windows信号量的封装。
跟谁更亲,Mutex还是EventWaitHandle?
本系列的第3篇Mutex、第4篇EventWaitHandle都提到过Semaphore,因为它们同继承于WaitHandle。所以Semaphore必然有着一些我们已经知道的特性:
- 你可以创建没有名称的“局部”信号量,也可以创建命名的“全局”信号量用于跨应用程序域的同步。
- 你可以用WaitOne()请求一个资源。
- 你需要使用try/finally结构调用“Close()”,确保信号量资源在使用后被正确释放。
- 你仍然需要注意在全局情况下Semaphore的访问安全问题。
总的来说,Semaphore与Mutex更像是兄弟,仍然与EventWaitHandle一脉不太亲近:
- Semaphore从机制上来说跟Mutex一样属于“锁”而不是“通知”,因此跟Mutex一样几乎没有“通知”的能力。
-
举个不恰当但是很形象的例子,Semaphore就是一个可以多次进入的“Mutex”。Mutex永远只允许一个线程拥有它,而Semaphore可以允许多个线程请求,因此Semaphore被用于管理一次可以允许多个线程进入并发访问资源的情况。之所以说“不恰当”,是因为一旦允许多个线程访问资源,那么这时候的资源一定不是互斥资源,相应的代码段也不再是“临界区”。你千万不要以为我们在上一篇中提到的“糖罐”里有多颗糖就叫做“资源池”(都说过了嘛一个糖罐一定是需要互斥访问的),除非你有多个糖罐而不是多颗糖。
-
因为Semaphore与Mutex在请求数量上的不同,因此他们的线程相关性是不同的。这一点,Semaphore到跟EventWaitHandle一样,它是线程无关的。也就是说对Semaphore地释放者可以不定是Semaphore的拥有者。比如说我可以是消费者线程总使用WaitOne()请求线程池中的资源从来不需要释放,而生产者总是Release线程池中的资源而从来不请求。
Semaphore的使用方法
如果你已经读过这个系列前面4篇的博文,我想到此为止你已经对Semapore的来龙去脉、性格特点掌握得八九不离十了。就像开篇所说,这次我们我们不要再废很多话来讨论它,大致应该知道的细节,除了上面我们说的差异,都已经在之前各篇讲过了。
所以我决定要偷懒了:
- Semaphore的构造函数在 这里,是的你会觉得已经很熟悉了,一望而知其意。其它的,请仍然记得命名前缀的问题;记得名称仍然是大小写敏感的;最后别忘记使用SemaphoreSecurity类来管理命名信号量的安全。
- Semaphore仍然使用WaitOne()请求资源,接口都来自WaitHandle,你已经看过很多遍了。
- Semaphore使用Release()来表示对资源的释放,不过与ReleaseMutex()不同,这个函数有重载方法允许你指定释放几个资源。这引发了一个问题,如果Release的次数超过资源总量,那么会引发SemaphoreFullException异常。比如线程A和线程B都进入信号量。如果线程B中发生了一个编程错误,导致它调用Release()两次(或者Release(2)),则两次调用都会成功。这样,信号量的计数就已经达到了最大值,所以,当线程A最终调用Release时将引发异常。这相当于本来资源中只有N个资源,最后却有超过N个资源被还回来。
- 记得使用完以后调用Close()释放信号量资源。
Sample Code
嘿嘿,没有。因为我实在想不出有什么特别适合Sempore的简单例子,总不能把Mutex那个应用程序单例的例子改成允许启动指定个数吧。等想到了,再来补上吧。就请先参见MSDN上的相关示例代码吧。
C#线程同步(6)- 读写锁 ReaderWriterLock - [Tech]
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://xxinside.blogbus.com/logs/47780781.html
预备知识:C#线程同步(1)-
临界区&Lock,C#线程同步(2)-
临界区&Monitor
到这一篇,在Windows下主流的线程同步方法已经都讲过了,包括穿插提到的Interlocked类,那都是我们传统的曾经学到过的概念。除此之外,.Net提供了一些特有的东西来帮助我们方便地完成代码,于是便有这一篇中要讨论的读写锁。
ReaderWriterLock锁的好处
它跟Monitor一样,是.Net的原生类,不再与操作系统有什么瓜葛。回想Monitor、EventWaitHandle两篇中,关于生产者、消费者和糖罐的例子,无论是一个消费者一个生产者、还是一个消费者和多个生产者,由于使用Monitor/lock的原因,一个时刻总是只有一个线程在对糖罐进行互斥的访问。这样其实会对吞吐量造成影响,如果有一个对实时性要求比较高的场景,在各种处理线程增加到一等数目后,处理速度的瓶颈就可能变为对资源的互斥访问上。
在某些场景里,多个并发的读访问并不会有什么问题,这就是ReaderWriterLock针对Monitor改进之处。以下摘自MSDN:
ReaderWriterLock
用于同步对资源的访问。在任一特定时刻,它允许多个线程同时进行读访问,或者允许单个线程进行写访问。在资源不经常发生更改的情况下,ReaderWriterLock
所提供的吞吐量比简单的一次只允许一个线程的锁(如 Monitor)更高。
在多数访问为读访问,而写访问频率较低、持续时间也比较短的情况下,ReaderWriterLock
的性能最好。多个读线程与单个写线程交替进行操作,所以读线程和写线程都不会长时间阻止。
注意
长时间持有读线程锁或写线程锁会使其他线程发生饥饿
(starve)。为了得到最好的性能,需要考虑重新构造应用程序以将写访问的持续时间减少到最小。
一个线程可以持有读线程锁或写线程锁,但是不能同时持有两者。若要获取写线程锁,请使用 UpgradeToWriterLock 和
DowngradeFromWriterLock,而不要通过释放读线程锁的方式获取。
递归锁请求会增加锁上的锁计数。
读线程和写线程将分别排入各自的队列。当线程释放写线程锁时,此刻读线程队列中的所有等待线程都将被授予读线程锁;当已释放所有读线程锁时,写线程队列中处于等待状态的下一个线程(如果存在)将被授予写线程锁,依此类推。换句话说,ReaderWriterLock
在一组读线程和一个写线程之间交替进行操作。
当写线程队列中有一个线程在等待活动读线程锁被释放时,请求新的读线程锁的线程会排入读线程队列。即使它们能和现有的阅读器锁持有者共享并发访问,也不会给它们的请求授予权限;这有助于防止编写器被阅读器无限期阻止。
大多数在 ReaderWriterLock
上获取锁的方法都采用超时值。使用超时可以避免应用程序中出现死锁。例如,某个线程可能获取了一个资源上的写线程锁,然后请求第二个资源上的读线程锁;同时,另一个线程获取了第二个资源上的写线程锁,并请求第一个资源上的读线程锁。如果不使用超时,这两个线程将出现死锁。
如果超时间隔过期并且没有授予锁请求,则此方法通过引发 ApplicationException
将控制返回给调用线程。线程可以捕捉此异常并确定下一步要进行的操作。
这段描述还算比较清楚,不会给人带来太多困惑,我只是想提醒几点:
-
请确信在你的使用场景中,读的并发访问是允许的。我们之前的生产者、消费者和糖罐的例子并不适合使用ReadWriterLock,因为生产者和消费者都在“写”糖罐,只是一个插入一个删除而已。
-
这里的并发只是针对读操作,读写本身还是互斥的。在读锁被获取时是无法得到写锁的,反之亦然。所以它适合于“写访问频率较低、持续时间也比较短的情况”。如果写时间较长,也就意味着对这个资源总的(读和写)访问频率较低,那么本来也就没有吞吐量低的问题了。如果出现这种状况,你可以尝试把占用时间的读写操作再次安排到其它工作线程中去,尽量缩短对资源的占用时间。
-
微软竟然对超时采用抛出异常的方式,并且居然说“可以捕捉此异常并确定下一步要进行的操作”……你见过用异常控制程序流程的设计吗?!(我对微软的抱怨是不是太多了?)
ReaderWriterLock的使用方法
好了,让我们继续上一篇言简意赅的偷懒风格,浏览下ReaderWriterLock的主要方法:
差不多了吧,其它Member可以参见这里。
Sample Code
是的,仍然偷懒了,因为仍然想不出好的有些实际意义的例子。MSDN在关于ReaderWriterLock类本身的介绍中,以及以上各方法的说明里都给出了若干Sample
Code,请自行参考吧。
.aspx:页面文件,里面是html代码,在.net中打开显示为而面显示的形状(被称为设计器)。可以直接在里面进行编辑。
.cs:代码文件,比如双击button,就会进入该文件自动生成单击函数。Page_Load也在其中生成。
【一,基本知识】
Active Server Page(ASP):一种【服务器端】(server-side)脚本环境。
静态页面:就是一张页面,只用来显示。
动态页面:有交互功能。
1,在客服端动:HTML代码内部包含着能通过浏览器解释并执行的代码(例如JavaScript脚本)。
缺点:1,不好完成复杂功能;2,浏览器的不同导致解释方式不同,相同代码不一定以同样方式执行;3,可以“查看源文
件”,而很多代码是不希望被看到。
2,在服务器端动:服务器解释执行页面中的指令(例如ASP)。(即web服务器处理指令后,生成html,再返回给页面进行显示)
所适用的Web服务器只限于IIS
【IIS的作用】
首先,IIS接受用户的客户端浏览器的页面请求信息,定位ASPX页面后,把请求信息交给ASP.NET模块来处理。ASP.NET模块分析ASPX
文件,执行其中的服务器端指令,生成纯HTML文档,并返回给IIS,最后IIS再将HTML返回到客户端浏览器。
=======================
一定要把ASP.NET开发的网页文件保存为.aspx后缀。这非常重要,因为IIS在定位网页文件后,只有后缀为.aspx的文件才会交给
ASP.NET模块去处理。
【二,基本结构】
ASP.NET本身并非一种编程语言,而是一种创建动态页面的技术,用于把编程语言(Visual
Basic.NET、C#等)代码段嵌入到页面的
HTML中。二者混合在一起,就构成了ASPX页面。【如何将两者进行结合】
1,使用<%...%>嵌入代码
a,在浏览器中单击“源文件”命令,将能看到代码被转化为客户端的HTML代码。
b,使用<%--...--%>注释代码,区别于:<!--注释-->:这是HTML代码中的注释方式
2,
使用<Script>...</Script>嵌入代码
a,<Script>标记有两个特殊的属性:Language和Runat=“Server”
b,Language:该属性用于指定<Script>...</Script>之间代码所使用的编程语言,默认为Visual
Basic.NET。另外,这里
指定的语言必须与ASPX页首行使用<%@ Page
Language="..."%>
c,Runat:Runat=“Server”属性用于指定代码运行的位置是在服务器端。
d,<Script>…<Script>常常用于定义各种变量或函数。在<%...%>中可以调用Script中定义的函数,如:
在script中定义函数
<script
language="C#" runat="Server">
String
func(string day){...}
</script>
在<%%>中可如此调用:<%...
string str = func(..); Response.Write(str);...%>
3,使用<%@ CodeFile=…%>绑定代码
a,ASP.NET提出了代码分离的思想,即把代码文件(C#代码)和页面显示文件(HTML代码)分离在不同的文档中,各自独立
完成Web页面的逻辑功能和显示功能;然后通过<%@
CodeFile=…%>将两者“绑定”在一起,达到把C#代码嵌入在HTML中的效果。
b,文件HelloWorld.aspx定义了页面的结构,包括页面的外观、包含的控件等
c,页面的功能实现由C#编程语言编写,在对应的.cs文件HelloWorld.aspx.cs中实现。
d,包含HTML代码的ASPX文件和包含C#代码的CS文件的对应关系,通过HelloWorld.aspx首行
中“CodeFile="HelloWorld.aspx.cs"”来确定。
4, 使用<%@ Page...%>设置页面属性
3中的CodeFile就是一个属性,除此还有:
Language=“C#|VB”:设置本页面所采用的编程语言,默认为“C#”。
AutoEventWireup=“True|False”:是否使用ASP.NET约定的的事件命名规则,为控件事件命名。
Inherits=“...”:定义供应用程序类继承的代码隐藏类。
ResponseEdcodeing=“...”:设置ASPX页面编码方式,默认为Unicode。
Trace=“True|False”:设置是否在程序中显示代码直行的跟踪(Trace)信息。
TraceMode=“SortType”:设置跟踪信息的排序方式,默认为根据执行时间排序“SortByTime”。
5, 使用<%@ Import %>引入类库
a, <%@ Import
NameSpace="System.Data"
%>这样,在本页面中,就可以使用数据库操作的各个类了。
b,ASP.NET默认支持8个空间,即这8个空间中的类不需要使用<%@
Import %>,可以直接使用。
(1)System:包含最基本的类及数据类型。
(2)System.Text:包含各种编码类、字符编码转换类。
(3)System.Collections:包含定义各种集合的类,如列表、队列、数组、哈希表、字典等。
(4)System.Web:包含了Web应用中客户端/服务器间联系的各种类。
(5)System.Web.UI:包含了各种用于Web的服务器控件。
(6)System.Web.UI.HtmlControls:包含了HTML控件。
(7)System.Web.UI.WebControls:包含了Web控件。
(8)System.Threading:提供多线程变成的类。
6, 添加服务器控件 (即:拖工具框到页面上)
“Runat="Server"”是Server控件非常重要的属性,当ASP.NET网页执行时,.NET会检查页面上的标签有
无“Runat="Server"”属性。如果没有,就会被直接发送到客户端的浏览器进行解析;如果有,则表示这个控件可以被.NET程序所
控制,需要等到程序执行完毕,再将HTML控件的执行结果发送到客户端浏览器。
7, 用事件响应页面动作(即:在客户端捕获服务器控件事件(如双击),然后做出相应处理。文中未进行深讨)
8,将事件绑定到方法
a, 对于在页上声明的控件,可以通过在控件的标记中设置属性
(attribute/property) 将事件绑定到方法。
b, 在“属性”中,单击事件符号 ,将显示所选控件的事件列表
9, 页面加载事件
通过在“设计”视图中双击页面,便可以自动创建该事件,默认的处理方法名为Page_Load。
【三,ASP.NET核心对象】
1, Page类
Response:向浏览器输出信息。
Request:管理访问者对页面的请求信息。
Server:提供一些与Web服务器相关的信息。
2, 页面的生命周期:请求页面, 开始, 初始化页面, 加载页面, 验证, 回发事件处理, 呈现页面, 卸载
【相应的生命周期中的事件】
事件 说明
Init 当服务器控件初始化时发生;初始化是控件生存期的第一步
InitComplete 在页初始化完成时发生
PreInit 在页初始化开始时发生
Load 当服务器控件加载到Page对象中时发生
LoadComplete 在页生命周期的加载阶段结束时发生
PreLoad 在页Load事件之前发生
PreRender 在页面加载控件对象之后、呈现之前发生。
PreRenderComplete 在呈现页内容之前发生
UnLoad 当服务器控件从内存中卸载时发生
Disposed 当从内存释放服务器控件时发生,这是服务器控件生存期的最后阶段
===================================
3, Request对象:接受和管理用户对页面的请求信息
当用户打开Web浏览器访问.aspx页面时,Web服务器就会收到其HTTP请求。请求信息既包括用户的请求方式(如POST、GET)、参数名、参数值等,又包括客户端的基本信息(如浏览器类型、版本号、用户所用语言以及编码方式等),这些信息将被整合在一起。通过Request对象,便可以访问这些数据。
Response.redirect :将浏览器重定向到另一个 URL。
从客户端得到数据。常用的两种取得数据的方法是Request.Form和Request.QueryString,对应的Form提交时的POST和GET方法。
GET方法将提交的数据构造称为URL的一部分传递给服务器,如常见的网址http://www.php.net/cal.php?id=1934中的“?id=1934”部分就是GET方法提交的数据。POST方法不会则象GET那样把提交的数据暴露在浏览器的地址栏中。
4,共享数据 (需实际操作一下,加深熟习)
a,利用Application共享数据
当需要在整个程序的级别共享信息时,可以使用Application对象。例如,需要设置一个计数器来统计访问系统的所有人数;或者在程序开始和结束时记录时间,以计算系统的运行时间,这些都可以使用Application对象来实现。Application
对象是HttpApplictionState类的实例,它的生命周期起始于系统开始运行时,终止于系统关闭。
其属性和方法如下:
|
AllKeys
|
获取Application中的键的集合
|
|
Contents
|
获取对Application对象中的数据
|
|
Count
|
获取Application集合中的对象数
|
|
Item
|
获取对Application集合中的对象的访问
|
|
Add
|
将新的对象添加到Application集合中
|
|
Clear
|
从Application集合中移除所有对象
|
|
Lock
|
锁定对Application变量的访问,以保证各个用户的访问不发生冲突
|
|
UnLock
|
取消锁定对Application变量的访问
|
|
OnStart
|
当Application对象的生命周期开始时,Application_Start事件会被启动
|
|
OnEnd
|
当Application对象的生命周期结束时Application_End事件会被启动
|
b,使用Global.asax文件:
Global.asax文件与Application对象具有紧密的联系,它的主要功能就是:设置一些在程序级别使用的变量以及实现Application对象的OnStart和OnEnd等事件。
c,利用Session共享数据
对于多用户使用的系统来说,有时候需要更细致的数据共享机制:每个用户都可以共享自己的所有数据,而其他用户则无法共享。要实现这种会话级别的共享,需要使用Session对象。
说明:Session意为“会话”,指从用户进入系统到关闭浏览器离开系统的这段交往时间。对于该用户来说,在Session中注册的变量可以保留其值,并可在各个页面中使用。由于这种特点,Session常用于用户在页面之间参数传递、用户身份认证、记录程序状态等。
常用方法:
Abandon 结束Session
Remove 删除Session中的对象
OnStart 当Session对象的生命周期开始时,Session_Start事件会被启动
OnEnd
当Session对象的生命周期结束时Session_End事件会被启动
同Application不同的是,如果在另外一个客户端的浏览器中输入P2.aspx地址,则不会显示“value1”,这是因为系统认为另一个客户端为其他用户,而Session只支持用户内部的数据共享。
d,利用Session验证用户
(2010-12-15 16:52)
===【学习方法】===
直接以项目进行快速扩展式学习
==================
【项目服务及密码】
ICTPSRV_2是一个服务,在本机(客户端)上有个ICTPSRV_2服务,在主机(*.1.7)上有个监听服务(Lisioner),
Oracle中,一个服务对应很多用户,一个用户与一个应用程序挂钩。因为没有专门的数据库名,所以在写程序时,以ICTPSRV_2作为数据库名。
在本项目中,有两个用户:kbser/nkipwd(知识库维护)
liang/sys(话务员处理系统)
每个用户相对是独立的,有独立的表和视图等。也可以将一个用户看成一个数据库。
【oracle服务基本原理】
1,数据库服务器上面有一个监听程序,负责监听客户端的请求。
2,客户端通过oracle的服务向服务器发送请求。
【服务配置的具体流程】
a.
首先在机子A装上数据库,此机就被称为oracle数据库服务器,默认安装会在A机上安装一个orcl数据库(oracle默认全局数据库).用户名和密码一般默认为:sys.(当然也可以再添加一个数据库,在Database
Configuration Assistant中可以创建、删除、修改数据库)
b. 如果创建了数据库,在Oracle Administration
Assitant(用来管理数据库的,比如启动或连接或断开数据库等操作)中就会看到创建的数据库.比如,在192.168.1.2上database下就有两个数据库:mobile和orcl.
c. 现在机A中有orcl数据库了,需要为其添加一个监听程序,在net
manager中的listener中进行配置。将机A的主机名和端口写上即可。如果是在机A上添加监听程序,则主机名为:localhost.
d. 服务器端有了监听程序后如何连上去?客户端需要通过数据库服务向监听程序去申请。在客户机B的net
manager的服务命名中新建一个服务,网络服务名为:test。申请的主机名为A机的IP地址,端口号默认为1257.服务名(即机A上的数据库名):orcl,表示向机A上的orcl数据库发送请求。
e.
在客户端用pl/sql登陆时,其database填写的是d步中创建的网络服务名,即test。用户名和密码为机A的orcl的用户名和密码,即sys.
【本项目中的表层次结构特征】
比如有两个表:
表一:
1 a1 b1 c1
2 a2 b2 c2
3 a3 b3 c3
表二:
1 x1 y1 z1
2 x2 y2 z2
3 x3 y3 z3
如果层次上,表一在表二的上层,也即:表示要用到表一的主键作为外键形成表三:
表三:
1 2 x1 y1 z1
2 3 x2 y2 z2
3 1 x3 y3 z3
4 1 x1 y1 z1
5 3 z2 y2 z2
从而表三的主键其实是蕴含表一的值的。(注,第二列其实是一个外键)
如果表特别多的话,有可能造成行数特别的长,导致一个大表的生成。
【 Oracle的存储结构图:】

【模式对象、表空间和数据文件之间的关系】

?存储过程?