[转载].NET Framework 2.0高级编程学习笔记(一):类型,泛型,集合

mikel阅读(770)

[转载].NET Framework 2.0高级编程学习笔记(一):类型,泛型,集合 – ForEvErNoMe – 博客园.

最近在学习.NET Framework 2.0高级编程这本书,感觉挺有意思的,于是根据自己的理解,做了笔记,总结下内容。本文笔记主要是从.NET类型,泛型,集合这三个方面进行描述。

1.类型

类型是对程序要处理的数据对象的分类。不同的数据对象占用存储空间不同,操作处理方法不同,所以必须分门别类;

(1)C#的主要类型有:

基本类型:数值、字符、逻辑型、字符串、对象(object)

系统或自定义的类型:结构、枚举、类等等。

上述这些类型,所占的内存空间不同。有的类型占用内存空间是确定的,有的不确定。

根据它们被分配存储空间方式的不同,可以分为:值类型/引用类型两大类。

上述的类型中,你是否又能区分它们属于哪一类呢?

基本数据类型大部分是值类型,除了object, string属于引用类型;类是引用类型;结构和枚举是值类型。

(2)内存的使用分别:栈(stack), 堆(heap) 静态区(static)。

值类型直接分配在栈上;

引用类型包括两部分:对象的引用(地址)在栈(stack)上,对象本身在堆(heap)上。

静态区(static)存放的是与对象的实例无关的部分。即当程序一装入内存,就要分配好。

如:入口方法,构造方法,常量,其他用static修饰的成员。

(3)举例:

例一:

int x, y=0;
x = y;
y = 9;

例二:设有类Myobj定义为:

class Myobj

{

      public int age;

      public string name;      

}

Myobj a, b;

a = new Myobj();

a.age = 28;     a.name = "puppy";

b = a;

a.age = 18;     a.name = "doggy";

Console.WriteLine(b.age);

Console.WriteLine(b.name);

例三:

string x, y="string1";

x = y;

y = "string2";

Console.WriteLine(x);

第一个例子输出是0。当y=9时,实际上是在栈中重新分配一个内存地址。

第二个例子输出是18和doggy。b=a时,a和b同时指向一个地址空间

第三个例子输出是string1。string是一个特殊的引用类型。 string实例在内存中不可修改。当需要修改时,总是创建一个新的实例,并将变量指向新的实例。

(在程序中频繁修改字符串变量,会产生大量内存垃圾。这是我们要使用StringBuilder类的原因)

(4)装箱和拆箱

装箱:将值类型转换为引用类型,按照自己的理解来说在堆中生成一个新的对象,栈中原本的地址指向该对象。

拆箱:将引用类型转换为值类型,大致同上,过程相反。

int x=1;

Object y=x;//装箱

int z=(int)y;/拆箱

2.泛型

问题的缘起:

请看一个Stack的程序。Stack是一种数据结构。可以存放许多元素,遵循后进先出的原则。

举例:(Stack)

class StackOfInt {

        private int[] m_ItemArray;

        private int m_Index = 0;

        public const int Max_Size = 100;

        public StackOfInt () { m_ItemArray = new int[Max_Size]; }

