sobaby博客

学习中
数据加载中……

2009年1月20日

ASP.NET MVC :MVC页面验证与授权

 在ASP.NET WebForm的架构下,我们可以通过一定的配置即可实现用户身份验证和授权。特别是在ASP.NET 2.0的Membership功能的支撑下,可以做到更加简洁可复用的用户验证系统。通过web.config可以做到对页面或目录对不同用户身份可见性的定制,但是它是基于物理文件和目录。而在ASP.NET MVC架构下,用户访问的每一个页面在磁盘中并没有一个固定的物理文件,它是通过Controller控制数据与视图的组合来生成HTML代码,进而向客户端输出。那么我们该如何来复用已有的表单验证授权机制呢?

在MVC中,请求的功能入口是Controller相应的Action函数,我们可以在函数执行前去控制请求权限。在ASP.NET MVC Preview 2后,提供了一个机制让我们可以对Action的AOP拦截,这个接口定义如下:           public interface IActionFilter

以下为引用的内容:
         {
             void OnActionExecuted(ActionExecutedContext filterContext);
             void OnActionExecuting(ActionExecutingContext filterContext);
             void OnResultExecuted(ResultExecutedContext filterContext);
             void OnResultExecuting(ResultExecutingContext filterContext);
         }

我们有两种方式来实现拦截,一种我们可以通过定义Attribute来实现拦截的功能,在System.Web.Mvc程序集中有一个ActionFilterAttribute抽象类,通过重写这个抽象类的这些虚方法,我们就可以实现对特定的执行过程进行拦截。

另一种方法,我们注意到Controller这个类也实现了IActionFilter这个接口,并且也提供了这四个函数的虚拟方法定义。框架内部,在调用Action方法的时候同时来调用这些拦截方法。具体的可以参考:ControllerActionInvoker 这个类的实现,所有的Action的调用都在这个类当中被实现。所以我们只要重写Controller里这四个虚方法,也可完成本Controller面的所有Action的拦截。

在这里,我也找到了国外友人已经实现好的基于角色的MVC权限控制的方案。

 

 

自定义了两个自定义Attribute,分别为:RequiresAuthenticationAttribute和RequiresRoleAttribute。通过这两个Attribute来可以作用于Class和Method,用标记哪些Controller或Action需要登录后,或者需要拥有哪些角色才能执行。如果用户没有拥有访问当然Controller或Action权限的时候,就会自动被重定向到登录页面去。下面是两个类的定义:

以下为引用的内容:
         /// <summary>
        /// Checks the User's authentication using FormsAuthentication
        /// and redirects to the Login Url for the application on fail
        /// </summary>
        [RequiresAuthentication]
        public class RequiresAuthenticationAttribute : ActionFilterAttribute
        {
            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                //redirect if not authenticated
                if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
                {
                    //use the current url for the redirect
                    string redirectOnSuccess = filterContext.HttpContext.Request.Url.AbsolutePath;
                    //send them off to the login page
                    string redirectUrl = string.Format("?ReturnUrl={0}", redirectOnSuccess);
                    string loginUrl = FormsAuthentication.LoginUrl + redirectUrl;
                    filterContext.HttpContext.Response.Redirect(loginUrl, true);
                }
            }
        }

posted @ 2009-01-20 14:01 sobaby 阅读(648) 评论(3) 编辑
一起学习MVC(4)-entity SQL语句

 

Entity SQL 是类似于 SQL 的、与存储无关的语言。Entity SQL 旨在基于 实体数据模型 (EDM) 查询和处理对象的多功能对象图形。下面我们和SQL语句对比着来学习。ESQL语句是不区分大小写的。

它支持以下功能 (摘在网上)

  支持与不支持的查询:

  from子句

  in和exists

  union, intersect, except来表示并交差集

  join表达式

  支持p.Address.City 这种级联查询

  不支持*操作

  group by时也要select k from T as t group by (t.x + t.y) as k这种方式

  

 

 

 首先从select开始:

   我们常见的SQL语句是:Select * from members

                    对应的ESQL语句就是:Select value m from members as m

                    这里的value是必须的 上面的语句也可以这么写:select value members  from members

如果选择某一字段呢?select userid from members 

               对应的ESQL语句就是:select value m.userid from members as m; 

 

如果要选择一个以上的字段就和上面的不一样,ESQL语句里必须要使用row函数,TSQL:select userid from members 

               对应的ESQL语句就是:select value row(m.userid,m.username) from members as m;  

 

我们再看下面带where语句的标准SQL:select * from members where userid=1

                    对应的ESQL语句就是:select value m from members as m where m.userid=1

                    看到了区别了吧。

那我们下面再写一个带top的SQL语句:select top 10 * from members where userid>0

                    对应的ESQL语句就是:select value top(10) m from members as m where m.userid>0 

下面继续写一个ORDER BY 的语句:select * from members order by userid desc

                    对应的ESQL语句就是:SELECT value m from members as m order by m.userid desc

如果有多个条件呢?select * from members order by userid desc,username desc

                    对应的ESQL语句就是:select value m from members as m order by m.userid desc,m.username desc

