sql函数的使用 存储过程的基本教程 系统函数

SQL Server 数据库的高级操作
(1) 批处理
(2) 变量
(3) 逻辑控制
(4) 函数
(5) 高级查询

*/

(1)批处理
将多条SQL语句作为一个整体去编译,生成一个执行计划,然后,执行!
理解批处理的关键在于"编译",对于由多条语句组成的一个批处理,
如果在编译时,其中,有一条出现语法错误,将会导致编译失败!

create table t
(
a int,
b int
)

-- 注释
-- 如果多行注释中包含了批处理的标识符go
-- 在编译的过程中代码将会被go分割成多个部分来分批编译
-- 多行注释的标记将会被分隔而导致编译出错
-- 以下几条语句是三个非常经典的批处理
-- 你猜一下会添加几条记录!
/*
insert into t values (1,1)
go
*/
insert into t values (2,2)
go
/*
insert into t values (3,3)
*/
go


-- 查询看添加了几条记录
select * from t

truncate table t

(2)变量

-- 全局变量
SQL Server中全局变量由系统定义、系统维护,用户一般仅可对其进行读取!

-- 查看SQL Server版本
print @@version

-- 服务器名称
print @@servername

-- 系统错误编号
insert into t values ('a','a')
print @@error

insert into t values ('a','a')
if @@error = 245
print 'Error'

-- SQL Server 版本的语言信息
print @@LANGUAGE

-- 一周的第一天从星期几算起
print @@datefirst

-- CPU 执行命令所耗费时间的累加
print @@cpu_busy

-- 获取最近添加的标识列的值
create table tt
(
a int identity(3, 10),
b int
)
insert into tt (b) values (1)
print @@identity
select * from tt

-- 局部变量
局部变量由用户定义,仅可在同一个批处理中调用和访问

declare @intAge tinyint
set @intAge = 12
print @intAge

declare @strName varchar(12)
select @strName = 'state'
print @strName
select au_lname, @strName from authors

(3)逻辑控制

-- IF条件判断
declare @i int
set @i = 12
if (@i > 10)
begin     -- {
   print 'Dadadada!'
   print 'Dadadada!'
end     -- }
else
begin
   print 'XiaoXiao!'
   print 'XiaoXiao!'
end

-- While循环控制
declare @i int;
set @i = 12;
print @i
return;
while (@i < 18)
begin
print @i;
set @i = @i + 1;
if @i < 17
   continue;
if @i > 15
   break;
end;

-- CASE 分支判断
select au_lname, state, '犹他州' from authors where state = 'UT'
select au_lname, state, '密西西比州' from authors where state = 'MI'
select au_lname, state, '肯塔基州' from authors where state = 'KS'

select au_lname, state,
case state
when 'UT' then '犹他州'
when 'MI' then '密西西比州'
when 'KS' then '肯塔基州'
when 'CA' then '加利福利亚'
else state
end
from authors

(4.1)系统函数

-- 获取指定字符串中左起第一个字符的ASC码
print ascii('ABCDEF')
-- 根据给定的ASC码获取相应的字符
print char(65)
-- 获取给定字符串的长度
print len('abcdef')
-- 大小写转换
print lower('ABCDEF')
print upper('abcdef')
-- 去空格
print ltrim('     abcd   dfd   df   ')
print rtrim('     abcd   dfd   df   ')
-- 求绝对值
print abs(-12)
-- 幂
-- 3 的 2 次方
print power(3,2)
print power(3,3)
-- 随机数
-- 0 - 1000 之间的随机数
print rand() * 1000
-- 获取圆周率
print pi()


-- 获取系统时间
print getdate()

-- 获取3天前的时间
print dateadd(day, -3 , getdate())
-- 获取3天后的时间
print dateadd(day, 3 , getdate())
-- 获取3年前的时间
print dateadd(year, -3 , getdate())
-- 获取3年后的时间
print dateadd(year, 3 , getdate())

-- 获取3月后的时间
print dateadd(month, 3 , getdate())
-- 获取9小时后的时间
print dateadd(hour, 9 , getdate())
-- 获取9分钟后的时间
print dateadd(minute, 9 , getdate())

-- 获取指定时间之间相隔多少年
print datediff(year, '2005-01-01', '2008-01-01')
-- 获取指定时间之间相隔多少月
print datediff(month, '2005-01-01', '2008-01-01')
-- 获取指定时间之间相隔多少天
print datediff(day, '2005-01-01', '2008-01-01')

-- 字符串合并
print 'abc' + 'def'

print 'abcder'

print 'abc' + '456'
print 'abc' + 456

-- 类型转换
print 'abc' + convert(varchar(10), 456)

select title_id, type, price from titles
-- 字符串连接必须保证类型一致(以下语句执行将会出错)
-- 类型转换
select title_id + type + price from titles
-- 正确
select title_id + type + convert(varchar(10), price) from titles

print '123' + convert(varchar(3), 123)
print '123' + '123'

print convert(varchar(12), '2005-09-01',110)

-- 获取指定时间的特定部分
print year(getdate())
print month(getdate())
print day(getdate())

-- 获取指定时间的特定部分
print datepart(year, getdate())
print datepart(month, getdate())
print datepart(day, getdate())
print datepart(hh, getdate())
print datepart(mi, getdate())
print datepart(ss, getdate())
print datepart(ms, getdate())

-- 获取指定时间的间隔部分
-- 返回跨两个指定日期的日期和时间边界数
print datediff(year, '2001-01-01', '2008-08-08')
print datediff(month, '2001-01-01', '2008-08-08')
print datediff(day, '2001-01-01', '2008-08-08')
print datediff(hour, '2001-01-01', '2008-08-08')
print datediff(mi, '2001-01-01', '2008-08-08')
print datediff(ss, '2001-01-01', '2008-08-08')

-- 在向指定日期加上一段时间的基础上,返回新的 datetime 值
print dateadd(year, 5, getdate())
print dateadd(month, 5, getdate())
print dateadd(day, 5, getdate())
print dateadd(hour, 5, getdate())
print dateadd(mi, 5, getdate())
print dateadd(ss, 5, getdate())

-- 其他
print host_id()
print host_name()
print db_id('pubs')
print db_name(5)


-- 利用系统函数作为默认值约束
drop table ttt

create table ttt
(
stu_name varchar(12),
stu_birthday datetime default (getdate())
)

alter table ttt
add constraint df_ttt_stu_birthday default   (getdate()) for stu_birthday

insert into ttt values ('ANiu', '2005-04-01')
insert into ttt values ('ANiu', getdate())

insert into ttt values ('AZhu', default)

sp_help ttt

select * from ttt

 

(4.2)自定义函数

select title_id
from titles
where type = 'business'

select stuff(title_id,1,3,'ABB'), type
from titles
where type = 'business'

select count(title_id) from titles where type = 'business'
select title_id from titles where type = 'business'


select   *,count(dbo.titleauthor.title_id)
FROM dbo.authors INNER JOIN
dbo.titleauthor ON dbo.authors.au_id = dbo.titleauthor.au_id

select au_id, count(title_id)
from titleauthor
group by au_id

SELECT dbo.authors.au_id, COUNT(dbo.titleauthor.title_id) AS '作品数量'
FROM dbo.authors   left outer JOIN
       dbo.titleauthor ON dbo.authors.au_id = dbo.titleauthor.au_id
GROUP BY dbo.authors.au_id
order by '作品数量'

-- 自定义函数的引子

-- 子查询
-- 统计每个作者的作品数
-- 将父查询中的作者编号传入子查询
-- 作为查询条件利用聚合函数count统计其作品数量
select au_lname, 
(select count(title_id)
from titleauthor as ta
where ta.au_id = a.au_id
) as TitleCount
from authors as a
order by TitleCount


-- 是否可以定义一个函数
-- 将作者编号作为参数统计其作品数量并将其返回
select au_id, au_lname, dbo.GetTitleCountByAuID(au_id) as TitleCount
from authors
order by TitleCount

create proc pro_CalTitleCount
as
select au_id, au_lname, dbo.GetTitleCountByAuID(au_id) as TitleCount
from authors
order by TitleCount
go

execute pro_CalTitleCount

-- vb中函数定义格式
function GetTitleCountByAuID(au_id as string) as integer

.......

GetTitleCountByAuID = ?
end function

dim strName string
declare @au_id varchar(12)

-- 根据给定的作者编号获取其相应的作品数量
create function GetTitleCountByAuID(@au_id varchar(12))
returns int
begin
return (select count(title_id)
   from titleauthor
   where au_id = @au_id)
end

 


select * from sales

select * from sales where title_id = 'BU1032'

select sum(qty) from sales where title_id = 'BU1032'

select title_id, sum(qty) from sales
group by title_id


select title_id, title, dbo.GetTotalSaleByTitleID(title_id) as TotalSales
from titles
order by TotalSales

create function GetTotalSaleByTitleID(@tid varchar(24))
returns int
begin
return(select sum(qty) from sales where title_id = @tid)
end


select top 10 title_id, title, dbo.GetTotalSaleByTitleID(title_id) as TotalSales
from titles
order by TotalSales desc

 

create function getpaixu(@id varchar(20))
returns int
begin
return(select count(TotalSales)
   from titles
    where ToalSales >(
   select TotalSales
    from titles
    where title_id=@id))
end


select dbo.getpaixu('pc1035') from titles

select count(title_id) + 1
from titles
where dbo.GetTotalSaleByTitleID(title_id) > dbo.GetTotalSaleByTitleID('pc1035')

drop function GetRankByTitleId

alter function GetRankByTitleId(@tid varchar(24))
returns int
begin
return (select count(title_id) + 1
   from titles
   where dbo.GetTotalSaleByTitleID(title_id) > dbo.GetTotalSaleByTitleID(@tid))
end

select title_id, title,
dbo.GetTotalSaleByTitleID(title_id) as TotalSales,
dbo.GetRankByTitleId(title_id) as TotalRank
from titles
order by TotalSales desc

sp_help titles
sp_helptext GetRankByTitleId
sp_helptext sp_helptext
sp_helptext xp_cmdshell

 