        public int Pop() {

              if (m_Index == 0)

                  throw new System.InvalidOperationException("Can't

                       Pop an empty stack.");

              return m_ItemArray[--m_Index];

        }

        public void Push(int item){

               if (m_Index == Max_Size)

                    throw new System.InvalidOperationException("Can't 

                             push an item on a full stack.");

               m_ItemArray[m_Index++] = item;

        }

}

缺点:这样的栈用object类型作为元素类型,可以供所有类型使用

但在压入元素时,要装箱;取用元素时要拆箱,代价很大,执行效率低;

类型不安全。转换类型时容易出错。

解决方案:如果我们可以在Stack类的定义中,提供一个类作参数,则可以简化此问题的解决。泛型的定义:在定义一个类型时,使用另一个或几个类型为参数,类型参数用<>围住,放在所定义的类型名后面。

在使用带有类型参数的泛型类型时,同时要给定参数类型的具体类型。

举例:定义泛型Stack类

class Stack<T> {

        private T[] m_ItemArray;

        private int m_Index = 0;

        public const int Max_Size = 100;

        public Stack() { m_ItemArray = new T[Max_Size]; }

        public T Pop() {

              if (m_Index == 0)

                  throw new System.InvalidOperationException("Can't

                       Pop an empty stack.");

              return m_ItemArray[--m_Index];

        }

        public void Push(T item){

               if (m_Index == Max_Size)

                    throw new System.InvalidOperationException("Can't 

                             push an item on a full stack.");

               m_ItemArray[m_Index++] = item;

        }

}

泛型的优点:

让代码更具有通用性,更简洁;

强类型,类型安全,不用担心类型转换错;

不用浪费很多装箱、拆箱的时间。

3.集合

集合类型是其对象中包含多个其他对象的特殊类型。

数组是最常用的集合类型。

要进一步理解的是,当我们在程序中申明一个数组时,实际上是指示CLR在执行期间帮我们创建和管理一个集合类型;

该集合类型的基类为System.Array

所以,我们可以对数组使用一些固有的属性和方法,如Length,Rank,Copy,Clone。他们来自Array类;还可以对数组用foreach来遍历。这是因为Array已经实现IEnumerable 接口。

由此我们可以牢记,数组是引用类型。可以用数组作为方法的参数,不使用ref也可以从方法中传出修改的结果

举例:

using System.Collection.Generic;

public class Program {

       public static void Main() {

              List<int> list = new List<int>(3);

              for (int i=0; i<8; i++) {

                    list.Add(i);

                    System.Console.WriteLine(“Count: {0} Capacity: {1}”,

                               list.Count, list.Capacity);

              }

              list.TrimExcess();

              System.Console.WriteLine(“Count: {0}  Capacity: {1}”,

                               list.Count, list.Capacity );

        }

}


完….待续!

[转载]MSSQL存储过程数据库多级分类代码

mikel阅读(888)

[转载]共享一个数据库多级分类代码(MSSQL存储过程版) – 遗忘海岸 – 博客园.

 

说明

相信多级分类在任何一个信息系统中都会用到,网上也能找到很多版本,下面这个是基于MSSQL存储过程版的,

手上还有VB跟C#版的,不过这几年数据库一直用MSSQL,编程语言却从VBScript到C#又到PB, 搞到现在这套分类代码分别用VB、C#、PB实现了一遍,浪费了不少时间,NND神马多数据库啊!!!哥被忽悠了。

分类采用前缀编码的方式,编码使用字符串类型的,当然也有使用二进制实现的牛人^_^.

表结构
说明(表Category,ClassId,ClassName,Code 为分类相关字段,DataNum,Info等是根据具体情况额外增减)

存储过程

--******************************
-- 多级分类存储过程
-- WDFrog 2012-2-15
-- http://wdfrog.cnblogs.com
--******************************

--******************************
--数据表定义
--******************************
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[Category]
GO

CREATE TABLE [dbo].[Category] (
[ClassID] [int] NOT NULL ,
[ClassName] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL ,
[Code] [nvarchar] (200) COLLATE Chinese_PRC_CI_AS NOT NULL ,
[DataNum] [int] NULL ,

[Info] [nvarchar] (1000) COLLATE Chinese_PRC_CI_AS NULL

) ON [PRIMARY]
GO

ALTER TABLE [dbo].[Category] ADD
CONSTRAINT [DF_Category_DataNum] DEFAULT (0) FOR [DataNum],
CONSTRAINT [PK_Category] PRIMARY KEY CLUSTERED
(
[ClassID]
) ON [PRIMARY]
GO

--*************************
-- 添加分类存储过程
--***************************

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category_Add]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[Category_Add]
GO

Create Proc Category_Add
@ClassName nvarchar(50),
@DataNum int ,

@Info nvarchar(1000),
@ParentID int -- 0表示根类别
As
Declare @EditCode int
Declare @StepLen int
Declare @matchStr nvarchar(50)
Declare @typeCode nvarchar(50)
Declare @Code nvarchar(200)
Declare @MyCode nvarchar(200)
Declare @ParentCode nvarchar(200)
Declare @selfCode int
Set @editCode=1
Set @StepLen=4
Set @matchStr=REPLICATE('_',@StepLen) --4个_
set @typeCode=''
Set @Code=''
Set @MyCode=''
Set @selfCode=0
Set @ParentCode=''

Select @ParentCode=Code From [Category] Where ClassID=@ParentID

If(@editCode=1)
Begin
--获取子类中编号最大的Code,column.ParentCode + matchStr中
Select Top 1 @MyCode= Code From [Category] Where Code Like @ParentCode + @matchStr Order By Code DESC
If @@ROWCOUNT &gt;0
Begin
Set @selfCode=Cast(Right(@MyCode,@StepLen) As Int ) +1
Set @typeCode=Replicate('0',@StepLen-1) + Cast(@selfCode As nvarchar)
Set @typeCode=Right(@typeCode,@StepLen)
Set @typeCode=@ParentCode + @TypeCode
End
Else
Begin
Set @typeCode=@ParentCode +Replicate('0',@StepLen-1)+'1'
End
End
Declare @ClassID int
Set @ClassID=0
--获取最大ClassID
Select @ClassId=Max(ClassID) From [Category]
If Not @ClassID Is Null
Begin
Set @ClassId=@ClassID +1
End
Else
Begin
Set @ClassID=1
End

Insert into [Category]
(ClassID,ClassName,Code,DataNum, Info)
values
(@ClassID,@ClassName,@typeCode,@DataNum, @Info)

Select @ClassID As ClassID
Go

--********************
-- 修改分类存储过程
--*********************
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category_Update]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[Category_Update]
GO

Create Proc Category_Update
@ClassID int , --需要修改的ClassID
@ClassName nvarchar(50),

@Info nvarchar(1000),
@ParentID int
As
Declare @EditCode int
Declare @StepLen int
Declare @matchStr nvarchar(50)
Declare @typeCode nvarchar(50)
Declare @Code nvarchar(200)
Declare @MyCode nvarchar(200)
Declare @ParentCode nvarchar(200)
Declare @selfCode int
Set @editCode=0
Set @StepLen=4
Set @matchStr=REPLICATE('_',@StepLen) --4个_
set @typeCode=''
Set @Code=''
Set @MyCode=''
Set @selfCode=0
Set @ParentCode=''

Select @ParentCode=Code From [Category] Where ClassID=@ParentID
Select @Code=Code From [Category] Where ClassID=@ClassID

--修改原有类别
--确定是否要修改Code字段
--查看是否改变了直接父类别(上一级)
If @ParentCode != Left(@code,len(@code)-@StepLen)
Begin
--过滤选择自己做为父类
If(@ParentCode !=@Code)
Begin
--过滤选择自己的子类为父类
If Len(@ParentCode) &gt; Len(@Code)
Begin
--因为 Len(@ParentCode) &gt; Len(@Code) 所以可以Left(@ParentCode,Len(@Code))
If Left(@ParentCode,Len(@Code)) != @Code --如果相等则为选择自己的子类为父类
Begin
Set @EditCode=1
End
End
Else
Begin
Set @EditCode=1
End
End

End

If(@editCode=1)
Begin
--获取子类中编号最大的Code,column.ParentCode + matchStr中
Select Top 1 @MyCode= Code From [Category] Where Code Like @ParentCode + @matchStr Order By Code DESC
--是否有子类
If @@ROWCOUNT &gt;0
Begin
Set @selfCode=Cast(Right(@MyCode,@StepLen) As Int ) +1
Set @typeCode=Replicate('0',@StepLen-1) + Cast(@selfCode As nvarchar)
Set @typeCode=Right(@typeCode,@StepLen)
Set @typeCode=@ParentCode + @TypeCode
End
Else --没有子类那么编号从1开始
Begin
Set @typeCode=@ParentCode +Replicate('0',@StepLen-1)+'1'
End
End

If (@editCode=1)
Begin
Update [Category] Set
ClassName=@ClassName,Code=@typeCode, Info=@Info
where ClassID=@ClassID
End
Else
Begin
Update [Category] Set
ClassName=@ClassName, Info=@Info
where ClassID=@ClassID
End
---修改子类编号(Code)
If(@editCode=1)
Begin
Update [Category] Set
Code=@typeCode + Right(Code,Len(Code)-Len(@Code))
Where Code Like @Code + '%'
End

GO

--************************************
-- 删除一个分类,只允许删除没有子类的分类
--************************************

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category_Del]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[Category_Del]
GO

Create Proc Category_Del
@ClassID int
As
If (Select Count(ClassID) From[Category] Where Code Like(Select Code From [Category] Where ClassID=@ClassID)+'%' And ClassId &lt;&gt; @ClassId ) &gt;0
Begin
RaisError ('不能删除带有子类的分类',16,1)
Return
End

Declare @Code nvarchar(200)
Declare @Value int
Set @Value=0
Select @Code=[Code],@Value=[DataNum] From [Category] Where [ClassID]=@ClassID
Update [Category] Set [DataNum]=[DataNum] - @Value Where [ClassID] In( Select ClassID From [Category] Where Len(Code) 4)
Begin
Select Top 1 @tempStr=Code From [Category] Where Len(Code)=Len(@Code) And Code &lt; @Code And Left(Code,Len(Code)-4)=Left(@Code,Len(@Code)-4) Order By Code DESC
End
Else
Begin
Select Top 1 @tempStr=Code From [Category] Where Len(Code)=Len(@Code) And Code &lt; @Code Order By Code DESC End End Else Begin If(Len(@Code) &gt;4)
Begin
Select Top 1 @tempStr=Code From [Category] Where Len(Code)=Len(@Code) And Code &gt; @Code And Left(Code,Len(Code)-4)=Left(@Code,Len(@Code)-4) Order By Code ASC
End
Else
Begin
Select Top 1 @tempStr=Code From [Category] Where Len(Code)=Len(@Code) And Code &gt;@Code Order By Code ASC
End
End
End
-- //已经是最前(最后)
If @tempStr Is Null Or RTrim(LTrim(@tempStr))=''
Begin
return
End

Declare @CodeLen int
Declare @MAXLEN int
Set @CodeLen=Len(@Code)
Set @MAXLEN=200
--//设置目标类,以及目标类的子类为----0001(目标类)或----00010002(子类)为形式
Update [Category] Set Code=@maskStr +Substring(code,@CodeLen +1,@MAXLEN) Where Left(code,@CodeLen)=@tempStr
--//更新当前交换类(包括子类)Code为目标类Code
Update [Category] Set Code=@tempStr +Substring(Code,@CodeLen+1,@MAXLEN) Where Left(code,@CodeLen)=@Code
--//更新目标类(包括子类)Code为当前交换类Code
Update [Category] Set Code=@Code +Substring(code,@CodeLen +1,@MAXLEN) Where Left(code,@CodeLen)=@maskStr

Go

--****************************
--获取指定分类的父分类信息
--*****************************
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category_QueryParent]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[Category_QueryParent]
Go

Create Proc Category_QueryParent
@ClassID int
As
Declare @ClassCode nvarchar(200)
Select @ClassCode=Code From [Category] Where ClassId=@ClassID
Select ClassID,ClassName,Code, DataNum
From [Category]
Where Len(Code) And Code = Left(@ClassCode,Len(Code))
Order By Code

Go

--******************************
-- 获取整个分类目录
--******************************
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category_Query]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[Category_Query]
Go

Create Proc Category_Query
As
Select [ClassID],[ClassName],[Code], [DataNum] From [Category] Order By [Code]
Go