是不是觉得很EZ??

 

下面再将一个分页的,分页规则按照USERID降序分页,每页10条。

          在MYSQL里面如果要分页的话就可以用下面的SQL

               select * from members order by userid desc limit,10,10

               其中第一个参数就是从开始的行号,第二个参数是要获取的最多的记录数

          如果我们要在SQLSERVER里面的话就要这么写了

   select top 10 * from members where userid<(select min(userid) from (select top 10 userid from members order by userid desc) as u) order by userid desc

  呵呵,是不是太复杂了??如果我们要在ESQL里面分页怎么办??这时可以使用两个函数skip 和 limit,看一下下面的写法:

select value m from members as m order by m.userid desc skip(10) limit(10)

这里的skip和limit不能脱离order by 子句,skip的意思是说跳过多少行,limit的意思就是选中行的数目,两个函数也可以单独使用,比如

select value m from members as m order by m.userid desc skip(10) 跳过前10行,返回从第11行(包含第11行)以后的所有记录

select value from members as m order by m.userid desc limit(10) 显示前10行记录和TOP的作用是一样的。

需要注意的一点就是:skip和top如果同时使用的话,该查询表达式无效,必须重写查询表达式,使用limit函数

 

接下来我们一起学习group by  IN EXISTS语句,还是老思路对比着TSQL来学ESQL     。

  首先学习group by我们来看一下TSQL:select count(*) from orders group by userid

                    对应的ESQL语句:select value count(k) from Orders as t group by t.userid as k 

                  当然我们也可以使用其他聚合函数max,min,sum,avg,我这里就不一一演示了

                     group by 后面的字段不用别名可以不?答案是可以的            

 

再看下面的TSQL语句:select count(*),userid from orders group by userid

                    对应的ESQL语句:select value row(count(k),k) from orders as t group by t.userid as k

说了这些多,怎么执行ESQL语句呢?

 

            b2cDBEntities db = new b2cDBEntities();
            
string sql = "select value m from members as m";
            var query 
= db.CreateQuery<Members>(sql);

            
foreach (var k in query)
            {
                Console.WriteLine(k.UserID);
            }

 

当然也可以象执行ADO.NET对象一样执行

 

string ConnectionString = "metadata=res://*/StoreModel.csdl|res://*/StoreModel.ssdl|res://*/StoreModel.msl;provider=System.Data.SqlClient;provider connection string=';Data Source=.;Initial Catalog=b2cDB;Integrated Security=True;MultipleActiveResultSets=True';";
            
using (EntityConnection conn = new EntityConnection(ConnectionString))
            {
                conn.Open();
                EntityCommand cmd 
= conn.CreateCommand();
                
string sql = "select VALUE m from b2cDBEntities.members as m";
                cmd.CommandText 
= sql;
                EntityDataReader dr 
= cmd.ExecuteReader(CommandBehavior.SequentialAccess);
                
while (dr.Read())
                {
                    Console.WriteLine(dr[
"UserID"]);
                }
                dr.Close();
            }

posted @ 2009-01-20 12:50 sobaby 阅读(464) 评论(0) 编辑

2009年1月19日

一起学习MVC(3)-搭建项目

 

 

我们现在来搭建项目,项目使用经典的3层架构user interface,business ,dataaccess.

 首先我们搭建model模型层,这一层我们使用ado.net entity framework,我们选择类库,project名称就叫Model好了。

 

 

建好了之后,我们在项目里新增一个Ado.net Entity Data Model实体

 

然后按照向导完成

 

最终的试图

上面的图片是不是和我们数据库的关系图一致?如果不清楚数据库关系图,可以查看http://www.cnblogs.com/sobaby/archive/2009/01/19/1378178.html这篇文章

我们用editplus打开刚才建立的edmx文件(也可以用记事本打开)

你会发现该文件包含3个主要部分。

Conceptual Models (CSDL)

Storage Models (SSDL)

Mapping (MSL)

一般情况下,我们没必要手动修改EDMX -XML文件。可视化的EDM 设计器、Mapping Details窗口和Model Browser 窗口包含有上述3个部分,友好地显示整个Entity Data Model模型。

我们看一下模型浏览器(可以在上图里点右键 选中模型浏览器 来查看):

Model Browser窗口以可视的树形图显示概念模型和存储模型。

 

映射视图:

EDM设计提供了一个不错的映射窗口,将实体映射到表或者视图:

左边是数据库中的字段名和数据类型,右边是对应的c#里的属性名和数据类型

 

最后我们来看看StoreModel.design.cs文件

 

首先看到的是下面的代码

 

 