select * from [order details]
select * from [order details] where productid = 23
select sum(quantity) from [order details] where productid = 23

--
create function GetTotalSaleByPID(@Pid varchar(12))
returns int
begin
return(select sum(quantity) from [order details] where productid = @Pid)
end

select * from products

select productid, productname, dbo.GetTotalSaleByPID(productid)
from products

 

select p.pub_name, t.*
from titles as t join publishers as p on t.pub_id = p.pub_id
where p.pub_name = 'hello'

select * from publishers

select p.pub_name, t.*
from titles as t join publishers as p on t.pub_id = p.pub_id
where p.pub_name = 'Algodata Infosystems'

alter procedure GetTitlesByPub(@PubName varchar(36))
with encryption
as
select p.pub_name, t.*
from titles as t join publishers as p on t.pub_id = p.pub_id
where p.pub_name = @PubName
go

sp_helptext GetTitlesByPub

execute GetTitlesByPub 'hello'
execute GetTitlesByPub 'Algodata Infosystems'

alter procedure TestNull
as
print 'Hello World!'
go

execute TestNull

alter function TestNullFun()
returns int
with encryption
begin
return(1)
end

sp_helptext TestNullFun

(5)高级查询

(6)存储过程


xp_cmdshell 'dir *.*'

xp_cmdshell 'net start iisadmin'


use northwind
go

CREATE FUNCTION LargeOrderShippers ( @FreightParm money )
RETURNS @OrderShipperTab TABLE
    (
     ShipperID      int,
     ShipperName    nvarchar(80),
     OrderID        int,
     ShippedDate    datetime,
     Freight        money
    )
AS
BEGIN
    INSERT @OrderShipperTab
         SELECT S.ShipperID, S.CompanyName,
                O.OrderID, O.ShippedDate, O.Freight
         FROM Shippers AS S INNER JOIN Orders AS O
               ON S.ShipperID = O.ShipVia
         WHERE O.Freight > @FreightParm
    RETURN
END

SELECT * FROM LargeOrderShippers( $500 )


posted @ 2007-04-04 10:11 簡單就好 阅读(966) | 评论 (4) | 编辑 收藏
 
[置顶]SQL基础
SQL Server 数据库的基本操作
(1) 数据库的创建
(2) 数据表的创建以及相关约束的指定(含临时表)
(3) 数据的添/删/改
(4) 数据的查询

*/

(0)创建数据库
-- 指定数据库名称
-- (注:如果数据库名中包含空格可以使用[]将其标示)
create database [Super WC]
-- 关于数据文件的定义
on
(
name = Super_WC_Data,     -- 逻辑名
filename = 'C:/Super_WC_Data.MDF',   -- 物理路径以及物理名
size = 2MB,      -- 初始大小
maxsize = 4MB,      -- 最大限制
filegrowth = 1MB            -- 增长大小
)
-- 关于日志文件的定义
log on
(
name = Super_WC_Log,
filename = 'C:/Super_WC_Log.LDF',
size = 3MB,
maxsize = 7MB,
filegrowth = 20%            -- 增长比例
)

-- 附加数据库
execute sp_attach_db '[Super WC]', 'C:/Super_WC_Data.MDF','C:/Super_WC_Log.LDF'
-- 分离数据库
execute sp_detach_db '[Super WC]'
-- 复制数据库
execute master.dbo.xp_cmdshell 'copy C:/Super_WC_Data.MDF D:/Super_WC_Data.MDF'
execute master.dbo.xp_cmdshell 'copy C:/Super_WC_Log.LDF D:/Super_WC_Log.LDF'


(1)创建数据表

创建一个数据表:学生(students)
结构如下:
字段        类型         是否允许为空      约束            备注
no          char(4)      No                主键            学号

name        nvarchar(8) No                唯一            姓名

birthday    datetime     No                检查(至少18年前) 生日

age         tinyint      No                缺省(默认等于当前时间减去生日) 年龄

sex         nchar(1)     No                缺省(默认'女')          性别

phone       char(11)     Yes               检查(要么没有,要么长度等于11) 电话

address     nvarchar(24)No                                 地址

没有特别约束的情况:
create table student
(
no         char(4)   not null,
name       nvarchar(8)   not null,
birthday   datetime   not null,
phone      char(11)   null,
address    nvarchar(24)   null
)

注意:没有特别约束的情况下,创建数据表可以参考“企业管理器”中“设计表”的操作格式!

包含约束的情况:
create table students
(
no         char(4)    primary key,
name       nvarchar(8)    unique,
birthday   datetime    check(datediff(year, birthday, getdate()) >= 18),
age        as datediff(year, birthday, getdate()),
sex    nchar(1)   default('女') check(sex = '女' or sex = '男')
phone      char(11)    check((phone is null) or (len(phone) = 11)),
address    nvarchar(24)
)


create table scores
(
no     char(4)     foreign key references students(no),
chinese   numeric(4,1) check(chinese >= 0 and chinese <= 100),
english   numeric(4,1) check(english >= 0 and english <= 100) 
)

以上答案只是最简单的描述形式!

比较规范的写法是
先用create table声明数据表的结构,

CREATE TABLE students
(
no        char(4),
name      nvarchar(8),
birthday datetime,
age       as DATEDIFF(year, birthday, getdate()),
sex     nchar(1),
phone     char(11),
address   nvarchar(24)
)

然后再ALTER TABLE   ADD CONSTRAINT 分别指定每个字段的约束:
每个约束都有一个独特的名称,其中,命名规范采用以下格式:
约束类型的简写_表名_字段名
pk_students_no

ALTER TABLE students
ADD CONSTRAINT pk_students_no PRIMARY KEY (no)

ALTER TABLE students
ADD CONSTRAINT uq_students_name UNIQUE (name)

ALTER TABLE students
ADD CONSTRAINT ck_students_birthday CHECK (datediff(year,[birthday],getdate()) >= 18)

ALTER TABLE students
ADD CONSTRAINT df_students_sex default ('女') for sex

ALTER TABLE students
ADD CONSTRAINT ck_students_sex CHECK ([sex] = '男' or [sex] = '女')

ALTER TABLE students
ADD CONSTRAINT ck_students_phone CHECK ([phone] is null or len([phone]) = 11)

相对应的对约束进行删除,则是通过DROP CONSTRAINT子句来完成:
ALTER TABLE students
DROP CONSTRAINT pk_students_no

ALTER TABLE students
DROP CONSTRAINT uq_students_name

注意:
约束只有添加与删除操作,没有修改操作!


注意:
其中,age(年龄)采用了“计算列”的表示方法!
“计算列”的定义:
在表中某个字段的值源于某一表达式的值(某一函数的运算结果或是其他字段的运算结果)!
比如:
某学生的成绩表结构如下:
数学
语文
体育
总分

创建命令如下:
create table scores
(
math       numeric(3, 1),
chinese   numeric(3, 1),
sport      numeric(3, 1),
total       as math + Chinese + sport
)

insert into scores values (80, 69, 95)

total 部分的值会自动计算,为 244

-- 创建临时表
-- 临时表将会存储在TempDB的临时数据库中
-- 当用户断开连接后,就会自动删除
-- 语法:在表名前加上#
create table #tt
(
a int,
b int
)

insert into #tt values (1,1)
insert into #tt values (2,2)

select * from #tt


(2)数据操作(添加/删除/修改)

添加操作(insert)的语法格式:
Insert [into] 数据表 (字段) values (数据)

-- 添加记录(一般情况)
insert into students
(no,name,birthday,sex,phone,address)
values
('0001', 'AHuang', '2000-01-01', '男', '13307331100', '株洲')

(注意: into 可以省略 )


-- 添加记录(如果是给所有字段添加数据,可以省略字段标示)
insert into students
values
('0002', 'ANing', '2008-08-08', '女', '13307330011', '北京')


-- 添加记录(如果是给具有默认约束的字段添加数据,想利用默认约束,可以利用default)
insert into students
values
('0002', 'ANing', '2008-08-08', default, '13307330011', '北京')


删除操作(delete)的语法格式:
Delete [from] 数据表 where 条件

-- 删除记录(删除所有)
delete from students

(注意: from 可以省略,即可以: delete students )

-- 删除记录(删除特定记录,可以通过 where 条件来过滤,比如:学号'0001'的学生记录)
delete from students where no = '0001'


修改操作(update)的语法格式:
update 数据表 set 字段 = 新值 where 条件

-- 修改记录(修改所有)
update students set 性别 = '女'

-- 修改记录(修改特定记录,可以通过 where 条件来过滤,比如:学号'0001'的学生记录)
update students set 性别 = '男' where no = '0001'

 

(3)数据查询

查询操作(select)的语法格式:
select 字段 from 数据表 where 条件 order by 字段

-- 查询记录(查询所有行与所有列,指定数据表所有字段)
select no, name, birthday, sex, phone, address from students


-- 查询记录(查询所有行与所有列,除了指定数据表所有字段,还可以通过 * 来指代所有字段)
select * from students

-- 查询记录(查询所有行与特定列,指定数据表特定字段)
select no, name, phone, address from students

-- 查询记录(给字段指定别名)
select no as '学号', name as '姓名', phone as '电话', address as '地址' from students

-- 查询记录(合并字段并指定别名)
select no as '学号', name as '姓名', address + ' ' + phone as '地址 电话' from students

(注意:合并字段的前提是字段类型必须一致,否则需要通过类型转换函数convert来实现!)

-- 类型转换函数 convert 语法
convert(目标类型, 数据)

convert(varchar(12), 1234)


(4)数据查询的高级应用(利用PUBS数据库作为演示数据)

-- 根据价格对书籍表记录排序
select * from titles order by price

(注意:默认数据排序采用升序:由低到高或是有小到大,可以通过设定关键字来调整)

-- 根据价格对书籍表记录排序(降序)
select * from titles order by price desc

-- 根据价格对书籍表记录排序(升序)
select * from titles order by price asc

-- 找出书籍表中最高与最低的价格
select max(price), min(price) from titles

-- 统计书籍表中书籍总数以及总价格和平均价格
select count(title_id), sum(price), avg(price) from titles