--*****************************
--重置所有分类为根分类
--*****************************
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category_Reset]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[Category_Reset]
Go

Create Proc Category_Reset
As
Declare @code nvarchar(200)
Declare @i int
Set @Code=''
Set @i=1

Declare Category_Cursor CURSOR For
Select CODE From [Category]

Open Category_Cursor
Fetch Next From Category_Cursor
WHILE @@FETCH_STATUS=0
Begin
Set @Code=Replicate(N'0',4) + Cast(@i as nvarchar)
Set @Code=Right(@Code,4)
Update [Category] Set Code= @Code WHERE Current Of Category_Cursor
Set @i=@i+1
Fetch Next From Category_Cursor
End
Close Category_Cursor
DEALLOCATE Category_Cursor

Go

--*********************
-- 获取指定分类的分类名称
--************************
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category_SelectClassName]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[Category_SelectClassName]
Go

Create Proc Category_SelectClassName
@ClassID int
AS
Select [ClassName] From [Category] Where [ClassID]=@ClassID
Go

--********************
-- 获取指定类的子类,并包括自身
--*********************
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category_QueryChildren]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[Category_QueryChildren]
Go

Create Proc Category_QueryChildren
@ClassID int
As
Declare @Code nvarchar(200)
Select @Code=[Code] From [Category] Where [ClassID]=@ClassID
Select [ClassID],[ClassName],[Code], [DataNum]
From [Category] Where Code Like @Code +'%' Order By Code

Go

--**********************
-- 获取一级分类列表
--***********************
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Category_QueryRoot]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[Category_QueryRoot]
Go

Create Proc Category_QueryRoot
AS
Select [ClassID],[ClassName],[Code], [DataNum] From [Category] Where Len(Code)=4 Order By Code

Go

[转载]SQL语句行列转换(附带数据库、表、视图操作)

mikel阅读(941)

[转载]SQL语句行列转换(附带数据库、表、视图操作) – 温度 – 博客园.

本来只是想解决怎么把数据的行和列进行转换的,但最近觉得一些数据库SQL语句的操作,很久没用了,有点陌生。所以也就随笔记录一些简单但很基本的操作。

 

我的数据库是MSSQL2005.

 

第一部分主要的操作包含:数据库的创建、删除,表的增、删、改,表中数据的增、删、改、查,视图的操作。

 

 1 --查询数据库是否存在
 2 if exists ( select * from sysdatabases where [name]='TestDB')
 3 print 'Yes, the DB exists'
 4 else
 5 print 'No, need a new one?'
 6 
 7 --新建一个数据库
 8 create database TestDB on
 9 (
 10     name = 'TestData',
 11     filename = 'G:\DBS\KeyTest.mdf',
 12     size = 3,
 13     filegrowth = 2
 14 )
 15 log on
 16 (
 17     name = 'TestLog',
 18     filename = 'G:\DBS\KeyTest.ldf',
 19     size = 3,
 20     filegrowth = 10
 21 )
 22 
 23 --drop database TestDB
 24 
 25 use TestDB
 26 go
 27 
 28 --新建一个表
 29 create table [Scores]
 30 (
 31     [ID] int identity(1,1) primary key,
 32     [Student] varchar(20) ,
 33     [Subject] varchar(30),
 34     [Score] float
 35 )
 36 
 37 --drop table [Scores]
 38 
 39 --修改表中的一列
 40 alter table Scores alter column [Student] varchar(20) not null
 41 
 42 --新增一列
 43 alter table Scores add Birthday datetime 
 44 
 45 --删除一列
 46 alter table Scores drop column Birthday
 47 
 48 --往表中插入单条数据,方法1:带列名
 49 insert into Scores(Student,Subject,Score)
 50 values('张三','语文','90')
 51 
 52 --往表中插入单条数据,方法2:不带列名,但要求值的类型要和列字段类型对应
 53 insert into Scores
 54 values('张三','英语','95')
 55 
 56 --插入多条数据:用union或者union all
 57 insert into Scores(Student,Subject,Score)
 58 select '李四','语文','89'
 59 union all
 60 select '李四','英语','78'
 61 
 62 --删除表中数据,没有条件时,删除所有
 63 delete from Scores where ID in(7,8)
 64 
 65 --修改表中数据
 66 update Scores 
 67 set Student='王五',Score='94'
 68 where ID=10
 69 
 70 --查看数据
 71 select * from Scores
 72 
 73 --查看表中最大的identity值
 74 select @@identity
 75 
 76 --或者利用dbcc命令查看表中最大的identity值
 77 dbcc checkident('Scores',noreseed)
 78 
 79 --创建视图,全部省略视图的属性列名,由子查询目标列的字段组成
 80 create view StudentView
 81 as
 82 select Student,Subject,Score
 83 from Scores
 84 
 85 --加上with check option,以后对视图的操作(增,改,删,查)都会自动加上where ID>3
 86 /*
 87 create view StudentView
 88 as
 89 select Student,Subject,Score
 90 from Scores
 91 where ID>3
 92 with check option
 93 */
 94 
 95 --创建视图,全部定义属性列名,需要定义列名的情况:
 96 ----某个目标列(子查询)不是单纯的属性列,而是聚集函数或列表达式
 97 ----多表连接时选出了几个同名列
 98 ----需要在视图中为某个列启用新的更合适的名字
 99 create view IS_Student(Student,Subject,MaxScore)
100 as
101 select Student,Subject,Score
102 from Scores
103 where Score=(select max(Score) from Scores)
104 
105 
106 --查询视图,和基本表完全样,只不过如果视图中有with check option,会自动加上那个条件
107 select * 
108 from StudentView
109 
110 --查询自定义列名的视图
111 select * 
112 from IS_Student
113 
114 --对视图的insert/delete/update,和对基本表的操作一样,并且最终都是用RDBMS自动转换为对基本表的更新
115 --并不是所有的视图都是可更新的,因为有些视图的更新不能有意义的转换成对相应基本表的更新
116 
117 --删除视图
118 drop view StudentView

 

第二部分,这次练习的主题。

 

【一】行转列

 

1,查询原始的数据

 

/***这次练习的主题,行转列,列转行***/
select * from Scores

 

 

2,得到姓名,通过group by

 

select Student as '姓名'
from Scores
group by Student
order by Student

 

 

3,再加上max, case……when

 

select Student as '姓名',
max(case Subject when '语文' then Score else 0 end) as '语文' ,--如果这个行是“语文”,就选此行作为列
max(case Subject when '英语' then Score else 0 end ) as '英语'
from Scores
group by Student
order by Student

 

 

 

 

查看其它资料时,看到另外一种方法,用pivot

 

--group by, avg/max, pivot。这里用max和avg,结果都一样,有什么区别吗?有点不明白
--参考网上的资料,用法如下
/*
pivot(
  聚合函数(要转成列值的列名)
  for 要转换的列
  in(目标列名)
  )
*/
select Student as '姓名',
avg(语文) as '语文',
avg(英语) as '英语'
from Scores
pivot(
    avg(Score) for Subject 
    in (语文,英语)
    )as NewScores
group by Student
order by Student asc

 

[转载]Asp.net中服务端控件事件是如何触发的?

mikel阅读(1038)

[转载]Asp.net中服务端控件事件是如何触发的? – @Dylan – 博客园.

初学ASP.NET的时候,曾被各种控件的强大功能所折服。当然,也很容易被搞晕,仅仅记住那些控件的名字都不是一件简单的事儿,何况还有那么多的属性、事件、功能、配置步骤……哈哈。后来越发觉得这些东西确实没啥价值,在企业级开发中很少用到。

写本文的目的并不是要“贬低”或者刻意“排斥”webform中的服务端控件,这里对其优劣也不做过多的讨论。