[assembly: global::System.Data.Objects.DataClasses.EdmSchemaAttribute()]
[assembly: 
global::System.Data.Objects.DataClasses.EdmRelationshipAttribute("StoreModel""FK_ORDERS_REFERENCE_MEMBERS""Members"global::System.Data.Metadata.Edm.RelationshipMultiplicity.ZeroOrOne, typeof(Model.Members), "Orders"global::System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Model.Orders))]
[assembly: 
global::System.Data.Objects.DataClasses.EdmRelationshipAttribute("StoreModel""FK_ORDERDET_REFERENCE_ORDERS""Orders"global::System.Data.Metadata.Edm.RelationshipMultiplicity.ZeroOrOne, typeof(Model.Orders), "OrderDetail"global::System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Model.OrderDetail))]
[assembly: 
global::System.Data.Objects.DataClasses.EdmRelationshipAttribute("StoreModel""FK_ORDERDET_REFERENCE_PRODUCTS""Products"global::System.Data.Metadata.Edm.RelationshipMultiplicity.ZeroOrOne, typeof(Model.Products), "OrderDetail"global::System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Model.OrderDetail))]

 

 

 

没有细看这个特性类,估计是对象实体的映射关系吧,查了一下MSDN,

第一个参数是此实体所参与的关联的命名空间名称。(不是很明白,命名空间不是Model吗?)

第二个参数是此实体所参与的关系的名称。(应该是数据库里对应的关系名吧)

第三个参数是关联一端的类型的角色名称。 (应该是主键表的表名)

第四个参数是RelationshipMultiplicity 的值,指示在关联一端的重数,例如一重或多重。(这个好理解,就是关系了,比如一对多,多对多)

第五个参数是关联一端的实体类型。(就是对应的实体名)

第六个参数关联另一端的类型的角色名称。(外键表表名)

第七个参数是RelationshipMultiplicity 的值,指示在关联另一端的重数,例如一重或多重。

第八个参数是关联另一端的实体类型。(对应的实体名)

 

据我的理解这就是实体的关系映射吧

 

我们继续看下面的代码了

public partial class b2cDBEntities : global::System.Data.Objects.ObjectContext

首先说一下System.Data.Objects和System.Data.Entity和System.Data.EntityClient这三个命名空间都是VS2008新加的,存在于System.Data.Entity.dll里(如果有错请指出来)

ObjectContext这个类提供以对象形式查询和使用实体数据的功能。

 

ObjectContext 类是以对象(这些对象是 Entity Data Model (EDM) 中定义的实体类型的实例)的形式与数据进行交互的主要类。ObjectContext 类的实例封装以下内容:

Entity Data Model 工具生成表示 EDM 概念性模型的对象层时,会从 ObjectContext 派生表示该模型的 EntityContainer 的类。有关更多信息,请参见对象服务概述(实体框架)

 

如果我们以后要用linq 2 Entities就必须要和这个类打交道。比如:

 

b2cDBEntities db = new b2cDBEntities();
            var query 
= from m in db.Members
                        select m;

 

这样就和我们在linq 2 sql里的是一样

posted @ 2009-01-19 11:53 sobaby 阅读(359) 评论(0) 编辑
一起学习MVC(2)-数据库设计

posted @ 2009-01-19 11:36 sobaby 阅读(780) 评论(5) 编辑
一起学习MVC(1)-搭建开发环境

  最近想学习MVC+ADO.NET Entity Framework,下面我们在实例中使用MVC框架和ADO.NET Entity Framework。

Entity Data Model是.NET Framework v3.5 SP1引入的新功能,比Linq To SQL更加强大的ORM,让开发人员只需要着眼于领域对象模型的开发,而不需要考虑它们是如何与关系数据库交互

  开发环境要求:VS2008+SP1+MVC beta1

               SP1补丁下载地址: http://download.microsoft.com/download/1/9/d/19d22169-a4b2-455f-8c28-ed137bd91487/VS2008SP1CHSX1512981.iso

                 大小:898.08MB

               MVC beta1下载地址:http://www.microsoft.com/downloads/details.aspx?FamilyId=A24D1E00-CD35-4F66-BAA0-2362BDDE0766&displaylang=en

 

  下载完了之后,先安装SP1补丁,然后安装MVC框架,装完之后我们可以在VS2008可以看到已安装的模板了

posted @ 2009-01-19 11:14 sobaby 阅读(357) 评论(0) 编辑

2008年12月23日

hello workflow


环境:

 

     VS2008直接可以创建workflow,不过在2005里也是可以创建工作流项目的,不过需要安装.NET Framework 3.0以上版本(包含3。0)

 下面说一下VS2005中安装workflow步骤,

          首先下载.NET Framework 3.0

        下载地址一:http://www.microsoft.com/downloads/details.aspx?displaylang=zh-cn&FamilyID=10CC340B-F857-4A14-83F5-25634C3BF043

                    不过这个要在线安装

       下载地址二:http://down1.tech.sina.com.cn/download/down_page/1152374400/28581.shtml

              

安装完了.NET Framework 3.0,我们还要装一个workflow扩展包,

          下载地址:http://www.microsoft.com/downloads/details.aspx?FamilyID=5d61409e-1fa3-48cf-8023-e8f38e709ba6&DisplayLang=zh-cn

安装完了Windows Workflow Foundation ,我们再打开VS2005,在项目类型里会发现多了一个workflow,截图如下:

 

 

点击打开右边的项目

