[SQL]SqlServer:CTE函数处理递归(WITH语法)

mikel阅读(1501)

     我们在做分类处理的时候,总会遇到递归的处理,比如说地区就是一个例子,中国–北京–西城区,我们可以把这样的信息存储在一个数据表中,用 ParentID区分根节点和叶子节点。假如我们要做导航,得到了”西城区”,但是还要得到他的父级,或夫父级,一种方式是用程序来处理,也是很简单,另 一种方式就是用数据库的功能。既然数据库能完成这件事,何必在用程序呢?

     在SQLServer2005以前的版本中,也能处理这种情况,不过当时用的是存储过程,代码也比较多,表设计的时候,还的加一个表示“深度”的字段,当时我也写过相关的文章,参见:

存储过程实现无限级分类(1)

存储过程实现无限级分类(2)

存储过程实现无限级分类(3)

    但是,在学习SQLServer2005新加的特性的时候,注意到WITH语句能实现这样的功能,并且代码简单,简练,最重要的是好理解。

下面是一张递归的表结构图:

Recursive_1

里边的parentID记录的就是他们之间的关系;

比如我们要查询id = 10008,并且parentID 为10008的数据怎么办呢?如:

Recursive_3

呵呵,不留悬念了,直接上代码吧!其实挺容易理解的:

WITH CategoryInfo AS(
Select id,text,parentid FROM Recursive Where id = 10008
UNION ALL
Select a.id,a.text,a.parentid FROM Recursive AS a,CategoryInfo AS b Where a.parentid = b.id
)
Select * FROM CategoryInfo

 

看看是不是简单,简练,好理解!

赶快试试吧!

这是从上向下查,如果从下向上查,怎么查呢,自己举一反三吧!

存储过程实现无限级分类(1)

    在做考题的的时候,涉及到分类,虽然不是无限级的,但是,为了以后扩展用,想做成无限级,在网上找找了,一个用存储过程作的,虽然添加,编辑,移动,用的 是存储过程,可是在读出来的时候只用了一条Select 语句,感觉挺爽的,下面我把存储过程列出来,我只用到了添加,编辑,没有用到移动。

1,表结构

===================================================
表结构:
表名:Tb_Column
表结构(所有字段非空):
Column_ID     
int 主键(注:非标识)
Column_Name   nvarchar(
50)分类名称
Parent_ID     
int 父分类ID(默认值0)
Column_Path   nvarchar(
1000) 分类路径
Column_Depth  int分类深度(默认值0)
Column_Order  int排序(默认值0)
Column_Intro  nvarchar(
1000)分类说明
================================================

2.添加的存储过程

Create PROCEDURE sp_Column_Insert
(
@Parent_ID 
int,
@Column_Name nvarchar(
50),
@Column_Intro nvarchar(
1000)
)
AS
Declare @Err As 
int
Set @Err
=0
Begin Tran
通过现有记录获取栏目ID
Declare @Column_ID As 
int
Declare @Column_Depth As 
int
Select @Column_ID 
= Max(Column_ID) From Tb_Column
IF @Column_ID Is Not Null
Set @Column_ID 
= @Column_ID+1
Else
Set @Column_ID 
= 1
判断是否是顶级栏目,设置其Column_Path和Column_Order
Declare @Column_Path As nvarchar(
1000)
Declare @Column_Order As 
int
IF @Parent_ID 
= 0
Begin
Set @Column_Path 
=Ltrim(Str(@Column_ID))
Select @Column_Order 
= Max(Column_Order) From Tb_Column
IF @Column_Order Is Not Null
Set @Column_Order 
= @Column_Order + 1
Else 
如果没有查询到记录,说明这是第一条记录
Set @Column_Order 
= 1
深度
Set @Column_Depth 
= 1
End
Else
Begin
获取父节点的路径和深度
Select @Column_Path 
= Column_Path ,@Column_Depth = Column_Depth From Tb_Column Where 
Column_ID
=@Parent_ID
IF @Column_Path Is Null
Begin
Set @Err 
= 1
Goto theEnd
End
获取同父节点下的最大序号
Select @Column_Order 
= Max(Column_Order) From Tb_PicColumn Where Column_Path like 
''+@Column_Path+'|%'  Or Column_ID = @Parent_ID
IF @Column_Order Is Not Null 
如果序号存在,那么将该序号后的所有序号都加1
Begin 
更新当前要插入节点后所有节点的序号
Update Tb_Column Set Column_Order 
= Column_Order +1 Where Column_Order 
>@Column_Order
同父节点下的最大序号加上1,构成自己的序号
Set @Column_Order 
= @Column_Order + 1
End 
Else
Begin
Set @Err
=1
Goto theEnd
End
父节点的路径加上自己的ID号,构成自己的路径
Set @Column_Path 
= @Column_Path + '|' + Ltrim(Str(@Column_ID))
深度
Set @Column_Depth 
= @Column_Depth+1
End
Insert Into Tb_Column(Column_Name,Parent_ID,Column_Path,Column_Depth,Column_Order,Column_Intro) 
Values(@Column_Name,@Parent_ID,@Column_Path,@Column_Depth,@Column_Order,@Column_Intro)
IF @@Error
<>0 
Begin
Set @Err
=1
Goto theEnd
End
更新当前记录之后的记录的ORDER
Update Tb_Column Set Column_Order = Column_Order+1 Where Column_Order  > @Column_Order 
theEnd:
IF @Err
=0
Begin
Commit Tran
Return @Column_ID
End
Else
Begin
    Rollback Tran
Return 
0
End
GO

存储过程实现无限级分类(2)
di。删除的存储过程

Create PROCEDURE sp_Column_Delete
(
@Column_ID 
int
)
AS
Declare @Err As 
int
Set @Err 
= 0
Begin Tran
首先查询该节点下是否有子节点
Select Column_ID From Tb_Column Where Parent_ID 
= @Column_ID
IF @@RowCount
<>0
    Begin
    Set @Err 
= 1
    Goto theEnd
    End
获取该节点的Column_Order,为了删除后整理其他记录的顺序
Declare @Column_Order As 
int
Select @Column_Order 
= Column_Order From Tb_Column Where Column_ID = @Column_ID
IF @Column_Order Is NUll
    Begin
      Set @Err 
=2
      Goto theEnd
    End 
更新其他记录的Column_Order
Update Tb_Column Set Column_Order 
= Column_Order 1 Where Column_Order >@Column_Order 
IF @@Error
<>0
    Begin
      Set @Err 
=3
      Goto theEnd
    End 
删除操作
Delete From Tb_Column Where Column_ID
=@Column_ID
IF @@Error
<>0
    Begin
      Set @Err 
=4
      Goto theEnd
  End 
更新其他记录的Column_ID
Update Tb_Column Set Column_ID= Column_ID  1 Where Column_ID >@Column_ID 
IF @@Error<>0
    Begin
      Set @Err =5
      Goto theEnd
    End 
theEnd:
IF @Err 
= 0 
    Begin
      Commit Tran
      Return 
0 删除成功
    End
Else
    Begin
      IF @Err
=1
  Begin
      Rollback Tran
      Return 
1 有子节点
End
      Else
Begin
      Rollback Tran
      Return 
2未知错误
End
    End
GO

4.编辑的存储过程(没有用到,我自己写了一个简单的只是编辑名称,没有涉及到移动)

Create PROCEDURE sp_Column_Update
(
@Column_ID 
int
,
@Parent_ID 
int
,
@Column_Name nvarchar(
50
),
@Column_Intro nvarchar(
1000
)
)
AS
Declare @Err As 
int