-- 找出书籍表中最贵3本书
select top 3 title_id, price from titles order by price desc

select * from titles
select * from jobs
--

-- 统计书籍表中每种类型的书籍总量
select type as '书籍类型', count(title_id) '书籍数量'
from titles group by type

select type, title_id from titles order by type

-- 统计书籍表中每种类型的书籍的预付款(advance)的总和
select type as '书籍类型', sum(advance) '预付款总和'
from titles group by type

-- 列出所有书籍类型
select type as '书籍类型' from titles
select distinct type as '书籍类型' from titles
select distinct type as '书籍类型', title_id from titles

-- 列出所有作者所在州
select distinct state as '州' from authors
select distinct state as '州', city as '城市' from authors where   state = 'CA'
select state as '州', city as '城市' from authors where state = 'CA'

(注: distinct 列出指定字段的值同时剔出所有重复的!)


-- 根据给定的作者姓名列出其所著书籍名称
select * from authors
select * from titles

select a.au_lname as '名', a.au_fname as '姓', t.title as '书籍名称'
from authors as a join titleauthor as ta on a.au_id = ta.au_id
join titles as t on ta.title_id = t.title_id
where a.au_lname = 'Green' and a.au_fname = 'Marjorie'


-- 根据给定的出版社名称列出其所有的出版书籍名称
select * from publishers

select p.pub_name as '出版社', t.title as '书籍名称'
from publishers as p join titles as t on p.pub_id = t.pub_id
where pub_name = 'New Moon Books'

select pub_name as '出版社', title as '书籍名称'
from publishers as p join titles as t on p.pub_id = t.pub_id
where pub_name = 'New Moon Books'


-- 在销售表中挑选1993年度的订单
select ord_num as '订单编号', title_id as '书籍编号', ord_date as '订购日期' 
from sales
where ord_date between '1993-01-01' and '1993-12-31'

(注意:between and 之间的值必须满足"开始点"<="结束点")
select ord_num as '订单编号', title_id as '书籍编号', ord_date as '订购日期' 
from sales
where ord_date between '1993-12-31' and '1993-1-1'


-- 在销售表中统计每本书的订单数量
select title_id as '书籍编号', count(ord_num) as '订单总数'   from sales group by title_id


-- 在销售表中统计每本书的总订购量
select title_id as '书籍编号', sum(qty) as '总定购量'   from sales group by title_id

-- 在销售表中统计每个商店的总订购量
select stor_id as '商店编号', sum(qty) as '总定购量'   from sales group by stor_id

-- 在销售表中查询每个商店定购了那几本书以及每本书相应的订购量
select stor_id as '商店编号', title_id as '书籍编号', sum(qty) as '总定购量'   from sales group by stor_id, title_id order by stor_id

 

-- 查询指定出版社出版的书
select p.pub_name as '出版社', t.title as '书籍'
from publishers as p join titles as t on p.pub_id = t.pub_id
where pub_name = 'New Moon Books'

-- 查询指定出版社出版的书籍的总数目
select p.pub_name as '出版社', count(t.title) as '书籍数目'
from publishers as p join titles as t on p.pub_id = t.pub_id
group by p.pub_name

-- 统计每个作者的版权税总和
select au_id as '作者编号', sum(royaltyper)   from titleauthor group by au_id

 

select * from authors
select * from titles
select * from publishers

select * from publishers where   = 'New Moon Books'
select * from titles where pub_id = '0736'

-- 子查询
-- 根据出版社的名称查询其出版的书籍
-- 首先,根据出版社的名称在publisher中找到相应的出版社编号
-- 然后,在根据出版社编号在titles中找到相应的书籍信息
select * from titles
where pub_id =
(select pub_id from publishers
   where pub_name = 'New Moon Books')


select title_id, type, price from titles

select title_id, type, price
from titles
order by price desc


select top 4 title_id, type, price
from titles
order by price desc


-- 子查询
-- 找寻最贵的书籍
-- 先通过聚合函数获取最高价格
-- 然后,将其作为查询条件,找出相应的书籍
select title_id, type, price from titles
where price = (select max(price) from titles)

select au_id,au_lname from authors where au_lname = 'Green'

-- 子查询
-- 根据作者的名查找其编写的书籍
-- 先通过子查询获取作者编号
-- 然后,将其作为查询条件,找出相应的书籍编号
-- 最后,在利用所得到的书籍编号来得到书籍信息
select au_id, title_id from titleauthor
where au_id =
(select au_id from authors where au_lname = 'Green')

select * from titles
where title_id in
(
select title_id from titleauthor
   where au_id =
   (select au_id from authors where au_lname = 'Green')
)

 

-- 打印输出
-- Print
print 'Hello world'

-- 获取系统时间
print getdate()

-- 获取3天前的时间
print dateadd(day, -3 , getdate())
-- 获取3天后的时间
print dateadd(day, 3 , getdate())
-- 获取3年前的时间
print dateadd(year, -3 , getdate())
-- 获取3年后的时间
print dateadd(year, 3 , getdate())

-- 获取3月后的时间
print dateadd(month, 3 , getdate())
-- 获取9小时后的时间
print dateadd(hour, 9 , getdate())
-- 获取9分钟后的时间
print dateadd(minute, 9 , getdate())

-- 获取指定时间之间相隔多少年
print datediff(year, '2005-01-01', '2008-01-01')
-- 获取指定时间之间相隔多少月
print datediff(month, '2005-01-01', '2008-01-01')
-- 获取指定时间之间相隔多少天
print datediff(day, '2005-01-01', '2008-01-01')

-- 获取给定字符串的长度
print len('abcdef')
-- 字符串合并
print 'abc' + 'def'

print 'abcder'

print 'abc' + '456'
print 'abc' + 456
-- 类型转换
print 'abc' + convert(varchar(10), 456)

print '123' + '456'
print '123' + 456
print 123 + '456'


-- 利用系统函数作为默认值约束
drop table ttt

create table ttt
(
stu_name varchar(12),
stu_birthday datetime default (getdate())
)

alter table ttt
add constraint df_ttt_stu_birthday default   (getdate()) for stu_birthday

insert into ttt values ('ANiu', '2005-04-01')
insert into ttt values ('ANiu', getdate())

insert into ttt values ('AZhu', default)

sp_help ttt

select * from ttt

-- 计算列
create table scores
(
chinese int,
english int,
total int
)

insert into scores values (80, 90, 170)

select * from scores

drop table scores

create table scores
(
chinese int,
english int,
total as chinese + english -- 计算列
)

insert into scores values (80, 90)

-- 故意添加,结果出错
insert into scores values (80, 90, 250)

select * from scores

posted @ 2007-04-04 10:09 簡單就好 阅读(155) | 评论 (0) | 编辑 收藏
 
 2007年6月7日 ORACLE系列(-)入门
     摘要:   ORACLE系统概述   ORACLE公司自86年推出版本5开始,系统具有分布数据库处理功能.88年推出版本6,ORACLE RDBMS(V6.0)可带事务处理选项(TPO),提高了事务处理的速度.1992年推出了版本7,在ORACLE RDBMS中可带过程数据库选项(procedural database option)和并行服务器选项(parallel serve...  阅读全文
posted @ 2007-06-07 14:00 簡單就好 阅读(112) | 评论 (0) | 编辑 收藏
 
 2007年5月31日 XP下安装SQLSERVER2000企业版
XP下安装SQLSERVER2000企业版本分类:默认栏目
XP下安装SQL2000企业版本
SQL2000企业版本适用于WIN 2000系统,2003系统和XP一般装不了需要选用个人版,当然如果你在不清楚的前提下辛辛苦苦下载了企业版本却不能安装,是不是很失望呢?这里介绍一个XP下安装装SQL2000企业版本方法以供参考~
(对于XP上不能安装SQLSERVER2000可作为参考)
办法如下:
  一.在SQL服务器的安装盘中找到MSDE这个目录,并且点击setup.exe安装它,过程简单直接下一步就OK了。
  二. 重启系统WINDOWSXP,这下就可以看到SQL服务的图标出现了。
三. 再拿出SQL服务器版的安装光盘,直接安装客户端工具(这个不要多说吧?最简单的方法就是直接点击光盘根目录下的autorun.exe)
根据提示安装,自检过程中知道系统不是SERVER版,会提示只安装客户端工具。(哈哈,服务端我已有了)
四. 打开企业管理器,试用SA用户连一下看看,是不是发现SA用户登陆失败?因为你还没有与信任SQL SERVER连接相关联。还好这个只要对系统注册表稍加修改就可以啦:
在运行中输入regedit打开注册表编辑器,找到[HKEY_LOCAL_MACHINE/SOFTWARE/MICROSOFT/MSSQLSERVER/MSSQLSERVER],这个项里面
有一个键值LoginMode,默认下,值是1,现在将值改为2,重启电脑。
五. 再打开企业管理,再连接试试,是不是OK了!
XP下安装SQLSERVER2000企业版
posted @ 2007-05-31 14:45 簡單就好 阅读(193) | 评论 (1) | 编辑 收藏
 
 2007年4月29日 将数据库从SQL2000迁移到SQL2005时,无法查看关系图的解决办法
选中这个数据库,新建一个查询,输入下面的脚本:

EXEC sp_dbcmptlevel database_name, 90
USE database_name
EXEC sp_changedbowner 'sa'

其中,database_name是此数据库的名称.

运行可能需要一段时间,运行完成后,点击"关系图",刷新,应该就可以看到关系图了.


posted @ 2007-04-29 13:43 簡單就好 阅读(627) | 评论 (0) | 编辑 收藏
 
*.Config配置文件的操作
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()等便捷操作。

7. 使用自定义类

 
比如ConfigSectionData里面
除了简单类型之外,可不可以有自定义的类
可以使用自定义类,不过需要定义一个转换器。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.ComponentModel;
// 要写入配置文件的自定义类
class CustomData
{
public CustomData(string s)
{
this.s = s;
}
private string s;
public string S
{
get { return s; }
set { s = value; }
}
}
// 自定义的转换器(演示代码省略了类型判断)
class CustomConvert : ConfigurationConverterBase
{
public override bool CanConvertFrom(ITypeDescriptorContext ctx, Type type)
{
return (type == typeof(string));
}
public override object ConvertTo(ITypeDescriptorContext ctx, CultureInfo ci, object value, Type type)
{
return (value as CustomData).S;
}
public override object ConvertFrom(ITypeDescriptorContext ctx, CultureInfo ci, object data)
{
return new CustomData((string)data);;
}
}
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; }
}
[ConfigurationProperty("custom")]
[TypeConverter(typeof(CustomConvert))] // 指定转换器
public CustomData Custom
{
get { return (CustomData)this["custom"]; }
set { this["custom"] = value; }
}
}
public class Program
{
static void Main(string[] args)
{
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigSectionData data = new ConfigSectionData();
data.Id = 1000;
data.Time = DateTime.Now;
data.Custom = new CustomData("abcdefg...");
config.Sections.Add("add", data);
config.Save(ConfigurationSaveMode.Minimal);
// 读取测试
ConfigSectionData configData = (ConfigSectionData)config.Sections["add"];
Console.WriteLine(configData.Custom.S);
}
}