我们可以看到工作流分两种:一种是顺序工作流,顾名思义   顺序工作流以固定的顺序从头到尾运行工作流,不要以为顺序工作流的流程是固定不变的,你也可以在其中加判断和循环。。适合条件,定义一组活动,按照固定的顺序走

  状态工作流,顾名思义,就是一个事件驱动式的工作流,因为状态的转变取决于发生的事件,依据目前所处的状态和所发生的事件,将决定是否要转移到另一个状态上,并在离开旧状态之后和进入新状态之前进行必要的处理操作。一旦状态机转移到一个代表结束的状态上,整个工作流的执行便算完成。适合条件,公司的审批流程等

下面我们要建立一个顺序工作流控制台应用程序

 

 

 

Sorry 上次写了一半因为有事就没写完

posted @ 2008-12-23 11:49 sobaby 阅读(147) 评论(0) 编辑
什么是工作流

工作流(Work Flow)就是工作流程的计算模型,即将工作流程中的工作如何前后组织在一起的逻辑和规则在计算机中以恰当的模型进行表示并对其实施计算。工作流要解决的主要问题是:为实现某个业务目标,在多个参与者之间,利用计算机,按某种预定规则自动传递文档、信息或者任务。简单地说,工作流就是一系列相互衔接、自动进行的业务活动或任务。我们可以将整个业务过程看作是一条河,其中流过的就是工作流。

  举现实中一个简单的例子,就拿报销单做例子,填写报销单,把填写好的报销单拿到财务室,财务室确认金额以及格式正确后,然后拿到老总那审批,审批通过与否都返回到财务室,如果审批通过财务通知报销人员前去领取报销金额,如果审批失败财务也通知报销人员

 这个工作流的流程:填写报销单=>财务=>老总=>财务=>报销人员

    这个工作流就被状态化了,并且可能要运行很长时间,比如说老总出差了且没带电脑,这时候这张报销单状态是否还有效呢?当然是有效的了,这些都需要保持当前状态一直到报销单处理完毕..

  上面的例子的工作流可能会耗费大部分时间在等待上,当工作流处于等待状态时,它是空闲的 不消耗资源的...

 总结:工作流是为了完成任务的必须步骤,工作流长时间的运行并且状态经常变化,需要人参与.现实中有很多工作流的例子,学生报名,饭店吃饭,合同审批,文档流传等.

posted @ 2008-12-23 11:25 sobaby 阅读(45) 评论(0) 编辑

2008年12月22日

学习WWF中

 从现在开始学习WWF, 要努力啦

posted @ 2008-12-22 16:35 sobaby 阅读(14) 评论(0) 编辑
关于回调函数


至于钩子函数,只是回调函数的一个特例。习惯上把与SetWindowsHookEx函数一起使用的回调函数称为钩子函数。也有人把利用VirtualQueryEx安装的函数称为钩子函数,不过这种叫法不太流行。

也可以这样,更容易理解:回调函数就好像是一个中断处理函数,系统在符合你设定的条件时自动调用。为此,你需要做三件事:

1.声明;

2.定义;

3.设置触发条件,就是在你的函数中把你的回调函数名称转化为地址作为一个参数,以便于系统调用。

声明和定义时应注意:回调函数由系统调用,所以可以认为它属于WINDOWS系统,不要把它当作你的某个类的成员函数。


二,回调函数、消息和事件例程


调用(calling)机制从汇编时代起已经大量使用:准备一段现成的代码,调用者可以随时跳转至此段代码的起始地址,执行完后再返回跳转时的后续地址。CPU为此准备了现成的调用指令,调用时可以压栈保护现场,调用结束后从堆栈中弹出现场地址,以便自动返回。借堆栈保护现场真是一项绝妙的发明,它使调用者和被调者可以互不相识,于是才有了后来的函数和构件。

此调用机制并非完美。回调函数就是一例。函数之类本是为调用者准备的美餐,其烹制者应对食客了如指掌,但实情并非如此。例如,写一个快速排序函数供他人调用,其中必包含比较大小。麻烦来了:此时并不知要比较的是何类数据--整数、浮点数、字符串?于是只好为每类数据制作一个不同的排序函数。更通行的办法是在函数参数中列一个回调函数地址,并通知调用者:君需自己准备一个比较函数,其中包含两个指针类参数,函数要比较此二指针所指数据之大小,并由函数返回值说明比较结果。排序函数借此调用者提供的函数来比较大小,借指针传递参数,可以全然不管所比较的数据类型。被调用者回头调用调用者的函数(够咬嘴的),故称其为回调(callback)。

回调函数使程序结构乱了许多。Windows API 函数集中有不少回调函数,尽管有详尽说明,仍使初学者一头雾水。恐怕这也是无奈之举。

无论何种事物,能以树形结构单向描述毕竟让人舒服些。如果某家族中孙辈又是某祖辈的祖辈,恐怕无人能理清其中的头绪。但数据处理之复杂往往需要构成网状结构,非简单的客户/服务器关系能穷尽。