Set @Err
=0
Begin Tran
获取修改前的:Parent_ID,Column_Depth,Column_Order
Declare @oParent_ID As 
int

Declare @oColumn_Depth As 
int
Declare @oColumn_Order As 
int
Declare @oColumn_Path As nvarchar(
1000)
Select @oParent_ID 
= Parent_ID, @oColumn_Depth = Column_Depth,@oColumn_Order = Column_Order, @oColumn_Path = Column_Path  From Tb_Column Where Column_ID =
 @Column_ID
IF @oParent_ID Is Null
    Begin
    Set @Err 
= 1

    Goto theEnd
    End
如果父ID没有改变,则直接修改栏目名和栏目简介
IF @oParent_ID 
=
 @Parent_ID
    Begin
    Update Tb_Column Set Column_Name 
= @Column_Name,Column_Intro = @Column_Intro Where Column_ID =
 @Column_ID
    IF @@Error 
<> 0

    Set @Err 
= 2
    Goto theEnd
    End
Declare @nColumn_Path As nvarchar(
1000)
Declare @nColumn_Depth As 
int

Declare @nColumn_Order As 
int
获取当前节点作为父节点所包含的节点数[包括自身] 注:如果返回 “1” 说明是单节点
Declare @theCount As 
int

Select @theCount 
= Count(Column_ID) From Tb_Column Where Column_ID=@Column_ID Or Column_Path like ''+@oColumn_Path+'|%'
IF @theCount Is Null
Begin
    Set @Err 
= 3
    Goto theEnd
End 
IF @Parent_ID
=0 如果是设置为顶级节点,将节点设置为最后一个顶级节点
Begin
Print '设置为顶级栏目'

Set @nColumn_Path 
= Ltrim(Str(@Column_ID))
Set @nColumn_Depth 
=1

Select @nColumn_Order 
= Max(Column_Order) From Tb_Column
IF @nColumn_Order Is NULL
                  Begin
     Set @Err 
= 4

     Goto theEnd
     End 
Set @nColumn_Order 
= @nColumn_Order  @theCount + 1
更新三部分 1 节点本身 2 所有子节点 2 本树更改之前的后面记录的顺序
Print '更新本栏目之前位置后面的所有栏目[不包括本栏目下的子栏目]的:Column_Order'

Update Tb_Column Set Column_Order 
= Column_Order@theCount Where (Column_Order >@oColumn_Order) And (Column_Path Not like ''+@oColumn_Path+'|%')
IF @@Error 
<> 0

    Begin
    Set @Err 
= 7
    Goto theEnd
    End
Print '更新本栏目的:Parent_ID,Column_Path,Column_Depth,Column_Order,Column_Name,Column_Intro'
Print 
'Order : '+Ltrim(Str(@nColumn_Order))
Update Tb_Column Set Parent_ID
=@Parent_ID,Column_Path = @nColumn_Path,Column_Depth = @nColumn_Depth,Column_Order = @nColumn_Order, Column_Name = @Column_Name,Column_Intro = @Column_Intro Where Column_ID =
 @Column_ID
IF @@Error 
<> 0

    Begin
    Set @Err 
= 5
    Goto theEnd
    End
Print '更新本栏目下的所有子栏目的:Column_Path,Column_Depth,Column_Order'
             Update Tb_Column Set Column_Path 
= Replace(Column_Path,@oColumn_Path,@nColumn_Path),Column_Depth = Column_Depth + (@nColumn_Depth@oColumn_Depth),Column_Order = Column_Order+( @nColumn_Order@oColumn_Order) Where Column_Path like ''+@oColumn_Path+'|%'
IF @@Error 
<> 0
    Begin
    Set @Err 
= 6
    Goto theEnd
    End
End 
Else
Begin
获取未来父节点的相关信息,并设置本节点的相关值
Select @nColumn_Depth 
= Column_Depth,@nColumn_Path = Column_Path From Tb_Column Where Column_ID =
 @Parent_ID
IF @nColumn_Depth Is  NULL Or @nColumn_Path Is Null
      Begin
      Set @Err 
= 8

      Goto theEnd
      End 
Set @nColumn_Depth 
= @nColumn_Depth +1
Select @nColumn_Order 
=Max(Column_Order) From Tb_Column Where Column_ID = @Paren
 
t_ID Or  Column_Path like 
''+@nColumn_Path+'|%'

IF @nColumn_Order Is  NULL
      Begin
      Set @Err 
= 9
      Goto theEnd
      End 
Set @nColumn_Path 
= @nColumn_Path +'|'+ Ltrim(Str(@Column_ID))
IF @nColumn_Order 
= @oColumn_Order+1 
如果新的父节点是原来位置上端最近一个兄弟,则所有节点的顺序都不改变
                    Begin
Update Tb_Column Set Parent_ID
=@Parent_ID,Column_Path = @nColumn_Path,Column_Depth = @nColumn_Depth, Column_Name = @Column_Name,Column_Intro = @Column_Intro Where Column_ID =
 @Column_ID
IF @@Error 
<> 0

    Begin
    Set @Err 
= 10
    Goto theEnd
    End
       End
Set  @nColumn_Order 
= @nColumn_Order + 1 
更新三部分 1 本树更改之前的后面(或前面)记录的顺序 1 节点本身  3
 所有子节点
分为向上移或象下移
Print '更新本栏目之前位置后面的所有栏目[或者本栏目之后位置]  [不包括本栏目下的子栏目]的:Column_Order'

IF @nColumn_Order 
< @oColumn_Order
Begin
Update Tb_Column Set Column_Order 
= Column_Order+@theCount Where Column_Order<@oColumn_Order  And Column_Order >=@nColumn_Order And (Column_Path Not like ''+@oColumn_Path+'|%') And Column_ID<>
@Column_ID
IF @@Error 
<> 0

        Begin
        Set @Err 
= 12
        Goto theEnd
        End
End
Else
Begin
Update Tb_Column Set Column_Order 
= Column_Order@theCount Where Column_Order >@oColumn_Order And Column_Order<@nColumn_Order  And (Column_Path Not like ''+@oColumn_Path+'|%') And Column_ID<>@Column_ID
IF @@Error 
<> 0

        Begin
        Set @Err 
= 13
        Goto theEnd
        End
End
Print '更新本栏目的:Parent_ID,Column_Path,Column_Depth,Column_Order,Column_Name,Column_Intro'
Print 
'Order : '+Ltrim(Str(@nColumn_Order))
IF @nColumn_Order 
>
 @oColumn_Order
Set @nColumn_Order 
= @nColumn_Order 
 @theCount
Update Tb_Column Set Parent_ID
=@Parent_ID,Column_Path = @nColumn_Path,Column_Depth = @nColumn_Depth,Column_Order = @nColumn_Order, Column_Name = @Column_Name,Column_Intro = @Column_Intro Where Column_ID =
 @Column_ID
IF @@Error 
<> 0

    Begin
    Set @Err 
= 10
    Goto theEnd
    End
Print '更新本栏目下的所有子栏目的:Column_Paht,Column_Depth,Column_Order'
             Update Tb_Column Set Column_Path 
= Replace(Column_Path,@oColumn_Path,@nColumn_Path),Column_Depth = Column_Depth + (@nColumn_Depth@oColumn_Depth),Column_Order = Column_Order+(@nColumn_Order@oColumn_Order) Where Column_Path like ''+@oColumn_Path+'|%'
IF @@Error 
<> 0
    Begin
    Set @Err 
= 11
    Goto theEnd
    End