保存后的配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="add" type="..." />
</configSections>
<add id="1000" time="04/17/2006 22:06:58" custom="abcdefg..." />
</configuration>

posted @ 2007-04-29 12:08 簡單就好 阅读(793) | 评论 (2) | 编辑 收藏
 
存储过程编写经验和优化措施
介绍:在数据库的开发过程中,经常会遇到复杂的业务逻辑和对数据库的操作,这个时候就会用SP来封装数据库操作。如果项目的SP较多,书写又没有一定的规范,将会影响以后的系统维护困难和大SP逻辑的难以理解,另外如果数据库的数据量大或者项目对SP的性能要求很,就会遇到优化的问题,否则速度有可能很慢,经过亲身经验,一个经过优化过的SP要比一个性能差的SP的效率甚至高几百倍。

内容:

1、开发人员如果用到其他库的Table或View,务必在当前库中建立View来实现跨库操作,最好不要直接使用“databse.dbo.table_name”,因为sp_depends不能显示出该SP所使用的跨库table或view,不方便校验。

2、开发人员在提交SP前,必须已经使用set showplan on分析过查询计划,做过自身的查询优化检查。

3、高程序运行效率,优化应用程序,在SP编写过程中应该注意以下几点:

a)        SQL的使用规范:

i.  尽量避免大事务操作,慎用holdlock子句,提高系统并发能力。
ii. 尽量避免反复访问同一张或几张表,尤其是数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。
iii.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。
iv. 注意where字句写法,必须考虑语句顺序,应该根据索引顺序、范围大小来确定条件子句的前后顺序,尽可能的让字段顺序与索引顺序相一致,范围从大到小。
v.  不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
vi. 尽量使用exists代替select count(1)来判断是否存在记录,count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率。
vii.尽量使用“>=”,不要使用“>”。
viii.注意一些or子句和union子句之间的替换
ix.注意表之间连接的数据类型,避免不同类型数据之间的连接。
x. 注意存储过程中参数和数据类型的关系。
xi.注意insert、update操作的数据量,防止与其他应用冲突。如果数据量超过200个数据页面(400k),那么系统将会进行锁升级,页级锁会升级成表级锁。


b) 索引的使用规范:
i.  索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引。
ii. 尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引
iii.避免对大表查询时进行table scan,必要时考虑新建索引。
iv. 在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。
v.  要注意索引的维护,周期性重建索引,重新编译存储过程。


c)tempdb的使用规范:
i.  尽量避免使用distinct、order by、group by、having、join、cumpute,因为这些语句会加重tempdb的负担。
ii. 避免频繁创建和删除临时表,减少系统表资源的消耗。
iii.在新建临时表时,如果一次性插入数据量很大,那么可以使用select into代替create table,避免log,提高速度;如果数据量不大,为了缓和系统表的资源,建议先create table,然后insert。
iv. 如果临时表的数据量较大,需要建立索引,那么应该将创建临时表和建立索引的过程放在单独一个子存储过程中,这样才能保证系统能够很好的使用到该临时表的索引。
v.  如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定。
vi. 慎用大的临时表与其他大表的连接查询和修改,减低系统表负担,因为这种操作会在一条语句中多次使用tempdb的系统表。

d)合理的算法使用:
根据上面已提到的SQL优化技术和ASE Tuning手册中的SQL优化内容,结合实际应用,采用多种算法进行比较,以获得消耗资源最少、效率最高的方法。具体可用ASE调优命令:set statistics io on, set statistics time on , set showplan on 等。

posted @ 2007-04-29 11:31 簡單就好 阅读(2579) | 评论 (17) | 编辑 收藏
 
 2007年4月27日 [转载]掌握 Ajax,第 1 部分: Ajax 简介

Ajax 由 HTML、JavaScript? 技术、DHTML 和 DOM 组成,这一杰出的方法可以将笨拙的 Web 界面转化成交互性的 Ajax 应用程序。本系列的作者是一位 Ajax 专家,他演示了这些技术如何协同工作 —— 从总体概述到细节的讨论 —— 使高效的 Web 开发成为现实。他还揭开了 Ajax 核心概念的神秘面纱,包括 XMLHttpRequest 对象。

五年前,如果不知道 XML,您就是一只无人重视的丑小鸭。十八个月前,Ruby 成了关注的中心,不知道 Ruby 的程序员只能坐冷板凳了。今天,如果想跟上最新的技术时尚,那您的目标就是 Ajax。

但是,Ajax 不仅仅 是一种时尚,它是一种构建网站的强大方法,而且不像学习一种全新的语言那样困难。

 请访问 Ajax 技术资源中心,这是有关 Ajax 编程模型信息的一站式中心,包括很多文档、教程、论坛、blog、wiki 和新闻。任何新信息都能在这里找到。
 
 
但在详细探讨 Ajax 是什么之前,先让我们花几分钟了解 Ajax 做 什么。目前,编写应用程序时有两种基本的选择:

桌面应用程序
Web 应用程序
两者是类似的,桌面应用程序通常以 CD 为介质(有时候可从网站下载)并完全安装到您的计算机上。桌面应用程序可能使用互联网下载更新,但运行这些应用程序的代码在桌面计算机上。Web 应用程序运行在某处的 Web 服务器上 —— 毫不奇怪,要通过 Web 浏览器访问这种应用程序。

不过,比这些应用程序的运行代码放在何处更重要的是,应用程序如何运转以及如何与其进行交互。桌面应用程序一般很快(就在您的计算机上运行,不用等待互联网连接),具有漂亮的用户界面(通常和操作系统有关)和非凡的动态性。可以单击、选择、输入、打开菜单和子菜单、到处巡游,基本上不需要等待。

另一方面,Web 应用程序是最新的潮流,它们提供了在桌面上不能实现的服务(比如 Amazon.com 和 eBay)。但是,伴随着 Web 的强大而出现的是等待,等待服务器响应,等待屏幕刷新,等待请求返回和生成新的页面。

显然这样说过于简略了,但基本的概念就是如此。您可能已经猜到,Ajax 尝试建立桌面应用程序的功能和交互性,与不断更新的 Web 应用程序之间的桥梁。可以使用像桌面应用程序中常见的动态用户界面和漂亮的控件,不过是在 Web 应用程序中。

还等什么呢?我们来看看 Ajax 如何将笨拙的 Web 界面转化成能迅速响应的 Ajax 应用程序吧。

老技术,新技巧

在谈到 Ajax 时,实际上涉及到多种技术,要灵活地运用它必须深入了解这些不同的技术(本系列的头几篇文章将分别讨论这些技术)。好消息是您可能已经非常熟悉其中的大部分技术,更好的是这些技术都很容易学习,并不像完整的编程语言(如 Java 或 Ruby)那样困难。

 Ajax 的定义

顺便说一下,Ajax 是 Asynchronous JavaScript and XML(以及 DHTML 等)的缩写。这个短语是 Adaptive Path 的 Jesse James Garrett 发明的(请参阅 参考资料),按照 Jesse 的解释,这不是 个首字母缩写词。
 
 
下面是 Ajax 应用程序所用到的基本技术:

HTML 用于建立 Web 表单并确定应用程序其他部分使用的字段。
JavaScript 代码是运行 Ajax 应用程序的核心代码,帮助改进与服务器应用程序的通信。
DHTML 或 Dynamic HTML,用于动态更新表单。我们将使用 div、span 和其他动态 HTML 元素来标记 HTML。
文档对象模型 DOM 用于(通过 JavaScript 代码)处理 HTML 结构和(某些情况下)服务器返回的 XML。
我们来进一步分析这些技术的职责。以后的文章中我将深入讨论这些技术,目前只要熟悉这些组件和技术就可以了。对这些代码越熟悉,就越容易从对这些技术的零散了解转变到真正把握这些技术(同时也真正打开了 Web 应用程序开发的大门)。

XMLHttpRequest 对象

要了解的一个对象可能对您来说也是最陌生的,即 XMLHttpRequest。这是一个 JavaScript 对象,创建该对象很简单,如清单 1 所示。


清单 1. 创建新的 XMLHttpRequest 对象
<script language="javascript" type="text/javascript">
            var xmlHttp = new XMLHttpRequest();
            </script>
           
 


下一期文章中将进一步讨论这个对象,现在要知道这是处理所有服务器通信的对象。继续阅读之前,先停下来想一想:通过 XMLHttpRequest 对象与服务器进行对话的是 JavaScript 技术。这不是一般的应用程序流,这恰恰是 Ajax 的强大功能的来源。

在一般的 Web 应用程序中,用户填写表单字段并单击 Submit 按钮。然后整个表单发送到服务器,服务器将它转发给处理表单的脚本(通常是 PHP 或 Java,也可能是 CGI 进程或者类似的东西),脚本执行完成后再发送回全新的页面。该页面可能是带有已经填充某些数据的新表单的 HTML,也可能是确认页面,或者是具有根据原来表单中输入数据选择的某些选项的页面。当然,在服务器上的脚本或程序处理和返回新表单时用户必须等待。屏幕变成一片空白,等到服务器返回数据后再重新绘制。这就是交互性差的原因,用户得不到立即反馈,因此感觉不同于桌面应用程序。