Windows 系统还包含着另一种更为广泛的回调机制,即消息机制。消息本是 Windows 的基本控制手段,乍看与函数调用无关,其实是一种变相的函数调用。发送消息的目的是通知收方运行一段预先准备好的代码,相当于调用一个函数。消息所附带的 WParam 和 LParam 相当于函数的参数,只不过比普通参数更通用一些。应用程序可以主动发送消息,更多情况下是坐等 Windows 发送消息。一旦消息进入所属消息队列,便检感兴趣的那些,跳转去执行相应的消息处理代码。操作系统本是为应用程序服务,由应用程序来调用。而应用程序一旦启动,却要反过来等待操作系统的调用。这分明也是一种回调,或者说是一种广义回调。其实,应用程序之间也可以形成这种回调。假如进程 B 收到进程 A 发来的消息,启动了一段代码,其中又向进程 A 发送消息,这就形成了回调。这种回调比较隐蔽,弄不好会搞成递归调用,若缺少终止条件,将会循环不已,直至把程序搞垮。若是故意编写成此递归调用,并设好终止条件,倒是很有意思。但这种程序结构太隐蔽,除非十分必要,还是不用为好。

至于钩子函数,只是回调函数的一个特例。习惯上把与SetWindowsHookEx函数一起使用的回调函数称为钩子函数。也有人把利用VirtualQueryEx安装的函数称为钩子函数,不过这种叫法不太流行。

也可以这样,更容易理解:回调函数就好像是一个中断处理函数,系统在符合你设定的条件时自动调用。为此,你需要做三件事:

1.声明;

2.定义;

3.设置触发条件,就是在你的函数中把你的回调函数名称转化为地址作为一个参数,以便于系统调用。

声明和定义时应注意:回调函数由系统调用,所以可以认为它属于WINDOWS系统,不要把它当作你的某个类的成员函数。


二,回调函数、消息和事件例程


调用(calling)机制从汇编时代起已经大量使用:准备一段现成的代码,调用者可以随时跳转至此段代码的起始地址,执行完后再返回跳转时的后续地址。CPU为此准备了现成的调用指令,调用时可以压栈保护现场,调用结束后从堆栈中弹出现场地址,以便自动返回。借堆栈保护现场真是一项绝妙的发明,它使调用者和被调者可以互不相识,于是才有了后来的函数和构件。

此调用机制并非完美。回调函数就是一例。函数之类本是为调用者准备的美餐,其烹制者应对食客了如指掌,但实情并非如此。例如,写一个快速排序函数供他人调用,其中必包含比较大小。麻烦来了:此时并不知要比较的是何类数据--整数、浮点数、字符串?于是只好为每类数据制作一个不同的排序函数。更通行的办法是在函数参数中列一个回调函数地址,并通知调用者:君需自己准备一个比较函数,其中包含两个指针类参数,函数要比较此二指针所指数据之大小,并由函数返回值说明比较结果。排序函数借此调用者提供的函数来比较大小,借指针传递参数,可以全然不管所比较的数据类型。被调用者回头调用调用者的函数(够咬嘴的),故称其为回调(callback)。

回调函数使程序结构乱了许多。Windows API 函数集中有不少回调函数,尽管有详尽说明,仍使初学者一头雾水。恐怕这也是无奈之举。

无论何种事物,能以树形结构单向描述毕竟让人舒服些。如果某家族中孙辈又是某祖辈的祖辈,恐怕无人能理清其中的头绪。但数据处理之复杂往往需要构成网状结构,非简单的客户/服务器关系能穷尽。

Windows 系统还包含着另一种更为广泛的回调机制,即消息机制。消息本是 Windows 的基本控制手段,乍看与函数调用无关,其实是一种变相的函数调用。发送消息的目的是通知收方运行一段预先准备好的代码,相当于调用一个函数。消息所附带的 WParam 和 LParam 相当于函数的参数,只不过比普通参数更通用一些。应用程序可以主动发送消息,更多情况下是坐等 Windows 发送消息。一旦消息进入所属消息队列,便检感兴趣的那些,跳转去执行相应的消息处理代码。操作系统本是为应用程序服务,由应用程序来调用。而应用程序一旦启动,却要反过来等待操作系统的调用。这分明也是一种回调,或者说是一种广义回调。其实,应用程序之间也可以形成这种回调。假如进程 B 收到进程 A 发来的消息,启动了一段代码,其中又向进程 A 发送消息,这就形成了回调。这种回调比较隐蔽,弄不好会搞成递归调用,若缺少终止条件,将会循环不已,直至把程序搞垮。若是故意编写成此递归调用,并设好终止条件,倒是很有意思。但这种程序结构太隐蔽,除非十分必要,还是不用为好。

 