不过有情提示初学者:其实很多控件的用法都差不多,掌握一种后,再去使用其他的会相对比较容易。例如会用GridView之后,再 去用ListView绝对不是难事。切勿“浮于控件表面”,因为你一旦形成了某种思维方式,以后会很难提高。当然,当你慢慢理解了web的本质和这些控件 的内部机制,回头再去看那些东西,会觉得实在太简单了。这又是一个“先磨刀还是先砍柴”的问题了。

 

我们经常拖动一个服务器控件到aspx页面上(例如一个Button按钮),然后双击,就会看到后台代码中注册了一个click事件。或者你也可以 在控件上“右键”–》“属性”,然后找到“闪电图标”–事件,然后注册相应的事件。这就是传说中的webform的“事件驱动机制”。如果你曾经接触 过VB或者windows开发,或许会倍感温馨。

那这些事件,到底是如何触发的呢?我能否模拟出来呢?为啥我有时候点击了Button按钮却没有触发后台事件?

ASP.NET 中在客户端触发服务端事件分为两种情况:

一. WebControls中的Button 和HtmlControls中的Type为submit的HtmlInputButton

这两种按钮最终到客户端的表现形式为:

< input name="Submit1" id="Submit1" type="submit" value=”Submit”>

这是Form表单的提交按钮,点击以后会作为参数发送到服务端,参数是这样的: 控件的name属性=控件的value值,对应上面的例子就是:Submit1= Submit。 服务端会根据接收到的控件的name属性的这个key来得知是这个按钮被点击了,从而在服务端触发这个按钮的点击事件。

二. HtmlControls 中的 Type为button的HtmlInputButton 和其它所有的控件事件,比如LinkButton点击,TextBox的Change事件等等:

这些事件在客户端产生后会经过一个统一的机制发送到服务端。

1. 首先ASP.NET页框架会使用两个Hidden域来存放表示是哪个控件触发的事件,以及事件的参数:

< !-- 表示触发事件的控件,一般是这个控件的name -->

< input type="hidden" name="__EVENTTARGET" value="" />
< !-- 表示触发事件的参数,一般是当某个控件有两个以上的事件时,用来区别是哪个事件 -->

< input type="hidden" name="__EVENTARGUMENT" value="" />

2. 服务端会生成一个JavaScript的方法来处理所有这些事件的发送,这段代码是:

< script type="javascript">
  < !--

  function __doPostBack(eventTarget, eventArgument) {

  var theform = document.WebForm2;

  theform.__EVENTTARGET.value = eventTarget;

  theform.__EVENTARGUMENT.value = eventArgument;

  theform.submit();

  }

  // -->

  < /script>

3. 每个会引发服务端事件的控件都会在响应的客户端事件中调用上面的代码

浏览器只认html、js、css、图片等静态内容,不管你什么aspx、php、jsp页面最终都会变成html,呈现给用户

比如,HtmlControls 中的 Type为button的HtmlInputButton的点击事件

< !--客户端的点击事件调用__doPostBack,eventTarget 参数为'Button2',表示是name为'Button2’控件触发的事件,eventArgument 为空,表示这个Type为button的HtmlInputButton只有一个客户端触发的服务端事件-->
< input language="javascript" onclick="__doPostBack('Button2','')" name="Button2" id="Button2" type="button" value="Button" />

 

又比如,TextBox控件的Change事件

< !--客户端的onchange事件调用__doPostBack,eventTarget 参数为’TextBox1’,表示是name为’TextBox1’控件触发的事件,而TextBox控件只有一个客户端触发的服务端事件TextChanged,故服务器就会去触发这个TextBox的TextChanged事件-->
< input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" />

4. 客户端触发事件后调用__doPostBack方法,将表示触发的控件源的eventTarget 和事件参数eventArgument分别付给两个隐藏域__EVENTTARGET和__EVENTARGUMENT,然后提交Form,在服务端根据 __EVENTTARGET和__EVENTARGUMENT来判断是哪个控件的什么事件触发了。

 

5.既然了解了__doPostBack这个“著名”的函数,那么我们当然也可以想办法“盗链”了。论坛上经常有很多人问,如何用普通的html控 件(标签)和js调用后台的方法/激发后台事件。呵呵。使用“盗链”不久可以了吗?当然,还有很多实现方式,这里我就不再赘述了。

 

说了这么多,相信读者已经理解了asp.net中所谓的“事件机制”了。还是那句话,web开发,无非是“请求/处理/响应”

[转载]ASP.NET MVC中使用jQuery时的浏览器缓存问题

mikel阅读(898)

[转载]大叔手记(20):ASP.NET MVC中使用jQuery时的浏览器缓存问题 – 汤姆大叔 – 博客园.

介绍

尽管JQuery在浏览器ajax调用的时候对缓存提供了很好的支持,还是有必要了解一下如何高效地使用http协议。

首先要做的事情是在服务器端支持HTTP GET,定义不同的URL输出不同的数据(MVC里对应的就是action)。如果要使用同一个地址获取不同的数据,那就不对了,一个HTTP POST也不行因为POST不能被缓存。许多开发人员使用POST主要有2个原因:明确了数据不能被缓存,或者是避免JSON攻击(JSON返回数组的时 候可以被入侵)。

缓存解释

JQuery全局对象里的ajax方法提供了一些options来支持缓存和Conditional GETs功能。

$.ajax({
    ifModified: [true|false],
    cache: [true|false],
});

ifModified选项定义的是在ajax调用的时候是否支持Conditional GETs功能。JQuery会自动帮我们处理服务器端返回的名为Last-Modified的header值,然后在随后的请求里的header里发送 If-Modified-Since。这需要我们的MVC Controller要实现Conditional GETs功能才能用。Conditional GETs功能在http缓存上下文中用于重新验证缓存中过期的条目。如果jQuery认为一个条目已经过期了,它首先会请求服务器使用 Conditional GETs功能重新验证该条目,如果服务器返回状态码304(Not modified),jQuery会重新使用缓存里的该项目,这样的话,我们可以节约很多流量去下载页面内容。

cache选项基本上是覆盖服务器端返回的http header里的所有关于缓存的设置,如果设置cache选项为false的话,jQuery会在请求的URL后面附件一个时间戳,以便区分之前的URL 地址,这样没错请求的内容都是最新的,也就是说浏览器每次接收的都是新地址,自然返回的都是最新数据。

让我们来看几个场景:

服务器端响应里设置No-Cache

服务器端为王,如果服务器端明确定义了response响应不能被缓存的话,jQuery也无能为力。ajax里的cache选项将被忽略。

JS代码:

$('#nocache').click(function () {
    $.ajax({
        url: '/Home/NoCache',
        ifModified: false,
        cache: true,
        success: function (data, status, xhr) {
            $('#content').html(data.count);
        }
    });
});

C#代码:

public ActionResult NoCache()
{
   // 禁用缓存
   Response.Cache.SetCacheability(HttpCacheability.NoCache);
   return Json(new { count = Count++ }, JsonRequestBehavior.AllowGet);
}

服务器端响应里设置过期时间

服务器端设置过期时间用于缓存数据,该条目在客户端将依据过期时间被缓存。

JS代码:

$('#expires').click(function () {
    $.ajax({
        url: '/Home/Expires',
        ifModified: false,
        cache: true,
        success: function (data, status, xhr) {
            $('#content').html(data.count);
        }
    });
});

C#代码:

public ActionResult Expires()
{
    Response.Cache.SetExpires(DateTime.Now.AddSeconds(5));
    return Json(new { count = Count++ }, JsonRequestBehavior.AllowGet);
}

客户端从来不缓存数据

客户端决定每次都要最新的数据(不能使用缓存),也就是说ajaxi里的cache选项设置为false,不管服务器端如何定义,jQuery每次请求的URL地址都是唯一不同的,目的是每次都获取最新的内容。

JS代码:

$('#expires_nocache').click(function () {
    $.ajax({
        url: '/Home/Expires',
        ifModified: false,
        cache: false, // 这里是关键
        success: function (data, status, xhr) {
            $('#content').html(data.count);
        }
    });
});