Ajax 基本上就是把 JavaScript 技术和 XMLHttpRequest 对象放在 Web 表单和服务器之间。当用户填写表单时,数据发送给一些 JavaScript 代码而不是 直接发送给服务器。相反,JavaScript 代码捕获表单数据并向服务器发送请求。同时用户屏幕上的表单也不会闪烁、消失或延迟。换句话说,JavaScript 代码在幕后发送请求,用户甚至不知道请求的发出。更好的是,请求是异步发送的,就是说 JavaScript 代码(和用户)不用等待服务器的响应。因此用户可以继续输入数据、滚动屏幕和使用应用程序。

然后,服务器将数据返回 JavaScript 代码(仍然在 Web 表单中),后者决定如何处理这些数据。它可以迅速更新表单数据,让人感觉应用程序是立即完成的,表单没有提交或刷新而用户得到了新数据。JavaScript 代码甚至可以对收到的数据执行某种计算,再发送另一个请求,完全不需要用户干预!这就是 XMLHttpRequest 的强大之处。它可以根据需要自行与服务器进行交互,用户甚至可以完全不知道幕后发生的一切。结果就是类似于桌面应用程序的动态、快速响应、高交互性的体验,但是背后又拥有互联网的全部强大力量。

加入一些 JavaScript

得到 XMLHttpRequest 的句柄后,其他的 JavaScript 代码就非常简单了。事实上,我们将使用 JavaScript 代码完成非常基本的任务:

获取表单数据:JavaScript 代码很容易从 HTML 表单中抽取数据并发送到服务器。
修改表单上的数据:更新表单也很简单,从设置字段值到迅速替换图像。
解析 HTML 和 XML:使用 JavaScript 代码操纵 DOM(请参阅 下一节),处理 HTML 表单服务器返回的 XML 数据的结构。
对于前两点,需要非常熟悉 getElementById() 方法,如 清单 2 所示。


清单 2. 用 JavaScript 代码捕获和设置字段值
// Get the value of the "phone" field and stuff it in a variable called phone
            var phone = document.getElementById("phone").value;
            // Set some values on a form using an array called response
            document.getElementById("order").value = response[0];
            document.getElementById("address").value = response[1];
           
 


这里没有特别需要注意的地方,真是好极了!您应该认识到这里并没有非常复杂的东西。只要掌握了 XMLHttpRequest,Ajax 应用程序的其他部分就是如 清单 2 所示的简单 JavaScript 代码了,混合有少量的 HTML。同时,还要用一点儿 DOM,我们就来看看吧。

以 DOM 结束

最后还有 DOM,即文档对象模型。可能对有些读者来说 DOM 有点儿令人生畏,HTML 设计者很少使用它,即使 JavaScript 程序员也不大用到它,除非要完成某项高端编程任务。大量使用 DOM 的是 复杂的 Java 和 C/C++ 程序,这可能就是 DOM 被认为难以学习的原因。

幸运的是,在 JavaScript 技术中使用 DOM 很容易,也非常直观。现在,按照常规也许应该说明如何使用 DOM,或者至少要给出一些示例代码,但这样做也可能误导您。即使不理会 DOM,仍然能深入地探讨 Ajax,这也是我准备采用的方法。以后的文章将再次讨论 DOM,现在只要知道可能需要 DOM 就可以了。当需要在 JavaScript 代码和服务器之间传递 XML 和改变 HTML 表单的时候,我们再深入研究 DOM。没有它也能做一些有趣的工作,因此现在就把 DOM 放到一边吧。

 

 


 回页首
 

 

获取 Request 对象

有了上面的基础知识后,我们来看看一些具体的例子。XMLHttpRequest 是 Ajax 应用程序的核心,而且对很多读者来说可能还比较陌生,我们就从这里开始吧。从 清单 1 可以看出,创建和使用这个对象非常简单,不是吗?等一等。

还记得几年前的那些讨厌的浏览器战争吗?没有一样东西在不同的浏览器上得到同样的结果。不管您是否相信,这些战争仍然在继续,虽然规模较小。但令人奇怪的是,XMLHttpRequest 成了这场战争的牺牲品之一。因此获得 XMLHttpRequest 对象可能需要采用不同的方法。下面我将详细地进行解释。

使用 Microsoft 浏览器

Microsoft 浏览器 Internet Explorer 使用 MSXML 解析器处理 XML(可以通过 参考资料 进一步了解 MSXML)。因此如果编写的 Ajax 应用程序要和 Internet Explorer 打交道,那么必须用一种特殊的方式创建对象。

但并不是这么简单。根据 Internet Explorer 中安装的 JavaScript 技术版本不同,MSXML 实际上有两种不同的版本,因此必须对这两种情况分别编写代码。请参阅 清单 3,其中的代码在 Microsoft 浏览器上创建了一个 XMLHttpRequest。


清单 3. 在 Microsoft 浏览器上创建 XMLHttpRequest 对象
var xmlHttp = false;
            try {
            xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
            try {
            xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
            xmlHttp = false;
            }
            }
           
 


您对这些代码可能还不完全理解,但没有关系。当本系列文章结束的时候,您将对 JavaScript 编程、错误处理、条件编译等有更深的了解。现在只要牢牢记住其中的两行代码:

xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");

xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");。

这两行代码基本上就是尝试使用一个版本的 MSXML 创建对象,如果失败则使用另一个版本创建该对象。不错吧?如果都不成功,则将 xmlHttp 变量设为 false,告诉您的代码出现了问题。如果出现这种情况,可能是因为安装了非 Microsoft 浏览器,需要使用不同的代码。

处理 Mozilla 和非 Microsoft 浏览器

如果选择的浏览器不是 Internet Explorer,或者为非 Microsoft 浏览器编写代码,就需要使用不同的代码。事实上就是 清单 1 所示的一行简单代码:

var xmlHttp = new XMLHttpRequest object;。

这行简单得多的代码在 Mozilla、Firefox、Safari、Opera 以及基本上所有以任何形式或方式支持 Ajax 的非 Microsoft 浏览器中,创建了 XMLHttpRequest 对象。

结合起来

关键是要支持所有 浏览器。谁愿意编写一个只能用于 Internet Explorer 或者非 Microsoft 浏览器的应用程序呢?或者更糟,要编写一个应用程序两次?当然不!因此代码要同时支持 Internet Explorer 和非 Microsoft 浏览器。清单 4 显示了这样的代码。


清单 4. 以支持多种浏览器的方式创建 XMLHttpRequest 对象
/* Create a new XMLHttpRequest object to talk to the Web server */
            var xmlHttp = false;
            /*@cc_on @*/
            /*@if (@_jscript_version >= 5)
            try {
            xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
            try {
            xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
            xmlHttp = false;
            }
            }
            @end @*/
            if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
            xmlHttp = new XMLHttpRequest();
            }
           
 


现在先不管那些注释掉的奇怪符号,如 @cc_on,这是特殊的 JavaScript 编译器命令,将在下一期针对 XMLHttpRequest 的文章中详细讨论。这段代码的核心分为三步:

建立一个变量 xmlHttp 来引用即将创建的 XMLHttpRequest 对象。
尝试在 Microsoft 浏览器中创建该对象:
尝试使用 Msxml2.XMLHTTP 对象创建它。
如果失败,再尝试 Microsoft.XMLHTTP 对象。
如果仍然没有建立 xmlHttp,则以非 Microsoft 的方式创建该对象。
最后,xmlHttp 应该引用一个有效的 XMLHttpRequest 对象,无论运行什么样的浏览器。

关于安全性的一点说明

安全性如何呢?现在浏览器允许用户提高他们的安全等级,关闭 JavaScript 技术,禁用浏览器中的任何选项。在这种情况下,代码无论如何都不会工作。此时必须适当地处理问题,这需要单独的一篇文章来讨论,要放到以后了(这个系列够长了吧?不用担心,读完之前也许您就掌握了)。现在要编写一段健壮但不够完美的代码,对于掌握 Ajax 来说就很好了。以后我们还将讨论更多的细节。

 

 


 回页首
 

 

Ajax 世界中的请求/响应

现在我们介绍了 Ajax,对 XMLHttpRequest 对象以及如何创建它也有了基本的了解。如果阅读得很仔细,您可能已经知道与服务器上的 Web 应用程序打交道的是 JavaScript 技术,而不是直接提交给那个应用程序的 HTML 表单。

还缺少什么呢?到底如何使用 XMLHttpRequest。因为这段代码非常重要,您编写的每个 Ajax 应用程序都要以某种形式使用它,先看看 Ajax 的基本请求/响应模型是什么样吧。

发出请求

您已经有了一个崭新的 XMLHttpRequest 对象,现在让它干点活儿吧。首先需要一个 Web 页面能够调用的 JavaScript 方法(比如当用户输入文本或者从菜单中选择一项时)。接下来就是在所有 Ajax 应用程序中基本都雷同的流程:

从 Web 表单中获取需要的数据。
建立要连接的 URL。
打开到服务器的连接。
设置服务器在完成后要运行的函数。
发送请求。
清单 5 中的示例 Ajax 方法就是按照这个顺序组织的:


清单 5. 发出 Ajax 请求
function callServer() {
            // Get the city and state from the web form
            var city = document.getElementById("city").value;
            var state = document.getElementById("state").value;
            // Only go on if there are values for both fields
            if ((city == null) || (city == "")) return;
            if ((state == null) || (state == "")) return;
            // Build the URL to connect to
            var url = "/scripts/getZipCode.php?city=" + escape(city) + "&state=" + escape(state);
            // Open a connection to the server
            xmlHttp.open("GET", url, true);
            // Setup a function for the server to run when it's done
            xmlHttp.onreadystatechange = updatePage;
            // Send the request
            xmlHttp.send(null);
            }
           
 


其中大部分代码意义都很明确。开始的代码使用基本 JavaScript 代码获取几个表单字段的值。然后设置一个 PHP 脚本作为链接的目标。要注意脚本 URL 的指定方式,city 和 state(来自表单)使用简单的 GET 参数附加在 URL 之后。