End
theEnd:
IF @Err
<>0 如果有错误则返回错误号
   Begin
   Rollback Tran
   Return @Err
   End
Else     
如果没有错误就返回0
   Begin
   Commit Tran
   Return 
0

   End
GO

 

存储过程实现无限级分类(3)

最后一步显示分类(只是一条select语句)

Create PROCEDURE sp_Column_List 
 AS
Select Column_ID, Column_Name, Parent_ID, Column_Path, Column_Depth, 
      Column_Order, Column_Intro
FROM Tb_Column
orDER BY Column_Order
GO

接下来就是在界面呈现了 ,    显示的时候用的是DataGrid

Html代码如下:

<asp:DataGrid id="DataGrid1" runat="server" AutoGenerateColumns="False" Width="520px" DataKeyField="Column_Id">
                                
<Columns>
                                    
<asp:TemplateColumn HeaderText="分类信息">
                                        
<ItemTemplate>
                                            
<asp:Label id="lbname" runat="server"></asp:Label>
                                        
</ItemTemplate>
                                        
<EditItemTemplate>
                                            
<asp:TextBox id="tbdgname" runat="server" Text='<%#DataBinder.Eval(Container.DataItem,"Column_Name")%>'>
                                            
</asp:TextBox>
                                        
</EditItemTemplate>
                                    
</asp:TemplateColumn>
                                    
<asp:TemplateColumn HeaderText="添加子节点">
                                        
<ItemTemplate>
                                            
<a href="#" onclick="Open('AddSonCate.aspx?fid=<%#DataBinder.Eval(Container.DataItem,"Column_Id")%>','son',280,80)">添加子分类</a>
                                        
</ItemTemplate>
                                    
</asp:TemplateColumn>
                                    
<asp:TemplateColumn HeaderText="编辑">
                                        
<ItemTemplate>
                                            
<asp:LinkButton runat="server" Text="编辑" CommandName="Edit" CausesValidation="false"></asp:LinkButton>
                                        
</ItemTemplate>
                                        
<EditItemTemplate>
                                            
<asp:LinkButton runat="server" Text="更新" CommandName="Update" CausesValidation="False"></asp:LinkButton>&nbsp;
                                            
<asp:LinkButton runat="server" Text="取消" CommandName="Cancel" CausesValidation="false"></asp:LinkButton>

                                        
</EditItemTemplate>
                                    
</asp:TemplateColumn>
                                    
<asp:TemplateColumn HeaderText="删除">
                                        
<ItemTemplate>
                                            
<asp:LinkButton runat="server" ID="lbdelete" Text="删除" CommandName="Delete" CausesValidation="false" CommandArgument='<%#DataBinder.Eval(Container.DataItem,"Column_Id")%>'>
                                            
</asp:LinkButton>
                                        
</ItemTemplate>
                                    
</asp:TemplateColumn>
                                
</Columns>
                            
</asp:DataGrid>

 

最关键的地方在DataGrid_ItemDataBind事件中

private void DataGrid1_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
        {
            
if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType ==
 ListItemType.AlternatingItem)
            {
                
//得到Column_Name字段的值

                string columnName = (string)DataBinder.Eval(e.Item.DataItem,    "Column_Name");
                
string columnTemp = ""
;
                
//得到深度值Column_Depth

                int columnDepth = Int32.Parse(DataBinder.Eval(e.Item.DataItem,"Column_Depth").ToString());
                
if(columnDepth>1
)
                {
                    
for(int i = 1;i<columnDepth;i++
)
                    {
                        columnTemp 
+="&nbsp;&nbsp;&nbsp;&nbsp;"
;
                    }
                    columnTemp
+=""
;
                }
                Label lbname 
= (Label)e.Item.FindControl("lbname"
);
                lbname.Text 
= columnTemp+
columnName ;
                LinkButton lbdelete 
= (LinkButton)e.Item.FindControl("lbdelete"
);
                lbdelete.Attributes.Add(
"OnClick","JavaScript:return confirm('确实要删掉此分类吗?');"
);
            }                
        }

[MVC]Free ASP.NET MVC “NerdDinner” Tutorial Now in

mikel阅读(766)

Scott联合几位编写的《Professional ASP.NET MVC 1.0 》在线文档上线了,以下引自Scott的blog:

Last month I blogged about a free end-to-end ASP.NET MVC tutorial called “NerdDinner” that I wrote for the Professional ASP.NET MVC 1.0 book from Wrox Press.  The book is now released and shipping on Amazon

The NerdDinner tutorial walks through how to build a small, but complete, application using ASP.NET MVC, and introduces some of the core concepts behind it.  You can download a PDF version of the tutorial here.

NerdDinner Tutorial Now Also Available in HTML

A few minutes ago I finished publishing an HTML version of the NerdDinner tutorial as well.  You can read it online for free here.

I split the tutorial up across 12 segments to make it more manageable to read.  I also increased the sizes of the screenshots, and used a really nifty syntax highlighter that Scott Hanselman helped set me up with.  I actually find the end result a lot easier to read than the PDF version.

Below are links to the different NerdDinner tutorial segments:

Hope this helps,

Scott