C#代码:

public ActionResult Expires()
{
    // 不管服务器端怎么设置都没用
    Response.Cache.SetExpires(DateTime.Now.AddSeconds(5));
    return Json(new { count = Count++ }, JsonRequestBehavior.AllowGet);
}

服务器端和客户端使用Conditional Gets功能验证缓存数据

客户端将条目放在缓存里,在过期之后重新验证。服务器端必须实现Conditional GET功能(使用ETags或者last modified的header)。

JS代码:

$('#expires_conditional').click(function () {
    $.ajax({
        url: '/Home/ExpiresWithConditional',
        ifModified: true, // 这里是关键
        cache: true,
        success: function (data, status, xhr) {
            $('#content').html(data.count);
        }
    });
});

C#代码:

public ActionResult ExpiresWithConditional()
{
    if (Request.Headers["If-Modified-Since"] != null && Count % 2 == 0)
    {
        return new HttpStatusCodeResult((int)HttpStatusCode.NotModified);
    }

    Response.Cache.SetExpires(DateTime.Now.AddSeconds(5));
    Response.Cache.SetLastModified(DateTime.Now);

    return Json(new { count = Count++ }, JsonRequestBehavior.AllowGet);
}

上述MVC action中的代码只是一个例子(非真实代码),在真实的实现中,服务器端应该能够知道数据自从上次响应以后是否被修改过。

总结

详细通过这4个场景,大家应该了解了ajax请求的缓存技术了吧,我就不做总结了。

英文原文来自:http://weblogs.ASP.NET/cibrax/archive/2012/02/10/hacking-the-browser-cache-with-jquery-and-asp-net-mvc.aspx

同步与推荐

本文已同步至目录索引:《大叔手记全集》

大叔手记:旨在记录日常工作中的各种小技巧与资料(包括但不限于技术),如对你有用,请推荐一把,给大叔写作的动力。

[转载]简单代码生成器原理剖析

mikel阅读(884)

[转载]简单代码生成器原理剖析 – 36氪 – 博客园.

上篇文章(深入浅出三层架构)分析了简单三层架构的实现。包括Model,DAL(数据访问层),BLL(业务逻辑层)的实现。

实际开发中,由于重复代码的操作,会花费大量时间,如果以代码生成器来自动生成三层架构代码,即节省精力,又可以节省大量的时间来做其他业务逻辑的代码,提高开发效率。

常用的代码生成器有:动软,CodeSmith 等。

简单代码生成器的基本功能描述:

一键生成Model,DAL,BLL,包括对应数据库中表的Model的自动生成,包括生成属性、添加、修改、删除、查询。

界面展示:

生成器开发技术要点:

  1. 查询系统视图:INFORMATION_SCHEMA.TABLES、 INFORMATION_SCHEMA.COLUMNS  可以获得数据库中表、列的相关信息。
  2. 字符串的拼接:StringBuilder的使用,其AppendLine()会自动换行。
  3. 将字符串写入文本文件:File.WriteAllText()
  4. 为了降低开发难度,先假设条件多一些,如表的主键都为Id,且自动增长,之后再逐步完善

关键代码:

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.IO;