然后打开一个连接,这是您第一次看到使用 XMLHttpRequest。其中指定了连接方法(GET)和要连接的 URL。最后一个参数如果设为 true,那么将请求一个异步连接(这就是 Ajax 的由来)。如果使用 false,那么代码发出请求后将等待服务器返回的响应。如果设为 true,当服务器在后台处理请求的时候用户仍然可以使用表单(甚至调用其他 JavaScript 方法)。

xmlHttp(要记住,这是 XMLHttpRequest 对象实例)的 onreadystatechange 属性可以告诉服务器在运行完成 后(可能要用五分钟或者五个小时)做什么。因为代码没有等待服务器,必须让服务器知道怎么做以便您能作出响应。在这个示例中,如果服务器处理完了请求,一个特殊的名为 updatePage() 的方法将被触发。

最后,使用值 null 调用 send()。因为已经在请求 URL 中添加了要发送给服务器的数据(city 和 state),所以请求中不需要发送任何数据。这样就发出了请求,服务器按照您的要求工作。

如果没有发现任何新鲜的东西,您应该体会到这是多么简单明了!除了牢牢记住 Ajax 的异步特性外,这些内容都相当简单。应该感激 Ajax 使您能够专心编写漂亮的应用程序和界面,而不用担心复杂的 HTTP 请求/响应代码。

清单 5 中的代码说明了 Ajax 的易用性。数据是简单的文本,可以作为请求 URL 的一部分。用 GET 而不是更复杂的 POST 发送请求。没有 XML 和要添加的内容头部,请求体中没有要发送的数据;换句话说,这就是 Ajax 的乌托邦。

不用担心,随着本系列文章的展开,事情会变得越来越复杂。您将看到如何发送 POST 请求、如何设置请求头部和内容类型、如何在消息中编码 XML、如何增加请求的安全性,可以做的工作还有很多!暂时先不用管那些难点,掌握好基本的东西就行了,很快我们就会建立一整套的 Ajax 工具库。

处理响应

现在要面对服务器的响应了。现在只要知道两点:

什么也不要做,直到 xmlHttp.readyState 属性的值等于 4。
服务器将把响应填充到 xmlHttp.responseText 属性中。
其中的第一点,即就绪状态,将在下一篇文章中详细讨论,您将进一步了解 HTTP 请求的阶段,可能比您设想的还多。现在只要检查一个特定的值(4)就可以了(下一期文章中还有更多的值要介绍)。第二点,使用 xmlHttp.responseText 属性获得服务器的响应,这很简单。清单 6 中的示例方法可供服务器根据 清单 5 中发送的数据调用。


清单 6. 处理服务器响应
function updatePage() {
            if (xmlHttp.readyState == 4) {
            var response = xmlHttp.responseText;
            document.getElementById("zipCode").value = response;
            }
            }
           
 


这些代码同样既不难也不复杂。它等待服务器调用,如果是就绪状态,则使用服务器返回的值(这里是用户输入的城市和州的 ZIP 编码)设置另一个表单字段的值。于是包含 ZIP 编码的 zipCode 字段突然出现了,而用户没有按任何按钮!这就是前面所说的桌面应用程序的感觉。快速响应、动态感受等等,这些都只因为有了小小的一段 Ajax 代码。

细心的读者可能注意到 zipCode 是一个普通的文本字段。一旦服务器返回 ZIP 编码,updatePage() 方法就用城市/州的 ZIP 编码设置那个字段的值,用户就可以改写该值。这样做有两个原因:保持例子简单,说明有时候可能希望 用户能够修改服务器返回的数据。要记住这两点,它们对于好的用户界面设计来说很重要。

 

 


 回页首
 

 

连接 Web 表单

还有什么呢?实际上没有多少了。一个 JavaScript 方法捕捉用户输入表单的信息并将其发送到服务器,另一个 JavaScript 方法监听和处理响应,并在响应返回时设置字段的值。所有这些实际上都依赖于调用 第一个 JavaScript 方法,它启动了整个过程。最明显的办法是在 HTML 表单中增加一个按钮,但这是 2001 年的办法,您不这样认为吗?还是像 清单 7 这样利用 JavaScript 技术吧。


清单 7. 启动一个 Ajax 过程
<form>
            <p>City: <input type="text" name="city" id="city" size="25"
            onChange="callServer();" /></p>
            <p>State: <input type="text" name="state" id="state" size="25"
            onChange="callServer();" /></p>
            <p>Zip Code: <input type="text" name="zipCode" id="city" size="5" /></p>
            </form>
           
 


如果感觉这像是一段相当普通的代码,那就对了,正是如此!当用户在 city 或 state 字段中输入新的值时,callServer() 方法就被触发,于是 Ajax 开始运行了。有点儿明白怎么回事了吧?好,就是如此!

 

 


 回页首
 

 

结束语

现在您可能已经准备开始编写第一个 Ajax 应用程序了,至少也希望认真读一下 参考资料 中的那些文章了吧?但可以首先从这些应用程序如何工作的基本概念开始,对 XMLHttpRequest 对象有基本的了解。在下一期文章中,您将掌握这个对象,学会如何处理 JavaScript 和服务器的通信、如何使用 HTML 表单以及如何获得 DOM 句柄。

现在先花点儿时间考虑考虑 Ajax 应用程序有多么强大。设想一下,当单击按钮、输入一个字段、从组合框中选择一个选项或者用鼠标在屏幕上拖动时,Web 表单能够立刻作出响应会是什么情形。想一想异步 究竟意味着什么,想一想 JavaScript 代码运行而且不等待 服务器对它的请求作出响应。会遇到什么样的问题?会进入什么样的领域?考虑到这种新的方法,编程的时候应如何改变表单的设计?

如果在这些问题上花一点儿时间,与简单地剪切/粘贴某些代码到您根本不理解的应用程序中相比,收益会更多。在下一期文章中,我们将把这些概念付诸实践,详细介绍使应用程序按照这种方式工作所需要的代码。因此,现在先享受一下 Ajax 所带来的可能性吧。

posted @ 2007-04-27 10:23 簡單就好 阅读(566) | 评论 (1) | 编辑 收藏
 
 2007年4月26日 用XML数据岛创建上下文菜单
  上下文菜单就是用户在页面上单击右键时所显示的一组命令。微软的MSDN有一个简单的例子说明了怎样建立自定义菜单。这里,我们将通过XML的数据岛来快速创建自定义的上下文菜单。XML数据岛就是存在于HTML文档中的XML数据的一部分。通过XML文档对象模型[XML document object model (DOM)],我们可以轻松地参考和引用XML里的内容。我们这里利用XML数据岛来存储上下文菜单的多个定义,其中的每一个定义都可以和文档中的任一元素相联系。在没有定义的地方,将显示默认的菜单。
  Internet Explorer 5.0首次提出对上下文菜单和数据岛的支持,我们的例子在除Internet Explorer 5.0及以上的浏览器里将自动被忽略。因此,如果你使用的浏览器不是Internet Explorer 5.0及以上的版本,你将看不到任何效果,只能看到浏览器的默认菜单。如果你使用的是Internet Explorer 5.0及以上的浏览器,你可以在页面上点击鼠标右键来看效果。注意:点击图象和文字将显示不同的菜单。下面我们进行分析:


第一步:定义菜单
  定义菜单是在文档XML数据岛里的进行的,你只需简单地在HTML文档的HEAD部分包含XML文件即可。例如:可以定义如下:

<xml id="contextDef">
<xmldata>
<contextmenu id="demo">
<item id="viewsource" value="查看源文件"/>
<item id="back" value="返回上页"/>
</contextmenu>
<contextmenu id="demob">
<item id="menu1" value="菜单项1" />
<item id="menu2" value="菜单项2" />
</contextmenu>
</xmldata>
</xml>

  在这里,带ID属性的<xml>根节点和<xmldata>节点是必须的[注意:在XML里大小写是敏感的]。一个contextmenu节点和它所包含的多个item节点定义了一个菜单。如果你要定义多个菜单,你只需定义多个contextmenu节点即可。contextmenu节点的id属性和页面中的相应元素相关联,item节点的id属性标明哪一个菜单项被我们选取。值得注意的是:在整个XML文档里,所有的ID属性不能重名。item节点的value值就是要在菜单里要显示的文字。

第二步:和HTML里的元素相关联

  在上面的XML数据岛里,我们定义了两个菜单demo和demob,要想和HTML里的元素相关联,只需简单地把contextmenu的ID值和HTML元素的contextmenu属性相连接即可。

<P contextmenu="demo">这个段落显示demo菜单的内容</P>
<IMG SRC="usedemob.gif" contextmenu="demob">

第三步:编写点击菜单项的执行的操作

  当我们单击菜单的每一个选项时,函数fnFireContext就被调用,并把代表所选菜单的一个对象参数传过来。为了处理单击的事件,只需编写简单的switch语句,根据不同的ID值执行不同的操作。例如:

function fnFireContext(oItem) {
switch (oItem.menuid) {
case "viewsource":
location.href = "view-source:" + location.href
break;
case "back":
history.back()
break;
default:
alert("您选的是:/n" + oItem.menuid + "/nText: " +
oItem.innerText)
}
}

你可以根据自己的需要进行更改鼠标单击事件的操作。

第四步:定义菜单外观

  定义外观只需使用样式单即可,下面我们给出完整的例子,你完全可以拷贝、粘贴来看到本例子的效果!![注意:浏览器必需是IE5+]。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<style>
.menu{ cursor: hand;
display: none;
position: absolute;
top: 0; left: 0;
overflow: hidden;
background-color: "#CFCFCF";
border: "1 solid";
border-top-color: "#EFEFEF";
border-left-color: "#EFEFEF";
border-right-color: "#505050";
border-bottom-color: "#505050";
font: 10pt 宋体;
margin:0pt;padding: 2pt
}

.menu SPAN {width: 100%; cursor: hand; padding-left: 10pt}
.menu SPAN.selected {background: navy; color:white; cursor: hand}
</style>