[C#]采访:Chad Myers和FubuMVC - ASP.NET上的另一个MVC实现

mikel阅读(738)

作者 Jon Arild Tørresdal译者 王瑜珩 发布于 2009年4月21日 下午9时51分

社区
.NET
主题
开放源代码,
Web框架
标签
ASP.NET MVC,
FubuMVC,
ASP.NET

ASP.NET MVC 正式版发布前,Jeremy D.MillerChad Myers 就在ASP.NET MVC的早期版本上进行了一些工作,并对底层实现做了一些修改。后来他们改掉了几乎所有的ASP.NET MVC实现,于是决定构造另一个MVC实现FubuMVC ,不久后Mark Nijhof 被邀请加入项目并成为主要成员。

Fubu代表“For us,by us”。现在FubuMVC除了使用ASP.NET Routing外,不使用任何ASP.NET MVC的实现代码,而ASP.NET Routing则已经包含在.NET Framework 3.5 SP1中。

InfoQ的Jon Arild Tørresda询问了Chad Myers,ASP.NET MVC与FubuMVC之间最大的不同是什么:

如果非要选一个,我选择“组合对继承”。这是一个设计上的基本区别,但并不是说ASP.NET MVC的设计不好,只是我认为ASP.NET MVC在类结构设计上倾向于使用继承,因而无法像使用组合那样易于设计动态的Web应用程序。

FubuMVC是一个前端控制器 (Front Controller)框架。Chad指出这个模式的两个主要目标是:

  1. 分离对请求的不同关注点
  2. 允许使用组合的方式构造响应,以发回给客户端

对于前端控制器,Chad解释道:“我们不是不能使用ASP.NET MVC来实现前端控制器,但是这非常的困难”。

在FubuMVC中有很多实现方面的决定,其中之一是在Controller的Action执行前后所执行的“行为”。Chad解释了为什么他们管它叫行为,以及它在FubuMVC中的意义。

当我在一个Virual ALT.NET(VAN)会议上向一些人演示FubuMVC的早期版本时,Steven Harman (http://stevenharman.net)建议我将之称为“行为”,因为这个词语准确描述了所发生的事,我有点喜欢这个名字。

在FubuMVC中,行为的实现方式实际上是装饰模式和职责链模式的混合体。

……

行为对请求管道拥有完全控制权,它可以添加或修改请求,动态选择需要执行的action以及是否要执行action,它可以修改或者完全替换 action的输出结果,并且可以在完成请求处理后执行一些代码。实际上,生成显示结果本身也是一个行为。FubuMVC使用行为本身来实现基本的功能, 这些基本功能和行为可以根据需要被替换或修改。

Mark Nijhof在他的文章FubuMVC and the Front Controller style framework中展示了这个管道:

Request Flow

Chad说,“行为开启了在其他框架中难以实现的可能”:

  • 将整个请求包装在try/catch/finally块中的能力
  • 多级缓存的能力
  • 根据运行时环境或请求时间,动态决定执行哪个action的能力

MVC模式的另一个方面,是使得开发人员可以对传统意义上无法进行测试的UI部分进行单元测试。Chad描述了微软是如何实现这一点的:

……微软在最近对MVC框架的更新中(Beta,RC和最终的发布版)迈出了一大步,相比于Preview 3,对单元测试的支持更好了。但是我仍然认为继承和防备代码的过度使用以及故意不使用接口,使得在ASP.NET MVC中进行测试显得很笨重。

他继续解释了FubuMVC是如何实现这一模式的:

相反,FubuMVC使用简洁的、易于mock的接口,着重于高内聚低耦合的设计。其中,低耦合更成功一些,但这一切仍在开发之中,我希望将来的设计可以提高内聚程度。

FubuMVC高度依赖SOLID原则,这使得它有很高的灵活性,开发人员仅仅使用一个mock就可以替换框架中的整套部件,并且可以使用任何他们喜欢的mock框架。

FubuMVC并没有很多的防御性代码……相反,它将注意力集中在设计提供自由控制的组件上面,这些组建是客户代码主要存在的地方:控制器(controller)、行为、视图(view)以及可以重载的部分。

FubuMVC的类之间几乎没有依赖关系,仅有的依赖也是对接口的依赖,这些接口可以很容易的用mock对象来模拟。

由于项目中有Jeremy(IoC容器StructureMap的创建者),你可能会认为控制反转和IoC容器会得到较多的支持,事实上也确实如此:

目前的版本仅支持StructureMap,但是将来很可能会加入对其他容器的支持。框架对于容器的使用非常少,仅限于在配置时使用。其余的部分利 用容器的自动绑定功能完成,因此基本上没有使用“service location”。对于仅有的一点service location,我们使用微软Patterns and Practices的Common Service Locator进行处理,它可以让我们方便的替换底层依附于CSL模式的IoC容器(多数容器都满足这个条件)。

FubuMVC还有一个contrib project,InfoQ问道相比于FubuMVC的核心框架,这个项目的目标有什么不同:

我们希望能够有更多的自由来发展FubuMVC,因此建立了FubuMVC Contrib。我们想尝试一下插件,这样可以有更多的人参与进来,他们可以在较少的限制下做更多的尝试,同时保持核心框架的稳定。

FubuMVC核心框架将会维持少数几个成员,对待补丁会更谨慎,对框架的修改也会更少。FubuMVC-Contrib将会有更多的参与者、更多 的改动、更低的要求,可能有无法工作的代码或实验性质的代码。当在contrib中开发出有趣的东西后,可以将这些东西合并到核心框架,或者拆分到单独的 项目中。

现今,FubuMVC还没有ASP.NET MVC那样成熟,但是它的实现方式很有趣,这个框架将会如何发展,它与ASP.NET MVC的发展方向将会有怎样的不同,我们将拭目以待。关于FubuMVC的更多信息,可以查看他们的wikiRyan Kelley的从头开始学FubuMVC教程。

原文地址:Interview: Chad Myers on FubuMVC – An Alternative MVC Implementation in ASP.NET

[SVN]更换SVN服务器地址

mikel阅读(697)

有时更换svn服务器地址的时候使代码和工程的联系断了,但是又还有代码没有提交,这时可以手工把工程和svn服务器连起来

郁闷呀,现在找到办法了

查找所有工程下的entries文件,把其中的 http://svnserver/svnroot (svn地址)替换为新的地址

ultraedit的在文件中替换功能太帅了

 

  使用最新版本的TortoiseSVN,右键在工作复本的根目录上右键->TortoiseSVN->重新定位(Relocate),再按提示操作就可以了..不过最好先备份..因为这个操作有一定的危险性

[IIS]没有对“C:\WINDOWS\Microsoft.NET\Framework\v2.0.5

mikel阅读(708)

在IIS中 发布程序一个ASP.NET程序,通过IE访问报如下错误:

当前标识(NT AUTHORITY\NETWORK SERVICE)没有对“C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files”访问权限
说明: 执行当前 Web 请求期间,出现未处理异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误出处详细信息。
异常详细信息: System.Web.HttpException: 当前标识(NT AUTHORITY\NETWORK SERVICE)没有对“C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files”访问权限
源错误:
执行当前 Web 请求期间生成了未处理异常。可以使用下面异常堆栈跟踪信息确定有关异常原因和发生位置信息。
堆栈跟踪:
。。。。。。

=====================================

翻阅了一些资料后发现是需要重新注册IIS服务扩展,在“开始”-“运行”里输入如入命令,回车,搞定

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis -i -enable

冠亿科技,尽心尽力

[MVC]ASP.NET MVC:为视图自定义辅助方法(上)

mikel阅读(781)

转载: http://www.cnblogs.com/JeffreyZhao/archive/2009/04/29/custom-view-helpers-1.html

 在编写ASP.NET MVC应用程序时,只依赖内置的视图辅助方法很难达到很高的生产力,即使是定义在MvcFutures中的补充类库,也很难满足项目的具体需求。此外,不 同的项目有不同的特点,在很多时候也需要定义较为特殊的辅助方法,使开发人员能够更快,更方便地写出更容易维护的代码。这也是自定义视图辅助方法最主要的 目的(没有之一)。而这次的文章,便是给出一个这方面的示例,可作为此类问题的一个参考。

预备

  在编写客户端HTML时,进行客户端验证是最常见的工作之一。既然ASP.NET MVC集成了JQuery类库,那么我们不妨基于JQuery的Validate插件编写这部分功能。假设有如下表单:

<form method="post" action="" id="form">
<p>
<span>Name: </span> <!-- 必填 -->
<input type="text" name="user.Name" />
</p>
<p>
<span>Age: </span> <!-- 必填,15到28之间的数字 -->
<input type="text" name="user.Age" />
</p>
<p>
<span>Email:</span> <!-- 必填,且为合法Email -->
<input type="text" name="user.Email" />
</p>
<input type="submit" value="Submit" />
</form>

  如果使用JQuery Validate插件进行客户端校验,那么我们可以编写如下代码:

<script language="javascript" type="text/javascript">
$("#form").validate({
"rules": {
"user.Name": { "required": true },
"user.Age": {
"required": true,
"number": true,
"range": [15, 28]
},
"user.Email": {
"required": true,
"email": true
}
},
"messages": {
"user.Name": { "required": "please provide your name!!!" },
"user.Email": {
"required": "email please...",
"email": "valid email please..."
}
}, "onkeyup": false
});
</script>

  以上这段脚本的作用便是告诉jQuery校验哪个表单,对表单中的各个元素分别采取哪种校验方式,以及在发生错误的时候该提示什么信息(详细内 容可查阅文档)。这里可能需要补充一段题外话。实际上jQuery.Validate非常灵活,可以有各种方式提供校验信息,例如直接为html元素加特 殊标记。不过老赵认为那些方法的trick意味太浓,且不容易编写辅助方法协助开发,因此刻意忽略那些方式。

  这样的结果可能会招一些前台开发人员欢喜,不过老赵觉得,至少有三个地方需要提高:

  1. 数据分散,提高维护难度。例如HTML元素和校验规则分离,校验规则
  2. 对于开发人员来说,编写JSON较不方便,容易发生错误。
  3. 缺少对于“自动校验”的支持,需要完全手写。

  而我们编写的辅助方法,主要就是面向前两点,而最后一点可以说是自然而然的事情。

入口

  任何方法都需要一个入口,这个入口可能是某个静态类,甚至直接定义在global下最直接的“方法”。而ASP.NET MVC视图的辅助方法的入口往往定义在ViewPage中。例如与HTML相关的辅助方法定义在ViewPage.Html属性下,而与URL相关的辅助 方法都定义在ViewPage.Url属性下(ViewUserControl和ViewMasterPage情况也一样)。这样的入口区分,其实也是为 辅助方法进行了分类,也让用户可以在IDE的帮助下快速定位到所需的辅助方法。在编写一些简单的自定义辅助方法时,我们可以利用C# 3.0的扩展方法特性,将方法定义在HtmlHelper和UrlHelper上,这样便可直接在页面上使用。而对于一些特殊情况,例如目前的状况,我们 便需要重新定义一个入口,以便我们的辅助方法可以以此作为基础进行扩展。为此,我们把这一重担交给jQueryHelper类:

public class JQueryHelper
{
public JQueryHelper(ViewContext viewContext, ViewPage page)
: this(viewContext, page, RouteTable.Routes)
{ }
public JQueryHelper(
ViewContext viewContext,
ViewPage page,
RouteCollection routeCollection)
{
this.ViewContext = viewContext;
this.Page = page;
this.RouteCollection = routeCollection;
}
public RouteCollection RouteCollection { get; private set; }
public ViewContext ViewContext { get; private set; }
public ViewPage Page { get; private set; }
}

  生成JQueryHelper对象时,我们会保留目前的上下文,让各种扩展方法自行选用。至于保留哪些成员,这并没有太多限制,一般来说够用便 可,如有不足,我们按需补充便是。接下来便是要让页面可以访问到这个辅助对象了,与Html和Url这些现有的入口不同,我们使用扩展方法,而不是属性来 提供入口。这样做的好处便是在毫无侵入的情况,提供了较为友好的语法,这也是扩展方法的美妙之处:

public static class JQueryExtensions
{
public static JQueryHelper JQuery(this ViewPage page)
{
var key = typeof(JQueryHelper);
var jquery = page.Items[key] as JQueryHelper;
if (jquery == null)
{
page.Items[key] = jquery = new JQueryHelper(page.ViewContext, page);
}
return jquery;
}
}

  ViewPage的Items属性是一个页面级别的存储容器,可以使用键/值的方式存放任意数据,我们这里便利用这个特性,为每个页面提供唯一的JQueryHelper对象。

校验

  因为一个页面可能有多个需要校验的区域,因此我们需要提供一个机制,能够保存多个“批次”的校验信息。于是,我们编写一个JQueryValidation类用来存放“一批”校验信息:

public class JQueryValidation
{
public JQueryValidation(ViewPage page)
{
this.Page = page;
}
public ViewPage Page { get; private set; }
...
}

  当然,在JQueryHelper这个入口上,需要有一个访问校验信息的方式。在这里,我们使用字符串来标识“一批”校验信息。此外,我们也提供了默认重载以方便某些简单场景下的使用:

public static class JQueryValidationExtensions
{
public static JQueryValidation Validate(this JQueryHelper jquery)
{
return jquery.Validate("(default)");
}
public static JQueryValidation Validate(this JQueryHelper jquery, string name)
{
var key = typeof(JQueryValidation) + "+" + name;
var page = jquery.Page;
var validation = page.Items[key] as JQueryValidation;
if (validation == null)
{
page.Items[key] = validation = new JQueryValidation(page);
}
return validation;
}
}

  作了那么多的铺垫,接下来便是最关键辅助方法编写了。通过对JavaScript代码进行分析之后,我们决定采用典型的“记录 – 汇总”模式来编写这个方法1。“记录 – 汇总”模式的原则便是通过“记录”将信息汇总后输出,在作为页面辅助方法时,我们可以在任何地方“记录”校验信息,然后在合适的地方输出一段汇总后的脚本。由于我们所需要的JavaScript非常规整,因此实现这个需求并不困难。

  首先是记录,对于每个

public class JQueryValidation
{
private Dictionary<string, Dictionary<string, object>> m_rules =
new Dictionary<string, Dictionary<string, object>>();
private Dictionary<string, Dictionary<string, string>> m_messages =
new Dictionary<string, Dictionary<string, string>>();
private void AddRuleAndMessage(string name, string rule, object value, string m
{
if (!this.m_rules.ContainsKey(name))
this.m_rules[name] = new Dictionary<string, object>();
this.m_rules[name][rule] = value;
if (!String.IsNullOrEmpty(message))
{
if (!this.m_messages.ContainsKey(name))
this.m_messages[name] = new Dictionary<string, string>();
this.m_messages[name][rule] = message;
}
}
public void Required(string name, string message)
{
this.AddRuleAndMessage(name, "required", true, message);
}
public void Email(string name, string message)
{
this.AddRuleAndMessage(name, "email", true, message);
}
public void Number(string name, string message)
{
this.AddRuleAndMessage(name, "number", true, message);
}
public void Range(string name, int min, int max, string message)
{
this.AddRuleAndMessage(name, "range", new int[] { min, max }, message);
}
...
}

  我们使用两个字典分别存放规则的描述(m_rules)与错误提示(m_messages),由于信息的统一,我们只需编写一个 AddRuleAndMessage方法便可满足所有需要,而其他的方法只是定义了一个良好的接口,然后简单地把信息委托给 AddRuleAndMessage而已。

  最后便是汇总,我们使用JavaScriptSerializer把规模的描述与错误提示序列化成JSON字符串并输出:

public string ToScripts(string form)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
StringBuilder builder = new StringBuilder();
builder.Append("$(");
serializer.Serialize(form, builder);
builder.Append(").validate(");
serializer.Serialize(
new
{
rules = this.m_rules,
messages = this.m_messages,
onkeyup = false
}, builder);
builder.Append(");");
return builder.ToString();
}

使用

  “记录 – 汇总”,无它耳。

<form method="post" action="" id="form">
<p>
<span>Name: </span> <!-- 必填 -->
<input type="text" name="user.Name" />
<% this.JQuery().Validate().Required("user.Name", "please provide your name!!!"); %>
    </p>
<p>
<span>Age: </span> <!-- 必填,15到28之间的数字 -->
<input type="text" name="user.Age" />
<% this.JQuery().Validate().Required("user.Age", null); %>
        <% this.JQuery().Validate().Number("user.Age", null); %>
        <% this.JQuery().Validate().Range("user.Age", 15, 28, null); %>
    </p>
<p>
<span>Email:</span> <!-- 必填,且为合法Email -->
<input type="text" name="user.Email" />
<% this.JQuery().Validate().Required("user.Email", "email please..."); %>
        <% this.JQuery().Validate().Email("user.Email", "valid email please..."); %>
    </p>
<input type="submit" value="Submit" />
</form>
<script language="javascript" type="text/javascript">
<%= this.JQuery().Validate().ToScripts("#form") %>
</script>

  请看最后生成的HTML:

<script language="javascript" type="text/javascript">
$("#form").validate({ "rules": { "user.Name": { "required": true }, …
</script>

  因为编写了客户端辅助方法,我们已经把繁冗的客户端脚本变成了可以由Visual Studio提示并轻易输出的服务器端辅助方法——您觉得如何,是否满意?

  其实老赵还不满意,不过接下去的工作,我们下次再继续吧。

 

注1:什么?您说没有听说过“记录 – 汇总”这个模式?嗯,这是正常的,因为这是老赵“发明”的模式之一。

[MVC][一步一步MVC]第六回:MVC范例大观园

mikel阅读(892)

MVC是个新鲜的东西,至少为ASP .NET Web世界带来或多或少的争议,褒奖者有之,诋毁者有之。这也正常,人类的思维确实没法统一,即便是多个选择其实是件好事儿也一样。不管怎样,评说者至少 应该在了解的基础上再进行评论,才显得“专业”,没有品味的找茬型选手实在不值得恭维。

作为MVC范例集锦,我将MVC发布之后业界的应用开源项目做个简单的整理,Anytao不打算对任何项目发表个人观点,因为有些项目未经本人了解,所以此处仅是one by one式的陈列,期望对学习者有些帮助而已。

NerdDinner随着ScottGu、Phli几位大牛为作品《Professional ASP.NET MVC 1.0》谱写的辅助系统,同时通过一个在线系统http://www.nerddinner.com/以生动的实例来展现MVC技术,NerdDinner示例贯穿全书,尤其是第一章的Walkthrough可以让我们对MVC有个初次了解。

该范例是ASP.NET/mvc的讲解实例,因为它太简单以至于没有什么好说的,初入MVC的同学可以关注一下,安装step by step的方式构建自己的Contact Management应用。

不知为什么范例程序常常以Store来演示(例如Petshop),可能的原因是Store应用简单而全面,所以MVCStore应用也是这样一个简化示例, 我们可以研究研究。

业界对Oxite有不少批评Rob Conery是最尖锐的一个,作为“号称”微软MVC范例的Oxite,虽有些细可圈可点,但是离微软官方范例的角度确实还有距离。园子的代震军发表的关于两个MVC示例的思考(MVCStore和Oxite),对此进行的讨论或许可以给您以思考,从应用角度而言Oxite其实并非一无是处,很多点上还是有不错的运用,例如关于ActionFilter和IoC的应用是值得参考的。

Kigg是Codeplex上开源项目,从项目规模上而言,Kigg属于“大块头”,以MVC为基础架构,同时设计很多技术应用,单元测试也有不错可借鉴的地方。

Ezsocio至少可以是个范例,一套基于微软.NET平台的Social Network Software(SNS)系统,会用到许多最新开发技术,如ASP.NET MVC、LINQ、WCF、Unity、JQuery等,虽然还是个半成品,但是作为对于MVC学习和了解角度而言,还是“够用的”。例如以Unity为 容器的IoC设计、JQuery在MVC的应用、Validation、Cryptography、Repository模式等,好了不做广告了,那只是 个还没有完成的作品。

Sharp-Architecture是MVC和NHibernate应用范例,作为号称“Sharp Architecture”的应用,我想一定可以为我们呈现不一样的设计,对于了解MVC和NHibernate的人或许是个不错的选择。

fubumvc,可以看作是for us by us MVC,是老赵同志推荐给我的。fubumvc在了解ASP .NET MVC核心架构的基础上,表达了作者“不同意”ASP .NET MVC对于设计的理解,所以几个人凑在一起合谋了fubumvc Framework。这里,有一些关于FubuMVC和ASP .NET MVC的区别。

 

既然是大观园,可能以后还有补充,谁有更好的分享也可以在留言提供给我,以方便大家“有底放肆”。至于品评和讨论,还是放在以后吧。

 

更多关注,尽在anytao.net/blog

 

anytao | © 2009 Anytao.com

2009/04/29 | http://anytao.cnblogs.com/ | http://anytao.net/blog/post/2009/04/28/anytao-mvc-06-mvcsamples.aspx

本文以“现状”提供且没有任何担保,同时也没有授予任何权利。 | This posting is provided "AS IS" with no warranties, and confers no rights.

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[C#].net反射技术封装

mikel阅读(724)

如果有人问你,如何调用一个类的private的方法或访问一个类的私有成员,如果你不知道反射的话,你会告诉他不可以。但是有了反射,这一切都成为可 能。我有时候会想,既然使用private来限制访问,为什么又要用反射去破坏这种限制呢?也许可以通过侧面来解释这个问题,如果你要维护类的封装性,那 请不要用反射,反射会破坏类的封装性。
    但反正某些情况下,又会变得相当地有用,比如你有一个dll,其中很多类被声明为internal,如果你要使用这些类,而又无法修改源代码的话,你就可以使用反射了,最近总结了一下,对反射做了一个封装。由于代码比较简单,没有做注释。

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace UsefulUtility
{
    public class Invoker
    {
        private static BindingFlags flags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.IgnoreCase;
        public static Type GetType(Assembly asm, string typeFullName)
        {
            return asm.GetType(typeFullName);
        }
        public static Type GetInnerType(string hiddenTypeName, Type outerType)
        {
            Module typeModule = outerType.Module;
            return typeModule.GetType(hiddenTypeName);
        }
        public static MethodInfo GetMethod(Type type, string funcName, Type[] paramTypes)
        {
            if (paramTypes != null)
                return type.GetMethod(funcName, flags, null, paramTypes, null);
            else
                return type.GetMethod(funcName, flags);
        }
        public static MethodInfo GetMethod(Type type, string funcName)
        {
            return GetMethod(type, funcName, null);
        }
        public static object CallMethod(object obj, string funcName, params object[] parameters)
        {
            Type type = obj.GetType();
            MethodInfo method = GetMethod(type, funcName, GetTypesFromObjects(parameters));
            return method.Invoke(obj, parameters);
        }
        public static object CallStaticMethod(Type type, string funcName, params object[] parameters)
        {
            MethodInfo method = GetMethod(type, funcName, GetTypesFromObjects(parameters));
            return method.Invoke(null, parameters);
        }
        public static object GetProperty(object obj, string propertyName)
        {
            return GetProperty(obj, propertyName, null);
        }
        public static object GetProperty(object obj, string propertyName, params object[] index)
        {
            Type type = obj.GetType();
            return type.GetProperty(propertyName, flags).GetValue(obj, index);
        }
        public static object GetStaticProperty(Type type, string propertyName)
        {
            return GetStaticProperty(type, propertyName, null);
        }
        public static object GetStaticProperty(Type type, string propertyName, params object[] index)
        {
            return type.GetProperty(propertyName, flags).GetValue(null, index);
        }
        public static void SetProperty(object obj, string propertyName, object value)
        {
            SetProperty(obj, propertyName, value, null);
        }
        public static void SetProperty(object obj, string propertyName, object value, params object[] index)
        {
            Type type = obj.GetType();
            type.GetProperty(propertyName, flags).SetValue(obj, value, index);
        }
        public static void SetStaticProperty(Type type, string propertyName, object value)
        {
            SetStaticProperty(type, propertyName, value, null);
        }
        public static void SetStaticProperty(Type type, string propertyName, object value, params object[] index)
        {
            type.GetProperty(propertyName, flags).SetValue(null, value, index);
        }
        public static object GetField(object obj, string fieldName)
        {
            Type type = obj.GetType();
            return type.GetField(fieldName, flags).GetValue(obj);
        }
        public static object GetSaticField(Type type, string fieldName)
        {
            return type.GetField(fieldName, flags).GetValue(null);
        }
        public static void SetField(object obj, string fieldName, object value)
        {
            Type type = obj.GetType();
            type.GetField(fieldName, flags).SetValue(obj, value);
        }
        public static void SetStaticField(Type type, string fieldName, object value)
        {
            type.GetField(fieldName, flags).SetValue(null, value);
        }
        private static ConstructorInfo GetConstructor(Type type, Type[] paramTypes)
        {
            return type.GetConstructor(paramTypes);
        }
        public static object CreateInstance(Type type, Type[] paramTypes, params object[] parameters)
        {
            return GetConstructor(type, paramTypes).Invoke(parameters);
        }
        public static object CreateInstance(Type type, params object[] parameters)
        {
            return GetConstructor(type, GetTypesFromObjects(parameters)).Invoke(parameters);
        }
        private static Type[] GetTypesFromObjects(object[] objs)
        {
            Type[] types = new Type[objs.Length];
            for (int i = 0; i < types.Length; i++)
                types[i] = objs[i].GetType();
            return types;
        }
        public static void AddEventHandler(object obj, string eventName, MethodInfo method, object methodOwner)
        {
            EventInfo eventInfo = obj.GetType().GetEvent(eventName, flags);
            Delegate eventDeleg = Delegate.CreateDelegate(eventInfo.EventHandlerType, methodOwner, method);
            eventInfo.AddEventHandler(obj, eventDeleg);
        }
        public static void RemoveEventHandler(object obj, string eventName, MethodInfo method,object methodOwner)
        {
            EventInfo eventInfo = obj.GetType().GetEvent(eventName, flags);
            Delegate eventDeleg = Delegate.CreateDelegate(eventInfo.EventHandlerType, methodOwner, method);
            eventInfo.RemoveEventHandler(obj, eventDeleg);
        }
    }
}

    下面的代码对上面的一些方法进行测试,由于手头只有一个Fetion SDK.dll的文件,所以就拿他开刀了,实现简单的登录,登录后发送一条测试信息到我的手机,为了安全略去了某些信息。

using System;
using System.Collections.Generic;
using System.Text;
using UsefulUtility;
using System.Reflection;
namespace ConsoleApplication1
{
    class Program
    {
        static object sdk;
        static Assembly asm;
        [STAThread]
        static void Main(string[] args)
        {
            // 载入程序集
            asm = Assembly.LoadFrom("Fetion SDK.dll");
            // 获取Fetion SDK的类型
            Type type = Invoker.GetType(asm, "com.hetaoos.FetionSDK.FetionSDK");
            // 实例化sdk
            sdk = Invoker.CreateInstance(type);
            // 获取账号管理的属性
            object accountManager = Invoker.GetProperty(sdk, "AccountManager");
            // 设置用户名和密码
            Invoker.CallMethod(accountManager, "FillUserIdAndPassword", new object[] { "手机号码", "飞信密码", false });
            // 监听事件
            Invoker.AddEventHandler(sdk, "SDK_UserSatusChange", Invoker.GetMethod(typeof(Program), "sdk_SDK_UserSatusChange"), null);
            // 调用登录方法
            Invoker.CallMethod(accountManager, "Login");
        }
        static void Hello()
        {
            Console.WriteLine("hello in Hello");
        }
        static void sdk_SDK_UserSatusChange(object sender, EventArgs e)
        {
            Console.WriteLine(Invoker.GetProperty(e, "NewStatus"));
            Console.WriteLine(Invoker.GetSaticField(Invoker.GetType(asm, "Imps.Client.UserAccountStatus"), "Logon"));
            // 这里用==不好使,要用Equals,不知道为什么
            if (Invoker.GetProperty(e, "NewStatus").Equals(Invoker.GetSaticField(Invoker.GetType(asm, "Imps.Client.UserAccountStatus"), "Logon")))
            {
                Console.WriteLine("hello");
                object contactControl = Invoker.GetProperty(sdk, "ContactControl");
                object sendSMS = Invoker.GetProperty(contactControl, "SendSMS");
                Invoker.CallMethod(sendSMS, "SendSMS", "要发送信息的飞信号或手机号码", "hello, a test");
            }
        }
    }
}

转载请注明出处,本文原始地址:http://www.cnblogs.com/dlutwy/archive/2009/04/28/1445352.html

posted @ 2009-04-28 13:17 小橋流水 阅读(334) 评论(9)  编辑 收藏 网摘 所属分类: 编程语言

  回复  引用  查看    

#1楼 2009-04-28 13:20 | henry      

我常用的
public static T[] GetMethodAttributes<T>(MethodInfo mi, bool inhert) where T: Attribute
{
return (T[]) mi.GetCustomAttributes(typeof(T), inhert);
}
public static T[] GetParemeterAttributes<T>(ParameterInfo pi, bool inhert) where T: Attribute
{
return (T[]) pi.GetCustomAttributes(typeof(T), inhert);
}
public static T[] GetPropertyAttributes<T>(PropertyInfo pi, bool inhert) where T: Attribute
{
return (T[]) pi.GetCustomAttributes(typeof(T), inhert);
}
public static T[] GetTypeAttributes<T>(Type type, bool inhert) where T: Attribute
{
return (T[]) type.GetCustomAttributes(typeof(T), inhert);
}

[FMS]FMS3系列(五):通过FMS实现时时视频聊天(Flash|Flex)

mikel阅读(599)

      本系列的前几篇文章中分别介绍了,连接FMS服务器、建立播放程序以及在线视频录制以及回放等功能的实现。相信看过前面几篇文章的朋友已经对FMS有了一定的认识,并熟悉了常用的编程模式。本文将结合前面几篇文章所出现的技术点,来实现一个时时视频聊天程序。

      通过FMS实现视频时时聊天其实很简单,也就是操作时时流。如果是单向视频聊天,则两端一边一边为发布端一边为订阅端,如果是双向视频聊天,则两边都分别是发布端和订阅端。

      如果从技术实现上来分析,单向视频聊天就是一边发布流另一边播放流,双向视频聊天则是两边都需要提供两个流,一个负责发布流,一个负责播放流。在说专业点就是一个创建流并且发送到服务器的客户端叫发布,一个创建流来接受内容的客户端叫订阅,当同一个客户端同是发布和订阅时,它必须创建两个流,一个是输出流,一个是接受流。

       说了这么多下面看看具体是怎么实现的,要实现视频聊天上面分析过,就是一边发布时时视频流一边播放,这同样离不开连接FMS,代码如下:

private function onPublishClick(evt:MouseEvent):void
{
      nc 
= new NetConnection();
      nc.connect(
"rtmp://localhost/LiveStreams");
      nc.addEventListener(NetStatusEvent.NET_STATUS,onNetStatusHandler);
}

 

       通过点击按扭连接(NetConnection)FMS服务器,然后向FMS发布(publish)视频流,达到视频发布的目的。这 里需要注意一点,在发布方法publish()中后一参数为“live”,表示时时视频流。以live的形式发布的流不会在FMS里生成.fla文件,不 同于“record”录制视频流生成.flv的视频文件。

private function onNetStatusHandler(evt:NetStatusEvent):void
 {
       trace(evt.info.code);
       
if(evt.info.code=="NetConnection.Connect.Success")
       {
           ns
=new NetStream(nc);
           ns.addEventListener(NetStatusEvent.NET_STATUS,onNetStatusHandler);
           ns.client
=new CustomClient();
           ns.attachCamera(cam);
           ns.attachAudio(mic);
           ns.publish(txtInput.text,
"live");
       }
}

 

      实现视频发布的核心技术点就是获取视频、音频数据,分别通过Camera和Microphone的静态方法实现。参考代码:

public function PublishStream():void
{
    btnPublish.label
="发布视频";
    btnPublish.addEventListener(MouseEvent.CLICK,onPublishClick);
            
    
//获取视频和声音,并将视频显示到Flash界面
    cam = Camera.getCamera();
    mic 
= Microphone.getMicrophone();
    video 
= new Video(320,240);
    video.attachCamera(cam);
    video.x
=20;
    video.y
=20;
    addChild(video);
}

 

      通过以上步骤就完成了视频聊天的视频流发布端的开发,完整的示例代码如下:

package
{
    import flash.net.
*;
    import flash.events.
*;
    import flash.display.
*;
    import flash.media.
*;
    import fl.controls.
*;
    
    
public class PublishStream extends Sprite
    {
        
private var video:Video;
        
private var nc:NetConnection;
        
private var ns:NetStream;
        
private var cam:Camera;
        
private var mic:Microphone;
        
        
public function PublishStream():void
        {
            btnPublish.label
="发布视频";
            btnPublish.addEventListener(MouseEvent.CLICK,onPublishClick);
            
            
//获取视频和声音,并将视频显示到Flash界面
            cam = Camera.getCamera();
            mic 
= Microphone.getMicrophone();
            video 
= new Video(320,240);
            video.attachCamera(cam);
            video.x
=20;
            video.y
=20;
            addChild(video);
        }
        
        
private function onPublishClick(evt:MouseEvent):void
        {
            nc 
= new NetConnection();
            nc.connect(
"rtmp://localhost/LiveStreams");
            nc.addEventListener(NetStatusEvent.NET_STATUS,onNetStatusHandler);
        }
        
        
private function onNetStatusHandler(evt:NetStatusEvent):void
        {
            trace(evt.info.code);
            
if(evt.info.code=="NetConnection.Connect.Success")
            {
                ns
=new NetStream(nc);
                ns.addEventListener(NetStatusEvent.NET_STATUS,onNetStatusHandler);
                ns.client
=new CustomClient();
                ns.attachCamera(cam);
                ns.attachAudio(mic);
                ns.publish(txtInput.text,
"live");
            }
        }
    }
}

 

      视频接收端相对发布端更简单,提供一个NetConnetion连接到发布端的FMS,通过NetStream播放时时视频流就完成。代码很简单,基本上都是在本系列前几篇文章中出现过的代码片段整合而成,详细见下代码块:

package
{
    import flash.net.
*;
    import flash.events.
*;
    import flash.display.
*;
    import flash.media.
*;
    
    
public class LiveStream extends Sprite
    {
        
private var video:Video;
        
private var nc:NetConnection;
        
private var ns:NetStream;
        
        
public function LiveStream():void
        {
            nc 
= new NetConnection();
            nc.connect(
"rtmp://localhost/LiveStreams");
            nc.addEventListener(NetStatusEvent.NET_STATUS,onNetStatusHandler);
        }
        
        
private function onNetStatusHandler(evt:NetStatusEvent):void
        {
            
if(evt.info.code=="NetConnection.Connect.Success")
            {
                ns
=new NetStream(nc);
                ns.addEventListener(NetStatusEvent.NET_STATUS,onNetStatusHandler);
                ns.client
=new CustomClient();
                video
=new Video();
                video.attachNetStream(ns);
                ns.play(
"1111");//1111为流的名字,对应于视频流发布端的publish("1111","live").
                addChild(video);
            }
        }
    }
}

 

      OK,到这里视频聊天的两端都完成了,如果需要做双向视频聊天,只需要在每一边多提供一个流就可以了,两端都实现发布视频流和接收视频流数据。

      或许看完文章的朋友回问到CustomClient是那里来的,CustomClient是为了处理元数据的,可以通过元数据向实况视频添加一些格外的属性,本文暂时不讨论,有兴趣的朋友可以查看官方文档了解。

本文示例程序下载:点击下载 (为了节约空间,这里只提供了Flash实现的示例代码,需要Flex的可以留言索取)

版权说明

  本文属原创文章,欢迎转载,其版权归作者和博客园共有。  

  作      者:Beniao

 文章出处:http://beniao.cnblogs.com/  或  http://www.cnblogs.com/

 

[搜索引擎]各搜索引擎搜索结果的获取

mikel阅读(753)

用http的get方法,构造要查询的url,get下来,分析结果页面即可
首先是构造url,以下是一些示例,主要看清楚?号后面的参数所代表的意思即可:
http://www.google.cn/search?num=100&&q=%E5%85%83%E6%90%9C%E7%B4%A2&start=10

http://www.baidu.com/s?wd=%D4%AA%CB%D1%CB%F7&rn=100&pn=10  //第二页pn

http://www.yahoo.cn/s?p=%E5%85%83%E6%90%9C%E7%B4%A2&b=10  //第二页b

http://search.yahoo.com/search?n=100&p=%E5%85%83%E6%90%9C%E7%B4%A2&b=101

http://cnweb.search.live.com/results.aspx?q=%E5%85%83%E6%90%9C%E7%B4%A2&first=51  //第二页first=51

http://p.zhongsou.com/p?w=%D4%AA%CB%D1%CB%F7&b=3  //b=3表示第三页

http://www.soso.com/q?w=%D4%AA%CB%D1%CB%F7&num=20&pg=1 //第一页,每页20个
第二步是解释搜索结果页面:
<meta http-equiv="content-type" content="text/html;charset=gb2312">

Google
搜索结果个数的字符串前缀:约有<b> //获取个数用字符串定位的方式
搜索结果开始的标签:<div id=res> //也可以用字符串定位的方式,要准确就用查找标签定位的方式
 各个搜索结果的开始标签:<div class=g> //字符串定位的方式
 
  搜索结果的url在第一个<a target=_blank class=l>标签里头
  搜索结果的标题在<a></a>的标签之间

  搜索结果的摘要在接下来的<table><tr><td>标签里头直到<b>…<b><br>
  搜索结果的重写的url在<b>…<b><br>之后的<span>标签里头,格式为:url,一个空格,网页大小
  搜索结果的网页快照在接下来的<a class=fl>的标签里头,属性中有url,标签之间有网页快照文字
  接下来还有类似网页等,都在<a class=fl>标签里头

 各个搜索结果的结束标签是</td></tr></table></div>

………………….

相关搜索的开始标签:<p class=e>
在接下来的各个<a></a>标签之间的内容就是相关搜索的内容
直到标签<br clear=all>就可以结束了

 

Baidu
搜索结果个数的字符串前缀:<td align=\"righ,在定位该字符串后,直到</td>,即在这个td标签之内含有的字符串包含相关网页数和用时
搜索结果开始的标签:<DIV id=ScriptDiv></DIV>
 各个搜索结果的开始标签:<table

  搜索结果的url在第一个<a target=_blank class=l>标签里头
  搜索结果的标题在<a></a>的标签之间,以<br>标签结束
  
  搜索结果的摘要以<br>开始直到下一个<br>标签
  
  接下来的一行(<br>换行)的font标签中有搜索结果url的重写,一个空格,网页大小,网页时间
  在接下来会有一些<a>标签如百度快照,直到又一个<br>

 然后搜索结果的结束标签</table>

…………………….

导航条的开始标签:<br clear=all>
导航条的内容在开始标签之后的<div class="p"></div>标签之间
相关搜索在接下来的<div>标签之间的各个<a>标签之内
其他考虑:对于字符串的匹配可以利用kmp,注意到匹配搜索结果各部分的时候所用到的模式字符串的最大前缀字符串最多是一个字符,这样可以避免求取最大前缀字符串从而提高效率;如果要精确地匹配还需要弄两个函数,一个用来构造标签,一个用来读取标签之间的文本。