namespace CodeGenerator
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        /// <summary>
 /// 执行ExecuteDataTable(),得到DataTable
 /// </summary>
 /// <param name="cmdText"></param>
 /// <param name="parameters"></param>
 /// <returns></returns>
        public  DataTable ExecuteDataTable(string cmdText,
            params SqlParameter[] parameters)
        {
            using (SqlConnection conn=new SqlConnection(txtConnStr.Text))
            {
                conn.Open();
                using(SqlCommand cmd=conn.CreateCommand())
                {
                    cmd.CommandText = cmdText;
                    cmd.Parameters.AddRange(parameters);
                    using (SqlDataAdapter adapter=new SqlDataAdapter (cmd))
                    {
                        DataTable dt = new DataTable();
                        adapter.Fill(dt);
                        return dt;
                    }
                }
            }

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            txtConnStr.Text = @"Data Source=EYES\SQLEXPRESS;Initial Catalog=SanCeng;Integrated Security=True";
        }

        private void btnConnStr_Click(object sender, EventArgs e)
        {

            //清空
            clbTables.Items.Clear();
            //查询系统试图
            string sql = "select * from INFORMATION_SCHEMA.TABLES";
            DataTable dt = ExecuteDataTable(sql);
            //根据系统视图取得TABLE_NAME
            foreach (DataRow row in dt.Rows)
            {
                string tablename = Convert.ToString(row["TABLE_NAME"]);
                clbTables.Items.Add(tablename);
            }

        }

        private void btnGo_Click(object sender, EventArgs e)
        {
            //连接字符串
 //方法AppendLine()追加字符串且自动执行换行

            foreach (string tableName in clbTables.CheckedItems)
            {
                string sql = "select * from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME=@TABLE_NAME ";
                DataTable dt = ExecuteDataTable(sql,new SqlParameter("TABLE_NAME",tableName));

                #region  生成Model
                CreatModel(tableName, dt);
                #endregion

                #region 生成DAL
          

                CreatDAL(tableName, dt);
                #endregion
                #region 生成BLL
                CreatBLL(tableName, dt);
                #endregion
            }
        }

        private static void CreatDAL(string tableName, DataTable dt)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using System.Linq;");
            sb.AppendLine("using System.Text;");
            sb.AppendLine("using 三层架构Demo.Model;");
            sb.AppendLine("using System.Data.SqlClient;");
            sb.AppendLine("using System.Data;");
            sb.AppendLine("namespace 三层架构Demo.DAL");
            sb.AppendLine("{");
            sb.AppendLine("class " + tableName + "DAL");
            sb.AppendLine("{");
            //去掉Id
            sb.AppendLine(" public int Addnew(" + tableName + " model)");
            sb.AppendLine("{");
            List<String> cols = new List<string>();
            List<String> parameters = new List<string>();
            foreach (DataRow row in dt.Rows)
            {
                string col = Convert.ToString(row["COLUMN_NAME"]);
                string parameter = "";
                if (col.ToLower()!="id")
                {
                        parameter= "@" + Convert.ToString(row["COLUMN_NAME"]);
                        cols.Add(col);
                        parameters.Add(parameter);
                }
                //parameters.Add(parameter)放外面加上一个NULL,所以会多出一个逗号
 // parameters.Add(parameter);
                
            }

            sb.AppendLine("string sql = \"insert into " + tableName + "(" + String.Join(",", cols) + ") output inserted.Id values(" + String.Join(",", parameters) + ")\";");
            sb.AppendLine("object obj= SQLHelper.ExecuteScalar(sql");

            foreach (DataRow row in dt.Rows)
            {
                string col = Convert.ToString(row["COLUMN_NAME"]);
                if (col.ToLower() != "id")
                {
                    sb.AppendLine(",new SqlParameter(\"" + col + "\",model." + col + ")");
                }
                
            }
            sb.AppendLine(");");
            sb.AppendLine("return Convert.ToInt32(obj);");
            sb.AppendLine("}");
            //Delete方法
          
            sb.AppendLine(" public int Delete(int id)");
            sb.AppendLine("{");
            sb.AppendLine(" string sql = \"delete from " + tableName + " where Id=@Id\";");
            sb.AppendLine("return SQLHelper.ExecuteNonQuery(sql,new SqlParameter(\"Id\",id));");
            sb.AppendLine("}");

            //Update方法
            sb.AppendLine("public int Update("+tableName+" model)");
            sb.AppendLine("{");
            string[] uParams1=(from col in cols select col+"=@"+col).ToArray();
         
            sb.AppendLine(" string sql = \"update "+tableName+" set "+String.Join(",",uParams1)+" where Id=@Id\";");

            string[] uParams2 = (from col in cols select  "new SqlParameter(\"" + col + "\",model." + col + ")").ToArray();
            sb.AppendLine(" return SQLHelper.ExecuteNonQuery(sql, " + String.Join(",", uParams2) + " ,new SqlParameter(\"Id\",model.Id));");
            sb.AppendLine("}");
          
            //GetId方法
            sb.AppendLine(" public "+tableName+" Get(int id)");
            sb.AppendLine("{");
            sb.AppendLine("string sql=\"select * from "+tableName+" where Id=@Id\";");
            sb.AppendLine(" DataTable dt=SQLHelper.ExecuteDataTable(sql,new SqlParameter(\"Id\",id));");
            sb.AppendLine("if (dt.Rows.Count<=0)");
            sb.AppendLine("{");
            sb.AppendLine(" return null;");
            sb.AppendLine("}");
            sb.AppendLine(" else if (dt.Rows.Count==1)");
            sb.AppendLine("{");
            sb.AppendLine(""+tableName+" model1 = new "+tableName+"();");
            foreach (DataRow row in dt.Rows)
            {
                string col = Convert.ToString(row["COLUMN_NAME"]);
                string dataType = Convert.ToString(row["data_TYPe"]);
                sb.AppendLine("model1." + col + " = Convert." + Get(GetType(dataType).ToString()) + "(dt.Rows[0][\"" + col + "\"]);");
                
            }
            sb.AppendLine("return model1;");
            sb.AppendLine("}");
            sb.AppendLine("else");
            sb.AppendLine("{");
            sb.AppendLine(" throw new Exception(\"数据库中有两条及以上重复数据\");");
            sb.AppendLine("}");
            sb.AppendLine("}");

            //IEnumerable()方法
            sb.AppendLine(" public IEnumerable<"+tableName+"> GetAll()");
            sb.AppendLine("{");
            sb.AppendLine(" string sql = \"select * from "+tableName+"\";");
            sb.AppendLine("DataTable dt = SQLHelper.ExecuteDataTable(sql);");
            sb.AppendLine(" List<"+tableName+"> list = new List<"+tableName+">();");
            sb.AppendLine(" foreach (DataRow row in dt.Rows)");
            sb.AppendLine("{");
            sb.AppendLine("" + tableName + " model = new " + tableName + "();");
            foreach (DataRow row in dt.Rows)
            {
                string col = Convert.ToString(row["COLUMN_NAME"]);
                string dataType = Convert.ToString(row["data_TYPE"]);
                sb.AppendLine("model." + col + " = Convert." + Get(GetType(dataType).ToString()) + "(row[\"" + col + "\"]);");
                
            }
            sb.AppendLine(" list.Add(model);");
            sb.AppendLine("}");
            sb.AppendLine("return list;");
            sb.AppendLine("}");
            sb.AppendLine("}");
            sb.AppendLine("}");
            File.WriteAllText(@"d:\"+tableName+"DAL.cs",sb.ToString());

            
        }
        /// <summary>
 /// 数据库类型转换为C#类型
 /// </summary>
 /// <param name="dataType"></param>
 /// <returns></returns>
        private static Type GetType(string dataType)
        {
            switch (dataType.ToLower())
            {
                case "nvarchar":
                case "varchar":
                case "nchar":
                case "char":
                    return typeof(string);
                case "int" :
                    return typeof(int);
                case "bigint":
                    return typeof(long);
                case "bit":
                    return typeof(bool);
                case "datetime":
                    return typeof(DateTime);
                default:
                    return typeof(object);
            }
        
        }

        private static string Get(string dataType)
        {
         
            switch (dataType.ToLower())
            {
                case "system.string":
                    return "ToString";
                case "system.int32":
                    return "ToInt32";
                case "system.int64":
                    return "ToInt64";
                case "system.datetime":
                    return "ToDateTime";
                case "system.boolean":
                    return "ToBoolean";
             
                default:
                    throw new Exception("找不到匹配的数据类型");
                    
            }
        }
        private static void CreatModel(string tableName, DataTable dt)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using System.Linq;");
            sb.AppendLine("using System.Text;");
            sb.AppendLine("namespace 三层架构Demo.Model");
            sb.AppendLine("{");
            sb.AppendLine("");
            sb.AppendLine("class " + tableName);
            sb.AppendLine("{");

            foreach (DataRow row in dt.Rows)
            {
                string dataType = Convert.ToString(row["DATA_TYPE"]);
                
                string columnName = Convert.ToString(row["COLUMN_NAME"]);

                sb.AppendLine("public " + GetType(dataType) + " " + columnName + " { get;set;}");
            }
            sb.AppendLine("}");
            sb.AppendLine("}");
            File.WriteAllText(@"d:\" + tableName + ".cs", sb.ToString());
            //MessageBox.Show(sb.ToString());
            
        }

        private static void CreatBLL(string tableName, DataTable dt)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using System.Linq;");
            sb.AppendLine("using System.Text;");
            sb.AppendLine("using 三层架构Demo.Model;");
            sb.AppendLine("using 三层架构Demo.DAL;");
            sb.AppendLine("using System.Data.SqlClient;");
            sb.AppendLine("using System.Data;");
            sb.AppendLine("namespace 三层架构Demo.BLL");
            sb.AppendLine("{");
            sb.AppendLine("class " + tableName+"BLL");
            sb.AppendLine("{");
            sb.AppendLine("public int Addnew("+tableName+" model)");
            sb.AppendLine("{");
            sb.AppendLine(" return new "+tableName+"DAL().Addnew(model);");
            sb.AppendLine("}");
            sb.AppendLine(" public int Delete(int id)");
            sb.AppendLine("{");
            sb.AppendLine(" return new "+tableName+"DAL().Delete(id);");
            sb.AppendLine("}");
            sb.AppendLine(" public int Update("+tableName+" model)");
            sb.AppendLine("{");
            sb.AppendLine(" return new " + tableName + "DAL().Update(model);");
            sb.AppendLine("}");
            sb.AppendLine(" public "+tableName+" Get(int id)");
            sb.AppendLine("{");
            sb.AppendLine(" return new "+tableName+"DAL().Get(id);");
            sb.AppendLine("}");
            sb.AppendLine(" public IEnumerable<"+tableName+"> GetAll()");
            sb.AppendLine("{");
            sb.AppendLine(" return new "+tableName+"DAL().GetAll();");
            sb.AppendLine("}");
            sb.AppendLine("}");
            sb.AppendLine("}");
            File.WriteAllText(@"d:\" + tableName + "BLL.cs", sb.ToString());
        }
    }
}

总结:
忽略了很多限制因素,所以代码生成器功能不是很完善。随着要考虑的条件增多,代码生成器越加复杂。但万变不离其中,只要有耐心,继续AppendLine()添加新语句,相信功能会愈加完善。“
工欲善其事必先利其器“,程序员不仅会用代码生成器,而且知道其原理才是优秀的程序员。切勿”知其然而不知其所以然“。

[转载]Android自动开关机实现

mikel阅读(963)

[转载]Android自动开关机实现 – Melanie Deng – 博客园.

关于Android自动关机,网上有很多应用程序和例子。 相对于自动开机来说,自动关机可以在应用层通过设置alarm来实现。而自动开机,网上的介绍就比较少了,因为它需要底层rtc时钟的支持。前段时间根据客户需求实现了自动开关机。在这里分享一下。

1. 简介

我的实现是在设置程序里面增加一个接口,让用户设置自动开关机,这个自动开关机的设置可以参照闹钟的设置。关于自动关机,考虑到关机的时候,用户可能正有一些重要的操作,那么应该给用户一个机会去取消当前的关机。