利用消息也可以构成狭义回调。上面所举排序函数一例,可以把回调函数地址换成窗口 handle。如此,当需要比较数据大小时,不是去调用回调函数,而是借 API 函数 SendMessage 向指定窗口发送消息。收到消息方负责比较数据大小,把比较结果通过消息本身的返回值传给消息发送方。所实现的功能与回调函数并无不同。当然,此例中改为消息纯属画蛇添脚,反倒把程序搞得很慢。但其他情况下并非总是如此,特别是需要异步调用时,发送消息是一种不错的选择。假如回调函数中包含文件处理之类的低速处理,调用方等不得,需要把同步调用改为异步调用,去启动一个单独的线程,然后马上执行后续代码,其余的事让线程慢慢去做。一个替代办法是借 API 函数 PostMessage 发送一个异步消息,然后立即执行后续代码。这要比自己搞个线程省事许多,而且更安全。

如今我们是活在一个 object 时代。只要与编程有关,无论何事都离不开 object。但 object 并未消除回调,反而把它发扬光大,弄得到处都是,只不过大都以事件(event)的身份出现,镶嵌在某个结构之中,显得更正统,更容易被人接受。应用程序要使用某个构件,总要先弄清构件的属性、方法和事件,然后给构件属性赋值,在适当的时候调用适当的构件方法,还要给事件编写处理例程,以备构件代码来调用。何谓事件?它不过是一个指向事件例程的地址,与回调函数地址没什么区别。

不过,此种回调方式比传统回调函数要高明许多。首先,它把让人不太舒服的回调函数变成一种自然而然的处理例程,使编程者顿觉气顺。再者,地址是一个危险的东西,用好了可使程序加速,用不好处处是陷阱,程序随时都会崩溃。现代编程方式总是想法把地址隐藏起来(隐藏比较彻底的如 VB 和 Java),其代价是降低了程序效率。事件例程(?)使编程者无需直接操作地址,但并不会使程序减速。
(例程似乎是进程的台湾翻译。)


三,精妙比喻:回调函数还真有点像您随身带的BP机:告诉别人号码,在它有事情时Call您。

回调用于层间协作,上层将本层函数安装在下层,这个函数就是回调,而下层在一定条件下触发回调,例如作为一个驱动,是一个底层,他在收到一个数据时,除了完成本层的处理工作外,还将进行回调,将这个数据交给上层应用层来做进一步处理,这在分层的数据通信中很普遍。其实回调和API非常接近,他们的共性都是跨层调用的函数。但区别是API是低层提供给高层的调用,一般这个函数对高层都是已知的;而回调正好相反,他是高层提供给底层的调用,对于低层他是未知的,必须由高层进行安装,这个安装函数其实就是一个低层提供的API,安装后低层不知道这个回调的名字,但它通过一个函数指针来保存这个回调,在需要调用时,只需引用这个函数指针和相关的参数指针。??? 其实:回调就是该函数写在高层,低层通过一个函数指针保存这个函数,在某个事件的触发下,低层通过该函数指针调用高层那个函数。



软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础。
 
对于不同类型的语言(如结构化语言和对象语言)、平台(Win32、JDK)或构架(CORBA、DCOM、WebService),客户和服务的交互除了同步方式以外,都需要具备一定的异步通知机制,让服务方(或接口提供方)在某些情况下能够主动通知客户,而回调是实现异步的一个最简捷的途径。

posted @ 2008-12-22 16:33 sobaby 阅读(133) 评论(4) 编辑

2008年11月13日

ASP.net:水晶报表的5种表格设计模式

与国外不同,国内的报表多以表格为主,所以用水晶报表实现表格虽然不太方便,但必须面对!
组成表格的框线分为上框线、下框线、左框线、右框线、内部横框线、内部竖框线六种,而上框线、下框线、内部横框线又属于横框线,左框线、右框线、内部竖框线又属于竖框线。在水晶报表里可以通过线、框、对象边框来实现,且听我慢慢道来……

目录

一、准线协助
1. 对象未动,准线先行。
2. 使用准线对齐表格的框线。
3. 使用准线对齐行、列。
4. 使用准线定位行、列。
5. 使用准线调整列宽和行高。

二、多节框
1. 绘制表格的外部框线。
2. 绘制表格的内部横框线。
3. 绘制表格的内部竖框线。

三、节内线
1. 将表格的标题行和数据行分离,分别绘制表格框线。
2. 绘制标题行部分的表格框线。
3. 绘制数据行部分的表格框线。

四、多节定位
1. 防止可变长度的对象相互覆盖。
2. 防止数据库字段和下框线相互覆盖。

五、对象边框
1. 用对象边框模拟表格的框线。
2. 将表格的标题行和数据行分离,并用对象边框模拟表格框线。
3. 用文本对象的边框模拟标题行部分的表格框线。
4. 用数据库字段的边框模拟数据行部分的表格框线。

一、准线协助

1. 对象未动,准线先行。

准线是一些非打印线条,可以将其放在"设计"选项卡和"预览"选项卡上的任何位置以帮助对齐。它们具有对齐属性,使对象自动与其对齐。对齐功能不仅使您能够轻松加精确地对齐报表对象,而且还可以同时对多个对象重新定位并调整大小。