<xml id="contextDef">
<xmldata>
<contextmenu id="demo">
<item id="viewsource" value="查看源文件"/>
<item id="back" value="后退……"/>
<item id="meng" value="访问【孟宪会之精彩世界】"/>
<item id="calculate" value="执行 JavaScript 代码"/>
</contextmenu>
<contextmenu id="demob">
<item id="菜单项例子1" value="菜单项例子1" />
<item id="菜单项例子2" value="菜单项例子2" />
</contextmenu>
</xmldata>
</xml>

<SCRIPT>

// 定义全局变量
var bContextKey=false;

function fnGetContextID(el) {
while (el!=null) {
if (el.contextmenu) return el.contextmenu
el = el.parentElement
}
return ""
}

function fnDetermine(){
oWorkItem=event.srcElement;

//键盘上的菜单键被按下时。
if(bContextKey==true){

//如果菜单的“状态”为“false”
if(oContextMenu.getAttribute("status")=="false"){

//捕获鼠标事件,以便和页面交互。
oContextMenu.setCapture();

//根据鼠标位置,确定菜单位置。
oContextMenu.style.top=event.clientY + document.body.scrollTop +
1;
oContextMenu.style.left=event.clientX + document.body.scrollLeft +
1;
oContextMenu.innerHTML="";

//设定菜单的“状态”为“true”
var sContext = fnGetContextID(event.srcElement)
if (sContext!="") {
fnPopulate(sContext)
oContextMenu.setAttribute("status","true");
event.returnValue=false;
}
else
event.returnValue=true
}
}
else{

// 如果键盘菜单键没有按下,并且菜单的“状态”为“true”。
if(oContextMenu.getAttribute("status")=="true"){
if((oWorkItem.parentElement.id=="oContextMenu") &&
(oWorkItem.getAttribute("component")=="menuitem")){
fnFireContext(oWorkItem)
}

// 当鼠标离开菜单或单击菜单项后,重设菜单(隐藏)

oContextMenu.style.display="none";
oContextMenu.setAttribute("status","false");
oContextMenu.releaseCapture();
oContextMenu.innerHTML="";
event.returnValue=false;
}
}
}


function fnPopulate(sID) {
var str=""
var elMenuRoot =
document.all.contextDef.XMLDocument.childNodes(0).selectSingle
Node(''contextmenu[@id="'' + sID + ''"]'')
if (elMenuRoot) {
for(var i=0;i<elMenuRoot.childNodes.length;i++)
str+=''<span component="menuitem" menuid="'' +
elMenuRoot.childNodes[i].getAttribute("id") +
''" id=oMenuItem'' + i + ''>'' +
elMenuRoot.childNodes[i].getAttribute("value") +
"</SPAN><BR>"
oContextMenu.innerHTML=str;
oContextMenu.style.display="block";
oContextMenu.style.pixelHeight = oContextMenu.scrollHeight
}
}

function fnChirpOn(){
if((event.clientX>0) &&(event.clientY>0)
&&(event.clientX<document.body.offsetWidth)
&&(event.clientY<document.body.offsetHeight)){
oWorkItem=event.srcElement;
if(oWorkItem.getAttribute("component")=="menuitem"){
oWorkItem.className = "selected"
}
}
}
function fnChirpOff(){
if((event.clientX>0) && (event.clientY>0) &&
(event.clientX<document.body.offsetWidth) &&
(event.clientY<document.body.offsetHeight)){
oWorkItem=event.srcElement;
if(oWorkItem.getAttribute("component")=="menuitem"){
oWorkItem.className = ""
}
}
}

function fnInit(){
if (oContextMenu) {
oContextMenu.style.width=180;
oContextMenu.style.height=document.body.offsetHeight/2;
oContextMenu.style.zIndex=2;

//设置菜单样式
document.οncοntextmenu=fnSuppress;
}
}

function fnInContext(el) {
while (el!=null) {
if (el.id=="oContextMenu") return true
el = el.offsetParent
}
return false
}

function fnSuppress(){
if (!(fnInContext(event.srcElement))) {
oContextMenu.style.display="none";
oContextMenu.setAttribute("status","false");
oContextMenu.releaseCapture();
bContextKey=true;
}

fnDetermine();
bContextKey=false;
}