1)一个BroadcastReceiver, 接收如下信息:

  a) 自定义的ACTION_REQUEST_POWER_OFF:设置auto power off时,通过AlarmManager设置的一个RTC_WAKEUP时钟。当到设置的关机时间时,之前设置到AlarmManager的这个 action会被广播。我们实现的这个BroadcastReceiver接收到这个消息后,就要开始power off流程

  b) 自定义的ACTION_REQUEST_POWER_ON:设置auto power on时,通过AlarmManager设置的一个RTC_WAKEUP时钟。我们知道power on的应该设置一个rtc的alarm,那么这个RTC_WAKEUP的alarm是做 什么的呢?其实当用户设置自动关机的时候,我设置了2个时钟,一个是RTC时钟,用于关机状态下开机;还有一个就是这个RTC_WAKEUP时钟。之所以 设置这个时钟,其实是这样的,比如说你设置了周一到周五每天7点半自动开机,而周四早上你7点就打开了手机,这样到7点半的时候,之前设置的时钟就过期 了,如果不重新设置的话,周五早上是不会自动开机的。所以这个时候,之前设置的RTC_WAKEUP就接收到了这样的信息,在重新设置下次自动开机的时 钟。

  c) BOOT_COMPLETE和TIMEZONE changed, Time set等时间相关的action:当系统开机完成或时间、时区发生改变时,都需要重新设置alarm。

2)一个处理power off 的Service,当BroadcastReceiver接收到ACTION_REQUEST_POWER_OFF,我们给用户一个机会去取消当前的自动关机。这个Service的作用就是启动一个无背景的页面,给用户提示。同时播放之前用户设置的提示音或振动。

3)一个Activity:显示一个dialog提示用户要自动关机,并用一个计时器倒计时。当用户确认关机,或者计时器到时间的时候,就关机。否则取消当前关机,并重设下次自动关机alarm。


2. 自动关机的实现。自动关机的实现比较简单,这里主要说一下怎么设置alarm,和实现关机:

1) 设置自动关机的alarm:

    AlarmManager am = (AlarmManager) context
                .getSystemService(Context.ALARM_SERVICE);

        Intent intent = new Intent(
                "com.android.settings.action.REQUEST_POWER_OFF");

        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
                intent, PendingIntent.FLAG_CANCEL_CURRENT);
        am = (AlarmManager) context
                .getSystemService(Context.ALARM_SERVICE);
        am.set(AlarmManager.RTC_WAKEUP, time, pendingIntent);

2)自动关机掉的是./frameworks/base/services/java/com/Android/server/ShutdownActivity.java:

        Intent newIntent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
        newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(newIntent);

Intent.ACTION_REQUEST_SHUTDOWN是Intent里面一个隐藏的action。

3. 自动开机的实现。一直在做上层应用和framework,对于底层不是很熟悉。正好有同事之前做过关机闹铃,所以把他之前的实现稍加改动就可以了。在系统 power off的状态下自动开机,我们需要设置一个rtc时钟,当用户设置自动开机时,由AlarmManagerService将时钟设置下去。这学要底层的支 持。这里的实现是定义一个我们自己的rtc alarm type:

1) 首先要在头文件里面定义:

  a) kernel/include/linux/android_alarm.h

#define ANDROID_ALARM_GET_TIME(type)        ALARM_IOW(4, type, struct timespec)
#define ANDROID_ALARM_SET_RTC               _IOW('a', 5, struct timespec)

/* we define ANDROID_RTC_ALARM_SET for auto power off */
#define ANDROID_RTC_ALARM_SET               _IOW('a', 7, int)

#define ANDROID_ALARM_BASE_CMD(cmd)         (cmd & ~(_IOC(0, 0, 0xf0, 0)))

  b) bionic/libc/kernel/common/linux/android_alarm.h

#define ANDROID_RTC_ALARM_SET _IOW('a', 7, int)

2) 定义完成之后,还需要实现:在kernel/drivers/rtc/alarm-dev.c文件的alarm_ioctl方法里面,增加一个case,实现设置alarm

    case ANDROID_RTC_ALARM_SET:
        {
            unsigned int rtc_alarm_time;
            struct rtc_time rtc_now;
            if (copy_from_user(&rtc_alarm_time, (void __user *)arg,
                sizeof(rtc_alarm_time))) {
                rv = -EFAULT;
                goto err1;
            }
            if (pmic_rtc_get_time(&rtc_now) < 0) {
                rtc_now.sec = 0;
                if (pmic_rtc_start(&rtc_now) < 0) {
                    printk("get and set rtc info failed\n");
                    break;
                }
            }
            pmic_rtc_disable_alarm(PM_RTC_ALARM_1);
            rtc_now.sec += rtc_alarm_time;
            pmic_rtc_enable_alarm(PM_RTC_ALARM_1, &rtc_now);
            break;
        }

当然不要忘记增加一个include:

#include <mach/pmic.h>

3)在frameworks/base/services/jni/com_android_server_AlarmManagerService.cpp里面增加一个方法去设置时钟:

static void android_server_AlarmManagerService_updateRtcAlarm(JNIEnv* env, jobject obj, jint fd, jint seconds)
{
#if HAVE_ANDROID_OS
    int result = ioctl(fd, ANDROID_RTC_ALARM_SET, &seconds);
    LOGE("set rtc alarm to %d later: %s\n", seconds, strerror(errno));
    if (result < 0)
    {
        LOGE("Unable to set rtc alarm to %d later: %s\n", seconds, strerror(errno));
    }
#endif
}

还有就是不要忘记定义一下接口:

{"updateRtcAlarm", "(II)V", (void*)android_server_AlarmManagerService_updateRtcAlarm},

4) 在frameworks/base/services/java/com/android/server/AlarmManagerService.java里面定义native的设置alarm的方法,然后调用就可以实现将自动关机的alarm设置下去了:

定义:private native void updateRtcAlarm(int fd, int seconds);

调用:

    public void setRepeating(int type, long triggerAtTime, long interval, 
            PendingIntent operation) {
        if (operation == null) {
            Slog.w(TAG, "set/setRepeating ignored because there is no intent");
            return;
        }
        synchronized (mLock) {
            Alarm alarm = new Alarm();
            alarm.type = type;
            alarm.when = triggerAtTime;
            alarm.repeatInterval = interval;
            alarm.operation = operation;

            // Remove this alarm if already scheduled.
            removeLocked(operation);

            if (localLOGV) Slog.v(TAG, "set: " + alarm);

            int index = addAlarmLocked(alarm);
            if (index == 0) {
                setLocked(alarm);
            }

            // Start to setup auto power on alarm
            if ((alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP) && 
                                alarm.operation.getTargetPackage().equals("com.android.settings")) {
                updateRtcAlarm(mDescriptor, (int)((alarm.when - System.currentTimeMillis()) / 1000));
            }
            // End to setup auto power on alarm
        }
    }

5)在应用层设置自动开机

        AlarmManager am = (AlarmManager) context
                .getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(
                "com.android.settings.action.REQUEST_POWER_ON");
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
                intent, PendingIntent.FLAG_CANCEL_CURRENT);
        am = (AlarmManager) context
                .getSystemService(Context.ALARM_SERVICE);
        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, pendingIntent);


4. 总结

1) 自动开机原理比较简单,但是需要底层的支持,所以对于做应用或者framework层的技术人员来说,实现起来稍微比较麻烦。
2) 在设置自动开关机的时候,需要考虑的情况很多,比如是否设置时间/时区的改变,手机当前是开机还是关机状态等。

[转载]开源一个小程序 HttpDownloader

mikel阅读(808)

[转载]开源一个小程序 HttpDownloader – 张志敏 – 博客园.

最近对在线版 CutTheRope 非常感兴趣, 总想下载下来好好研究一下, 于是便写了一个 Http 下载的小工具, 根据 Url 列表进行多线程下载, 然后根据 Url 的相对路径进行保存, 使用起来感觉不错, 现在分享给大家。

HttpDownloader 使用起来非常简单, 只要简单的输入 Url 列表, 选择本地保存目录, 然后就可以开始批量下载了, 程序运行截图如下:

程序运行截图

点击下载按钮之后开始下载, 等进度条走到尽头就下载完了, 最后下载好的文件如下图所示:

image

怎么采集到 Url 呢? 这样的工具有很多, 我用的是 HttpWatch , 只要将 HttpWatch 记录到的 Url 保存到 csv 文件, 用 Excel 打开, 稍微处理一下就得到 url 列表了。

最后说明一下, 这个小程序需要的运行时是 WPF 4.0 , 相信大家都已经安装了 VS2010 了吧, 这个应该不是问题啦。

程序下载地址是 http://files.cnblogs.com/beginor/HttpDownloader.zip

源码下载地址是 https://github.com/beginor/practice/tree/master/src/wpf

整理好的 CutTheRope 下载 http://115.com/file/c2m60rk3

[转载]SQL Server 2000数据库移植到SQL Server 2008R2数据库服务器中碰到的”3145错误”及解决办法

mikel阅读(892)

[转载]SQL Server 2000数据库移植到SQL Server 2008R2数据库服务器中碰到的”3145错误”及解决办法 – Ritchie(乞戈) – 博客园.

辛苦忙碌了一个星期终于安装配置好了TFS服务器,给每个团队成员分配了账户和邮箱。不过,老机器中的部分数据需要备份到新机器中,其中在移植一个使用DVBBS架设的论坛的时候,出了点问题,记录如下,以备查找,也希望能帮助到其他人。

由 于论坛已经运行了一段时间,包括公司注册用户、相关讨论共享等重要资料,需要将其移植到新安装TFS服务器的机器上,并对外公开访问权限。旧的机器上安装 的是SQL Server 2000,在将论坛数据库备份之后,在SQL Server 2008R2的数据库上还原的过程中,出现了如下的3145错误:

备份集中的数据库备份与现有的 ‘xxx’ 数据库不同。

一直以为SQL Server数据库是向前后兼容的,怎么可能存在这样的问题呢?不过检查一番之后,确定两台数据库服务器都没有问题,且论坛的备份在原数据库服务器上可以正常还原。没办法,改之,把在SQL Server 2008R2上新建的数据库更改为 SQL Server 2000格式的,再尝试还原,仍然是同样的错误。在网上找了下,说到的办法,尝试了不少,其中有一篇是“备份集中的数据库备份与现有的数据库不同 SQL 2005 ERROR:3145 解决办法 ”,提供了如下命令方法:

1.新建一个与现有数据库重名的数据库

2.在查询窗口运行如下语句:

use master
restore database AdventureWorks from disk = ‘E:\databasebackup\AdventureWorks.bak’
with replace, MOVE N’AdventureWorks_Data’ TO N’C:\Program Files\Microsoft SQL Server\MSSQL.5\MSSQL\Data\AdventureWorks.mdf’, MOVE N’AdventureWorks_Log’ TO N’C:\Program Files\Microsoft SQL Server\MSSQL.5\MSSQL\Data\AdventureWorks_log.ldf’

从语句上来看,和在图形界面的操作是一样的,但是结果却是语句运行成功,图形界面怎么过不去。

在其他网页上也看到了类似的方法,也有不少成功解决这个错误的,不过,在SQL Server 2008R2服务器上,还是无法正确备份,也没有找到有其它人在SQL Server 2008R2上,用其它方法解决还原备份的问题。

不过,将论坛数据库从旧的SQL Server 2000上分离出来,再附加上SQL Server 2008R2上,成功,数据完整的恢复了!

不清楚为什么在SQL Server 2005上可以,在SQL Server 2008R2却不能恢复备份。不过,好在使用分离和附加的方法,成功将论坛迁移。在此记下,做为参考吧。

[转载]SQLite学习手册(索引和数据分析/清理)

mikel阅读(916)

[转载]SQLite学习手册(索引和数据分析/清理) – Stephen_Liu – 博客园.

一、创建索引:

SQLite中,创建索引的SQL语法和其他大多数关系型数据库基本相同,因为这里也仅仅是给出示例用法:
    sqlite> CREATE TABLE testtable (first_col integer,second_col integer);
    –创建最简单的索引,该索引基于某个表的一个字段。
    sqlite> CREATE INDEX testtable_idx ON testtable(first_col);
    –创建联合索引,该索引基于某个表的多个字段,同时可以指定每个字段的排序规则(升序/降序)。
    sqlite> CREATE INDEX testtable_idx2 ON testtable(first_col ASC,second_col DESC);
    –创建唯一性索引,该索引规则和数据表的唯一性约束的规则相同,即NULL和任何值都不同,包括NULL本身。
    sqlite> CREATE UNIQUE INDEX testtable_idx3 ON testtable(second_col DESC);
    sqlite> .indices testtable
    testtable_idx
    testtable_idx2    
    testtable_idx3
从.indices命令的输出可以看出,三个索引均已成功创建。

二、删除索引:

索引的删除和视图的删除非常相似,含义也是如此,因此这里也只是给出示例:
    sqlite> DROP INDEX testtable_idx;
    –如果删除不存在的索引将会导致操作失败,如果在不确定的情况下又不希望错误被抛出,可以使用”IF EXISTS”从句
    sqlite> DROP INDEX testtable_idx;
    Error: no such index: testtable_idx
    sqlite> DROP INDEX IF EXISTS testtable_idx;

三、重建索引:

重建索引用于删除已经存在的索引,同时基于其原有的规则重建该索引。这里需要说明的是,如果在REINDEX语句后面没有给出数据库名,那么当前连接下 所有Attached数据库中所有索引都会被重建。如果指定了数据库名和表名,那么该表中的所有索引都会被重建,如果只是指定索引名,那么当前数据库的指 定索引被重建。
    –当前连接attached所有数据库中的索引都被重建。
    sqlite> REINDEX;
    –重建当前主数据库中testtable表的所有索引。
    sqlite> REINDEX testtable;
    –重建当前主数据库中名称为testtable_idx2的索引。
    sqlite> REINDEX testtable_idx2;

四、数据分析:

和PostgreSQL非常相似,SQLite中的ANALYZE命令也同样用于分析数据表和索引中的数据,并将统计结果存放于SQLite的内部系统表中,以便于查询优化器可以根据分析后的统计数据选择最优的查询执行路径,从而提高整个查询的效率。见如下示例:
    –如果在ANALYZE命令之后没有指定任何参数,则分析当前连接中所有Attached数据库中的表和索引。
    sqlite> ANALYZE;
    –如果指定数据库作为ANALYZE的参数,那么该数据库下的所有表和索引都将被分析并生成统计数据。
    sqlite> ANALYZE main;
    –如果指定了数据库中的某个表或索引为ANALYZE的参数,那么该表和其所有关联的索引都将被分析。
    sqlite> ANALYZE main.testtable;
    sqlite> ANALYZE main.testtable_idx2;

五、数据清理:

和PostgreSQL中的VACUUM命令相比,他们的功能以及实现方式非常相似,不同的是PostgreSQL提供了更细的粒度,而SQLite只 能将该命令作用于数据库,无法再精确到数据库中指定的数据表或者索引,然而这一点恰恰是PostgreSQL可以做到的。
当某个数据库中的一个或多个数据表存在大量的插入、更新和删除等操作时,将会有大量的磁盘空间被已删除的数据所占用,在没有执行VACUUM命令之 前,SQLite并没有将它们归还于操作系统。由于该类数据表中的数据存储非常分散,因此在查询时,无法得到更好的批量IO读取效果,从而影响了查询效 率。
在SQLite中,仅支持清理当前连接中的主数据库,而不能清理其它Attached数据库。VACUUM命令在完成数据清理时采用了和 PostgreSQL相同的策略,即创建一个和当前数据库文件相同大小的新数据库文件,之后再将该数据库文件中的数据有组织的导入到新文件中,其中已经删 除的数据块将不会被导入,在完成导入后,收缩新数据库文件的尺寸到适当的大小。该命令的执行非常简单,如:
sqlite> VACUUM;