(1) 显示、插入、移动和删除准线
① 显示准线
选择"视图"菜单上的"准线"命令。
使用"设计"命令可以在"设计"选项卡中显示或隐藏准线。使用"预览"命令可以在"预览"选项卡中显示或隐藏准线。
注意:在"预览"选项卡上,必须选择一个对象才能看到准线。
② 插入准线
在"设计"或"预览"选项卡中,单击顶部的标尺可以插入垂直准线;单击左侧的标尺可以插入水平准线。
注意:每条准线均与其起始标尺位置上的一个箭头相连。
③ 移动准线
要将准线放置到目标位置,请将其箭头沿标尺拖至相应的位置上。
④ 删除准线
要删除准线,将其箭头拖出标尺。

注意:如果选择了"网格线对齐"选项,则只能以网格为单位增量插入或移动准线。要去除这个限制,右击报表的空白处,在快捷菜单取消"与网格对齐"命令。

(2) 使对象与准线对齐
① 要使对象与准线对齐,将报表对象拖至准线之上,直至对象边缘落在准线之上。
② 可以将多个对象与同一条准线对齐,以同时对多个对象重新定位并调整大小。
③ 可以使对象的顶部或底部与水平准线对齐,也可以使对象的左侧、右侧或中垂线(垂直平分对象的不可见线条)与垂直准线对齐。
④ 如果是对齐多个对象,要用对象的同一侧对齐准线。

(3) 使用准线定位对象
使一个或多个对象与某条准线对齐之后,通过移动该准线即可同时移动所有对象。要移动准线,沿标尺拖动其箭头。
注意:当移动准线时,所有与之对齐的对象也一起移动。但如果移动的是与准线对齐的对象,该准线并不随之移动。

(4) 使用准线调整对象大小
使用两条准线可以对两个或多个大小(高度或宽度)相同或不同的对象调整大小。两种情况下使用两个准线调整大小的过程是一样的。
① 创建一个准线。
② 将对象的一侧与该准线对齐。
③ 在该对象的另一侧创建第二条准线。
注意:此准线不应接触到该对象。
④ 单击对象以激活大小调整控柄。
⑤ 将调整大小控柄拖至第二条准线,让对象与该准线对齐。
⑥ 对于每一个要与这两条准线对齐的其他对象,重复执行第 2 步到第 5 步。
⑦ 如果对象不是所需要的大小,则拖动准线之一或全部准线直到对象大小正确为止。

2. 使用准线对齐表格的框线。

注意:如果选择了"网格线对齐"选项,则只能以网格为单位增量插入或移动准线。要去除这个限制,右击报表的空白处,在快捷菜单取消"与网格对齐"命令。

(1) 插入框
① 在"设计"选项卡中,通过单击左侧的标尺插入两条水平准线。一上一下,分别对应表格的上框线和下框线。
② 通过单击顶部的标尺插入两条垂直准线。一左一右,分别对应表格的左框线和右框线。
③ 插入框,并调整大小,使框的四边分别对齐四条准线。

(2) 插入竖线
① 在"设计"选项卡中,通过单击左侧的标尺插入两条水平准线。一上一下,分别对应竖框线的上下顶点。
② 单击顶部的标尺插入一条垂直准线,用于在水平方向上辅助定位竖框线。
③ 插入竖线,并对齐垂直准线。
④ 调整竖线的大小,使之上下两侧分别对齐上下两条水平准线。

(3) 插入横线
① 在"设计"选项卡中,通过单击顶部的标尺插入两条垂直准线。一左一右,分别对应横框线的左右顶点。
② 单击左侧的标尺插入一条水平准线,用于在垂直方向上辅助定位横框线。
③ 插入横线,并对齐水平准线。
④ 调整横线的大小,使之左右两侧分别对齐左右两条垂直准线。

3. 使用准线对齐行、列。
(1) 对齐行
① 在"设计"选项卡中,单击左侧的标尺插入水平准线。
② 将同一行上的数据字段或者表格标题的底部与水平准线对齐。

(2) 对齐列
① 在"设计"选项卡中,单击顶部的标尺插入垂直准线。
② 将同一列上的数据字段和表格标题的左侧与垂直准线对齐。

4. 使用准线定位行、列。
每条准线均与其起始标尺位置上的一个箭头相连。将行与水平准线对齐或者将列与垂直准线对齐后,沿标尺拖动其箭头,就可以移动行和列。

5. 使用准线调整列宽和行高。
(1) 调整列宽
① 在"设计"选项卡中,单击顶部的标尺插入垂直准线1,并将同一列上的数据字段和表格标题的左侧与垂直准线1对齐。
② 在列的右侧,单击顶部的标尺插入垂直准线2。
注意:此准线不应接触到数据字段和表格标题。
④ 按住 Ctrl 连续单击,以选中同一列上的数据字段和表格标题,在最后一个被选中的对象上会出现大小调整控柄。
⑤ 将调整大小控柄拖至垂直准线2,让列与该准线对齐。
⑥ 如果列不是所需要的大小,则拖动准线之一或全部准线直到列大小正确为止。