function javameng(){
window.open("http://lucky.myrice.com","_blank","width=400,height=
400,top=20,left=20")
}

function fnFireContext(oItem) {

// 自定义上下文菜单项的功能
switch (oItem.menuid) {
case "viewsource":
location.href = "view-source:" + location.href
break;
case "back":
history.back()
break;
case "meng":
location.href="http://lucky.myrice.com"
break;
case "calculate":
javameng()
break;
default:
alert("你点击的菜单项是:/n/n/n" + oItem.menuid +"啊!!!")
}
}
</SCRIPT>

<BODY οnlοad="fnInit()" οnclick="fnDetermine()" bgcolor="#ccffcc">
<div status="false" οnmοuseοver="fnChirpOn()" οnmοuseοut="fnChirpOff()" id="oContextMenu" class="menu"></div>这里放你任意的其他的东西! ...<br>... 这里放你任意的其他的东西! ...<br>... 这里放你任意的其他的东西! ...<br><br>
<P contextmenu="demo">这里是利用上下文菜单的里子!你把鼠标移动到这里,然后单击鼠标又键,可以看到菜单内容?lt;br>这里是利用上下文菜单的里子!你把鼠标移动到这里,然后单击鼠标又键,可以看到菜单内容!<br>这里是利用上下文菜单的里子!
你把鼠标移动到这里,然后单击鼠标又键,可以看到菜单内容!<br>这里是利用上下文菜单的里子!你把鼠标移动到这里,然后单击鼠标又键,可以看到菜单内容!<br>这里是利用上下文菜单的里子!你把鼠标移动到这里,然后单击鼠标又键,可以看到菜
单内容!<br></p><p>你也可以把鼠标放到下面的图象上面,点击又键!<p>
<center><IMG SRC="http://lucky.myrice.com/javabk1.jpg" contextmenu="demob">
</body>
</html>
posted @ 2007-04-26 13:16 簡單就好 阅读(26) | 评论 (0) | 编辑 收藏
 
 2007年4月23日 AOP 实现
向来我是“拿来主义”,即只管拿来用,不管正在用的东西是怎么实现的。最近由于一直想把 AOP 以及 Io C 等技术加入到项目中,因此对这些技术相当关注。后来选择了CastleProject中的DynamicProxy作为关注对象。不过这次起了贪心,不想再只知道使用,不知道如何实现了,于是开始深入去查看Castle是如何实现 AOP 的。

Proxy 是实现 AOP 的途径之一,通过代理可以有效的拦截某个类方法的执行过程。

从Castle中的测试例子可以看出,Castle使用了动态代码生成来对需要拦截的类生成一个代理,从而达到织入的目的。但是Castle是如何织入的呢?我打算使用一个简单的例子来测试Castle动态生成的代码。

Scene我设想了一个例子,一个类有一个方法:DoSomething,这个方法调用时输出Yes, * is running DoSomething,其中 * 表示类的名称。然后我需要在这个类调用DoSomething之前及之后,还需要输出Now i'm starting Do Something以及 Now i'm ending DoSomething。当然,有很多方法可以实现,由于Castle是使用代理,所以我这里只使用代理。 
对于传统的代理模式,可以得出如下的类图:

图1

 

上图的类的代码分别如下:

图2:类A的代码

 

图3:类RealANoVirtual的代码

 

图4:类ProxyClass的代码

 

然后,在使用这些时,可以如下编写代码:

图5:运行代码

 

运行上面的代码,我们就可以特出如下的结果:

图6:运行结果

 

通过代理,可以透明的使用DoSomething方法。

那么很显然,Castle也需要生成这样一个代理类,从而能够在调用DoSomething之前调用用户指定的代码。因此Castle提供了DynamicProxy以及Interceptor来达到目的。其中DynamicProxy会生成一个代理类,代理DoSomething操作,而在代理DoSomething操作时,使用用户指定的Interceptor达到拦截操作的目的。

为了看到Castle生成的动态代理是怎么样的,我写了一个类:RealA,代码如下:

图7:RealA代码

 

RealA中只有一个方法,DoSomething。我需要在DoSomething操作前后达到与前面的传统的模式一样的效果,按照Castle的要求,我需要写一个Interceptor,从而能够拦截前后的操作,代码如下:

图8:Interceptor代码

 

暂时不去考虑ProxyInterceptor是怎么一回事,继续往下。

有了需要拦截的类与Interceptor,那么直接就可以使用了,如下的代码:

图9:运行代码

 

图10:运行的结果

 

Bingle!!!,成功了。

那么Castle究竟做了什么呢?

在运行时,Castle会生成一个Assembly,放在应用的运行目录下,名称为:GeneratedAssembly.dll。通过反编译这个Assembly,就可以看到Castle究竟做了什么。

为了更好的说明,我把反编译后的代码分段说明:

1、Castle生成了一个代理类CProxyTypeRealA0,代理类继承自RealA:

public class C Proxy Type Real A0 : Real A

{

...

}

2、在CProxyTypeRealA0类中如下定义了一些Field:

图11:Field定义

 

其中的delegate的定义如下:

图12:delegate的定义

 

那么,这些Delegate的目的是什么呢?通过CProxyTypeRealA0的构造函数中的代码可以看出用途:

图13:构造函数

 

这里,我们关注的是DoSomething。从上面的构造函数代码可以看出,delegate指向了callback__DoSomething这个回调函数,这个函数的代码如下:

图14:回调函数代码

 

在这个回调函数中,就直接调用了RealA的DoSomething方法。

那么,Castle是如何调用到RealA的DoSomething的方法的呢?

3、调用链

在CProxyTypeRealA0中,覆盖了RealA的DoSomething方法,如下:

图15:覆盖的DoSomething

 

从代码中可以看出,DoSomething中调用了本地方法_Method2Invocation获取一个Invocation,然后调用Interceptor执行一些动作。

仔细看,在_Method2Invocation调用时传入了一个参数this.cached1,这个参数是一个delegate对象,并且在构造函数中指向了回调函数callback_DoSomething。这是一个很关键的地方,因此我们有必要看看_Method2Invocation作了什么。

图16 Method2Invocation的代码

 

Method2Invocation方法中,创建/获取了一个MethodInfo对应的Invocation,而Invocation封装了传入的delegate等相关信息。

很显然,封装这些信息是为了更好的传递。从Method2Invocation出来,接下去就是调用在构造函数中传入的Interceptor。

Interceptor的Intercept方法需要两个参数,一个就是前面封装好的Invocation对象,另一个是一个参数数组。目前看不出这个参数数组有什么用。

那么Intercept方法作了什么

看看Interceptor的实现代码,我的例子中的Interceptor直接继承自StandardInterceptor,所以先看看StandardInterceptor的代码:

图17 StandardInterceptor的代码

 

从代码中可以看出,这是一个模板模式。分别调用了PreProcess,PostProcess方法。这样就可以调用了我覆盖的两个方法。但是,在这里依然没有看到调用了DoSomething方法,别急,代码中调用了传入的Invocaion对象的Process方法。那么这个方法作了什么?

由于在Method2Invocation中,创建的是SameClassInvocation对象,因此,直接看SameClassInvocation的代码:

图18 SameClassInvocation的代码

 

Bingle!!! Process方法调用了传入的delegate对象,这样就调用到了回调函数callback_DoSomething,而回调函数又调用了RealA的DoSomething方法。这样,一个完整的拦截过程就实现了。同时,这里也看到前面的参数数组就是DoSomething的

参数数组,只不过例子中没有参数,所以为零了。

至此,我们看到了Castle完成代码织入的整个过程:

首先通过代码生成完成一个代理类,该代理类继承自要织入的类。然后在代理类中覆盖要拦截的方法,并在覆盖的方法中封装Invocation对象,并传给用户传入的Intercepter对象的Intercept方法。在Intercept方法依次调用Intercepter的PreProcess,通过Invocation传入的Delegate指向的回调函数,Intercepter的PostProcess方法,从而达到拦截的目的。

下面是例子的代码下载:

http://www.zeroport.net/files/DynamicProxy.zip

示例在Snippet Comiler 2.0 中编译运行通过。编译时需要自行添加对Castle.DynamicProxy.dll的引用。

如果在VS.NET中运行,需要自己创建Project,并加入压缩包中的文件。

Click to read this topic
2005-4-5 15:41:09 - endlessnet@msn.com
posted @ 2007-04-23 16:58 簡單就好 阅读(66) | 评论 (1) | 编辑 收藏
 
[转]基于AJAX的ASP.NET聊天室 & 如何建立共识
好久没有写点东西了,很久以前就对EnterpriseLibrary很感兴趣,但是一直没有时间去研究和使用这个强大的企业库。最近终于狠下心来,下载了Microsoft Enterprise Library 3.0 - December 2006 CTP,但对这么大的一个类库,想一下子进入状态那是不可能的,从中发现配置文件的关键,特别是配置文件的操作那是相当的不错。我想不论是小网站还是大项目,都会涉及到对配置文件的操作,通过收集和我做过得一些项目在这里整理了一下.NET2.0对配置文件的操作。

在客户端应用程序中我们可以用ConfigurationManager这个类来操作,在Web程序中我们可以应用WebConfigurationManager这个类,其实他们使用方法差不多,他们分别位于System.Configuration和System.Web.Configuration命名空间下。我们要对配置文件进行操作当然我们的引入该类所在的命名空间,由于我们经常见到Web应用程序这里主要讨论一下Web.Config中的操作。

对原有的节点的读取等操作.NET都有了很好的封装,下面我们来看看对自定义节点的操作,有了这个咚咚我们就可以在后台添加一个UI以实现对配置文件某些节点的修改和增添,),9L1专I@qKVy!(当然也可以应用于版本升级等。我们先建立一个我们要定义的节点的类,该类必须继承System.Configuraion.ConfigurationSection类,如果有嵌套元素必须继承SystemConfiguration.ConfigurationElement类,在将该类或其集合包含于父节点的类中。

    public class NSection : ConfigurationSection {

        public NSection() {

       

        }

        [ConfigurationProperty("id")]

        public int ID

        {

            get { return (int)this["id"]; }

            set { this["id"] = value; }

        }

 

        [ConfigurationProperty("name")]

        public string Name

        {

            get { return this["name"].ToString(); }

            set { this["name"] = value; }

        }

 

        public override string ToString()

        {

            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("id = {0};name = {1}", ID, Name);

            return sb.ToString();

        }

    }

上面我们定义了一个类,即我们要定义的节点,我们在节点中插入一个ID和Name。下面我们把这个节点加入到配置文件中:(注:可能原来有节点存在,

`G理件/EKe[a的59B垠b(_

我们先移除后加入!)看看代码;
 

    protected void Page_Load(object sender, EventArgs e)

    {

        NSection section = new NSection();

        section.ID = 1;

        section.Name = "Test";

        Configuration config = WebConfigurationManager.OpenWebConfiguration("~");

        config.Sections.Remove("nSection");

        config.Sections.Add("nSection", section);

        config.Save();

    }

运行后看看Web.config有什么变化,4Jwtm网9`xJNI教件[|a是不是多了一个nSection节点,由于我是直接在Default.aspx中测试,所以加入configSections中的type属性有点别扭,看看配置(Web.config)文件:

    <configSections>

        <section name="nSection" type="_Default+NSection, App_Web_sct0la5g, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" />

    </configSections>

    <nSection id="1" name="Test" />

 

以上实现了对配置文件节点的增添,业D}^IhA|理4]q下面看看对现有文件的修改,同样应用上面的类:

Configuration config1 = WebConfigurationManager.OpenWebConfiguration("~");

        NSection section1 = config1.GetSection("nSection") as NSection;

        section1.ID = 2;

        section1.Name = "Test2";

        config1.Save();

这里可能会出现一个错误,如果你也用Default.aspx文件来直接定义NSection类,应为程序集的应用会出错,即<section name="nSection" type="_Default+NSection, App_Web_sct0la5g, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" />。运行上面的程序就可以得到我们所有的结果。有时候一个配置文件显得太扎乱,需要多个配置文件,那如何操作其他的配置文件呢?我们可以通过ConfigurationFileMap fileMap = new ConfigurationFileMap();

        fileMap.MachineConfigFilename = Server.MapPath("NExplus.config");

        System.Configuration.Configuration config = ConfigurationManager.OpenMappedMachineConfiguration(fileMap);来打开其他的和Web.config没有什么区别。值得注意的是其他的配置文件中必须要有:

<?xml version="1.0"?>

 <configuration>

 </configuration>

否则就会报错。

    我想很多人都用过序列化和反序列化来完成这样的操作,得到的结果是一样的。微软给了我们这个咚咚,我们应该用用J.其实在客户端的应用程序也就是用不同的类而已,用不同的方法来打开配置文件,其他的没什么不同。这篇文章是为了能更好的理解EnterpriseLibrary而作的一个总结,希望对大家也带来一点帮助!

posted @ 2007-04-23 16:45 簡單就好 阅读(51) | 评论 (0) | 编辑 收藏
 
 2007年4月16日 如何操作Excel
--添加引用 com 里面 Microsoft Excel 11.0 Object Library
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Office.Interop;
using System.Reflection;
using System.Data.SqlClient;
namespace ConsoleApplication1
{
    class Program
    {
        string ConnectionString = "server=.;uid=sa;pwd=sasa;database=pubs";

        static void Main(string[] args)
        {
        
         
            Microsoft.Office.Interop.Excel.Application xApp = new Microsoft.Office.Interop.Excel.ApplicationClass();

            xApp.Visible = true;
            //得到WorkBook对象, 可以用两种方式之一: 下面的是打开已有的文件
            Microsoft.Office.Interop.Excel.Workbook xBook = xApp.Workbooks._Open(@"c:/1.xls",
                Missing.Value, Missing.Value, Missing.Value, Missing.Value
                , Missing.Value, Missing.Value, Missing.Value, Missing.Value
                , Missing.Value, Missing.Value, Missing.Value, Missing.Value);
            //xBook=xApp.Workbooks.Add(Missing.Value);//新建文件的代码
            //指定要操作的Sheet,两种方式:

            Microsoft.Office.Interop.Excel.Worksheet xSheet = (Microsoft.Office.Interop.Excel.Worksheet)xBook.Sheets[1];
            //Excel.Worksheet xSheet=(Excel.Worksheet)xApp.ActiveSheet;

            //读取数据,通过Range对象
            //Microsoft.Office.Interop.Excel.Range rng1 = xSheet.get_Range("A1", Type.Missing);
            //Console.WriteLine(rng1.Value2);

            for (int i = 2661; i < 4615; i++)
            {
                //读取,通过Range对象,但使用不同的接口得到Range
                Microsoft.Office.Interop.Excel.Range rng2 = (Microsoft.Office.Interop.Excel.Range)xSheet.Cells[i, 1];
             //   Console.WriteLine(rng2.Value2);
                Program pro = new Program();
            
                //写入数据
                Microsoft.Office.Interop.Excel.Range rng3 = xSheet.get_Range("B"+i, Missing.Value);
                rng3.Value2 = pro.select(rng2.Value2.ToString());
            }
        //    Console.Read();

       

            //保存方式一:保存WorkBook
            xBook.SaveAs(@"c:/2.xls",
                Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value,
                Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange, Missing.Value, Missing.Value, Missing.Value,
                Missing.Value, Missing.Value);
            Console.WriteLine("aa");
            Console.Read();
        }
        public string select(string name)
        {
            using (SqlConnection conn = new SqlConnection(ConnectionString))
            {
                conn.Open();
                string strsql = string.Format("select * from jobs", name);
                using (SqlCommand cmd = new SqlCommand(strsql,conn))
                {
                    using (SqlDataReader sda = cmd.ExecuteReader())
                    {
                        if (sda.Read())
                        {
                            return sda["job_desc"].ToString();
                        }
                        else
                        {
                            return "";
                        }
                    }
                }
            }
        }
    }
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值