(2) 调整行高
① 在"设计"选项卡中,单击左侧的标尺插入水平准线1,并将同一行上的数据字段或者表格标题的顶部与水平准线1对齐。
② 在行的底部,单击左侧的标尺插入水平准线2。
注意:此准线不应接触到数据字段或者表格标题。
④ 按住 Ctrl 连续单击,以选中同一行上的所有数据字段,在最后一个被选中的对象上会出现大小调整控柄。
⑤ 将调整大小控柄拖至水平准线2,让行与该准线对齐。
⑥ 如果行不是所需要的大小,则拖动准线之一或全部准线直到行大小正确为止。

二、多节框

1. 绘制表格的外部框线。
(1) 插入框,跨越"页眉"和"详细资料"两节。
(2) 调整"详细资料"的大小,使其底部与表格的下框线对齐。

2. 绘制表格的内部横框线。
(1) 在"详细资料"插入横线。
(2) 在垂直方向上移动横线,使其与"详细资料"顶部对齐。
(3) 调整横线的大小,使其左右两端与外部框线对齐。

3. 绘制表格的内部竖框线。
(1) 根据列的多少在报表插入相应数量的竖线。
(2) 在水平方向上移动竖线,使其恰好处于两列之间。
(3) 调整竖线的大小,使其上下两端与外部框线对齐。

问题:如果你在"报表页脚"插入了子报表,那么表格的竖框线和下框线将出现在子报表上,显示效果混乱不戡!所以就有了下面的……

三、节内线

1. 将表格的标题行和数据行分离,分别绘制表格框线。
(1) 将表格的标题行放在"页眉",框线绘制不超出"页眉"。
(2) 将表格的数据行放在"详细资料",框线绘制不超出"详细资料"。

2. 绘制标题行部分的表格框线。
(1) 在"页眉"中,用线绘制表格的上框线,表格的内部横框线,表格左框线、右框线、内部竖框线的标题栏部分。
绘制横框线和竖框线请参考本文的:二、多节框 2. 绘制表格的内部横框线。 3. 绘制表格的内部竖框线。
(4) 调整"页眉"的大小,使其底部与表格的内部横框线对齐。

3. 绘制数据行部分的表格框线。
(1) 在"详细资料"中,用线绘制表格的下框线,表格左框线、右框线、内部竖框线的标题栏部分。
绘制横框线和竖框线请参考本文的:二、多节框 2. 绘制表格的内部横框线。 3. 绘制表格的内部竖框线。
(2) 调整"详细资料"的大小,使其底部与表格的下框线对齐。

问题:如果数据行的行高不固定,即数据库字段设置为"可以扩大",以根据数据大小自动垂直扩展。则数据将超出表格的下框线,而且竖线无法自动垂直扩展,显示效果非常难看!所以就有了下面的……

四、多节定位

1. 防止可变长度的对象相互覆盖。
创建多个节,并将对象放在可变长度对象所在节的下面的节,可消除这种套印问题。

2. 防止数据库字段和下框线相互覆盖。
(1) 在"详细资料"创建两个节:"详细资料 a"、"详细资料 b"。
(2) 在"详细资料 a"中放置数据库字段。
(3) 在"详细资料 b"中,用线绘制表格的下框线,并调整"详细资料b"的大小,使其底部与下框线对齐。
(4) 在"详细资料"用线绘制竖框线,并跨越"详细资料 a"和"详细资料 b"两节。

五、对象边框

1. 用对象边框模拟表格的框线。
(1) 选择报表对象并从"格式"菜单或右击菜单中选择格式命令,进入"格式设置编辑器"对话框。
(2) 选择"边框"选项卡。
(3) 将左、右、顶部、底部框线的"线型"设置为"单线"。

2. 将表格的标题行和数据行分离,并用对象边框模拟表格框线。
(1) 将表格的标题行放在"页眉",用文本对象的边框模拟标题行的框线。
(2) 将表格的数据行放在"详细资料",用数据库字段的边框模拟数据行的框线。

3. 用文本对象的边框模拟标题行部分的表格框线。
(1) 最左侧文本对象边框的左、右、顶部、底部框线的"线型"设置为"单线"。
(2) 其它文本对象的边框的右、顶部、底部框线的"线型"设置为"单线"。
(3) 将各个文本对象从左到右紧密相连, 则顶部框线就组成了表格的上框线,底部框线就组成了表格的内部横框线,左、右框线便是表格左框线、右框线、内部竖框线的标题栏部分。
(4) 调整"页眉"的大小,使其底部与表格的内部横框线对齐。

4. 用数据库字段的边框模拟数据行部分的表格框线。
(1) 最左侧文本对象边框的左、右、底部框线的"线型"设置为"单线"。
(2) 其它文本对象的边框的右、底部框线的"线型"设置为"单线"。
(3) 将各个文本对象从左到右紧密相连,底部框线便组合成了表格的下框线,左、右框线便是表格左框线、右框线、内部竖框线的标题栏部分。
(4) 调整"详细资料"的大小,使其底部与表格的下框线对齐。

posted @ 2008-11-13 20:53 sobaby 阅读(260) 评论(0) 编辑