随笔 - 11, 文章 - 18, 评论 - 1, 引用 - 0
数据加载中……

2008年2月26日

asp.net中自定义日期控件方法(转载)

在最近开发的一个asp.net 2.0系统上,要给业务部门出数据,在查询界面上经常要用到一个日期选择框,2周前看了www.asp.net上发布的一本书Asp.net Data Tutorial,对asp.net的三层结构开发方式有了一定了解(DataSet, ObjectDataStore)。上周,下了两个asp.net上的范例程序代码,一个是Duwamish7,另一个是petshop3.0(j2ee平台上petstore的竞争版)。网上也有这两个程序的代码分析,Duwamish7采用自定义DataSet+sqlconnection封装存储过程的方式,petshop3.0则采用常见的三层结构,数据访问层DAL通过ORM映射,将结果表的一行映射成一个对象(比如Customer,Product,Order等,考虑了分页机制),业务逻辑层BLL处理参数校验等业务逻辑的实现,展现层处理数据的显示(内容和形式的分离)。看过代码后,感觉asp.net里边的自定义控件的功能还是很强大的。比起j2ee里边的标签库,代码复用程度更高,尤其适合于做各种表单查询控件。比如根据业务逻辑,将各种输入框、下拉框组合起来,进行复用。

    本系统中要用到一个日期输入框,日期标题可变(操作日期、申请日期、确认日期、开始日期、截止日期....),可以为空或者必须输入,光标在输入框中时,自动弹出日期选择框。结合这些需求,封装了一个日期选择控件。

asp端代码
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CustomDate.ascx.cs" Inherits="Control_StartEndDate" %>

<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
<asp:TextBox ID="txtEndDate" runat="server" onfocus="setday(this)" Width="88px" ></asp:TextBox>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="txtEndDate"
     Enabled="false" Display="None">
</asp:RequiredFieldValidator>

asp对应的cs代码为:
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class Control_StartEndDate : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // Label1.Text是动态变化的,所以验证信息也相应动态生成
        RequiredFieldValidator1.ErrorMessage = Label1.Text + "不能为空!";
    }

    // 当前日期字符串值
    public string DateString
    {
        get
        {
            return txtEndDate.Text;
        }
    }

    // 日期的标题名称(操作日期、申请日期、确认日期、开始日期、截止日期....)
 public string DateTitle
    {
        set
        {
            //"截止日期";
            Label1.Text = value;
        }
    }

 

    // 日期是否允许为空,即是否启用RequiredFieldValidator
    public bool Nullable
    {
        set
        {
            //bool nullable = bool.Parse(value);
            if (value == true)
                RequiredFieldValidator1.Enabled = false;
            else
                RequiredFieldValidator1.Enabled = true;
        }
    }

    // 初始日期字符串文本
    public string InitialText
    {
        set
        {
            txtEndDate.Text = value;
        }
    }

    // 初始日期值
    public String InitialDate
    {
        set
        {
            //if (IsPostBack) return;

            string dateStr = "";
            DateTime dt = DateTime.Today; // 取当日日期

            // 取上一开放日的日期
            if ("LastOpenDay".Equals(value))
            {
                // 为简单起见,上一个开放日取前天(要精确日期则必须查询数据库)
                DateTime newdt = dt.AddDays(-2);
dateStr = newdt.ToString("yyyy-MM-dd");
            }
            else if ("Today".Equals(value))
            {
                dateStr = dt.ToString("yyyy-MM-dd");
            }
            else
            {
            }
           
            txtEndDate.Text = dateStr;
        }
    }
}

使用范例
<%@ Page Language="C#" MasterPageFile="~/MasterPage/Navigation.master" AutoEventWireup="true"
    CodeFile="share4areadis.aspx.cs" Inherits="report_share4birthsex" Theme="Harvest" %>

 

<%@ Register Src="../Control/FundCodeList.ascx" TagName="FundCodeList" TagPrefix="uc1" %>
<%@ Register Src="../Control/CustomDate.ascx" TagName="CustomDate" TagPrefix="uc2" %>
<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">

    <div>
        <uc1:FundCodeList ID="FundCodeList1" runat="server" ShowCustomItem="true" />
        <uc2:CustomDate ID="CustomDate1" runat="server" DateTitle="截止日期" Nullable="false"
            InitialDate="LastOpenDay" />
        <asp:Button ID="Button1" runat="server" Text="查询" OnClick="Button1_Click" />
        <asp:ValidationSummary ID="ValidationSummary1" runat="server" DisplayMode="BulletList"
            ShowMessageBox="true" ShowSummary="false" />
        <rsweb:ReportViewer ID="ReportViewer1" runat="server" Font-Names="Verdana" Font-Size="8pt"
Style="width: 100%; height: 120%">
            <LocalReport>
            </LocalReport>
        </rsweb:ReportViewer>
        <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetShare4AreaDis"
            TypeName="VM.BusinessRule.CustAnalysis">
            <SelectParameters>
                <asp:ControlParameter ControlID="FundCodeList1" Name="FundCode" PropertyName="FundCode"
                    Type="String" />
                <asp:ControlParameter ControlID="CustomDate1" Name="EndDate" PropertyName="DateString"
                    Type="String" />
            </SelectParameters>
        </asp:ObjectDataSource>
    </div>
</asp:Content>

 

具体看<uc2:CustomDate ID="CustomDate1" runat="server" DateTitle="截止日期" Nullable="false"
            InitialDate="LastOpenDay" />这行,DateTitle为日期标题,Nullable设置是否允许为空,InitialDate设置初始日期(当天,上一个开放日)
<asp:ValidationSummary ID="ValidationSummary1" runat="server" DisplayMode="BulletList"
            ShowMessageBox="true" ShowSummary="false" />为验证汇总,将各个控件中的验证信息汇总起来,统一显示(ShowMessageBox="true"表示用弹出消息框的形式显示验证信息)。

posted @ 2008-03-20 14:04 noahsky 阅读(47) | 评论 (0)编辑

【转载】分层开发思想与小笼包

作者:李天平
早上吃早点的时候,突发灵感,结合吃的小笼包和日常的开发经验来说一下分层的应用。

首先,谈一下什么是三层架构,所谓的三层开发就是将整个业务应用划分为表示层-业务逻辑层―数据访问层-数据库等,有的还要细一些,明确地将客户端的表示层、业务逻辑访问、和数据访问及数据库访问划分出来,十分有利于系统的开发,维护、部署和扩展。

软件要分层,其实总结一句话,是为了实现“高内聚、低耦合”。采用“分而治之”的思想,把问题划分开来各个解决,易于控制,易于延展,易于分配资源。  

这种三层结构有什么优势呢?  

1. 通过将整个系统分为不同的逻辑块,大大降低了应用系统开发和维护的成本。

三层结构将表示部分和业务逻辑部分按照客户层和应用服务器相分离,客户端和应用服务器、应用服务器和数据库服务器之间的通信以及异构平台之间的数据交换等都可以通过中间件或者相关程序来实现。当数据库或者应用服务器的业务逻辑改变时,客户端并不需要改变,反之亦然,从而大大提高了系统模块的复用性,缩短了开发周期,降低了维护费用。

趣味理解

假设饭店这样来做小笼包:

一层蒸饺,

一层肉包,

一层素包。

首先这种方式我们可以让做包子的分成不同的小组,来负责做不同的包子。当做好了每一种包子,他们可以很方便的知道,该放到哪个笼里,不至于挨个去查看该放在哪里,我们也很容易地将笼屉放到火上面去,而不会影响其他笼的加热。当我们要吃肉包或蒸饺时,我们也很容易就能找到我们所需要的,非常节省时间和体力。把肉包或蒸饺拿走或者增加,并不影响素包的加热。

如果我们把这些都放在一个大笼里,你觉得会怎么样?会不会很乱,维护起来很浪费时间,不但找起来麻烦,而其每次揭开锅都会对其他的有影响。  

2. 将数据访问和逻辑操作都集中到组件中,增强了系统的复用性。

如:将数据访问集中到数据访问层的组件中,从而减少了应用程序中的重复代码,每个需要访问数据库、表的窗体都使用相同的组件。

如:一些共性的逻辑操作都集中封装在逻辑层的组件中,每一个使用该方法的操作,可以共享来访问该组件。

趣味理解:

简单的理解,笼屉本身就是一个重用的例子,如果我们使用一次性的笼(像一次性筷子一样理解),会浪费多少人工来做啊(重复编写麻烦),并且会有很大的资源浪费(代码冗余),最后还要进行垃圾处理(后期维护)。并且,如果笼屉和炉火的锅按一种规范和标准(接口)来做,这样的笼屉不但可以在这里用,也可以拿到别的地方用。(虽然这样理解不太确切,简单的这样理解好了)  

3. 系统的扩展性大大增强。

模块化使得系统很容易在纵向和水平两个方向拓展:一方面可以将系统升级为更大、更有力的平台,同时也可以适当增加规模来增强系统的网络应用。由于摆脱了系统同构性的限制,使得分布数据处理成为可能。在扩充或修改功能时,基本不会破坏原有结构的稳定性。

趣味理解:

随着顾客的需要,我们现在需要增加新品种,如蒸馒头,蒸地瓜,那么直接可以增加一层笼就是了,对其他笼不会有什么影响。  

三层结构在营造企业竞争优势中的作用主要体现在模块化设计使得用户在现有结构的基础上实现了系统扩展,从而提高企业信息化的速度和业务水平;同时三层结构中中间件的出现使得用户可以直接从市场上选择合适的产品来构建系统,大大降低了开发周期和开发费用。  

但分层结构也有缺点也不是越多越好,那样管理很多层会比较麻烦,运行效率可能比较低。所以,一个具备良好层次结构的系统,其层的数目要恰到好处才行。  

posted @ 2008-03-20 14:03 noahsky 阅读(11) | 评论 (0)编辑

【转载】.Net平台开发的技术规范与实践精华总结

一、代码规范

良好的代码风格来自于同一的代码规范。风格良好的代码不仅具备可读性和可维护性,同时也给人行云流水、赏心悦目之快感。

据Microsoft公司统计,基于微软平台的开发中,有70-80%的印度工程师在完成同类算法或者模块时,使用的代码基本一致;而相同的调查中只有20%的中国工程师们是基本一致的。这说明我们的代码生产过程亟待规范。

实义命名

类型、变量、常量、方法等标识符一律采用对应的英文实义;如果涉及到两个独立的实义单词,则中间用下划线间隔或者单词首字母大写(两种方式都可以);如果标识符的长度超过了30个字母,则基本上以英文单词发音的重读音节取选出三个字母,如Repeater用rpt,Management用mgt。

大小写规则

目前一般有两种大小写规则:

Pascal大小写形式,所有单词第一个字母大写,其他字母小写。

Camel大小写形式,除了第一个单词,所有单词第一个字母大写,其他字母小写。

n         类名使用Pascal大小写形式

public class HelloWorld(或者Hello_World,以下同,不再赘述)

{

 ...

}

n         方法使用Pascal大小写形式

public class HelloWorld()

{

 void SayHello(string name)

 {

  ...

 }

}

n         变量和方法参数使用Camel 大小写形式

public class HelloWorld()

{

 int totalCount = 0;

 void SayHello(string name)

 {

  string fullMessage = "Hello " + name;

  ...

 }

}

n         不要使用匈牙利方法来命名变量

以前,多数程序员喜欢把数据类型作为变量名的前缀而m_作为成员变量的前缀。例如: string m_sName;int nAge;

然而,这种方式在.NET编码规范中是不推荐的。所有变量都用Camel 大小写形式,而不是用数据类型和m_来作前缀。

用name,address,salary等代替nam,addr,sal。

别使用单个字母的变量象i,n,x 等。使用 index,temp等。用于循环迭代的变量例外:

如果变量只用于迭代计数,没有在循环的其他地方出现,允许用单个字母的变量命名,而不是另外取实义名。

文件名要和类名匹配,例如,对于类HelloWorld,相应的文件名应为helloworld.cs。

缩进和间隔

n         缩进用TAB,不用 SPACES。

n         注释需和代码对齐。

n         遵循VS2005的自动对齐规则,不要人为的调整。

n         用一个空行来分开代码的逻辑分组。

n         在一个类中,各个方法的实现体必须用空行间隔,大括弧“{}”需独立一行。

n         在每个运算符和括号的前后都空一格。如:

  If  ( showResult == true )

  {

   for  (  int i = 0; i < 10; i++ )

   {

    //

   }

  }

而不是:

  if(showResult==true)

  {

   for(int i= 0;i<10;i++)

   {

    //

   }

  }

良好的编程习惯

n         避免使用大文件。如果一个文件里的代码超过300~400行,必须考虑将代码分开到不同类中。

n         避免写太长的方法。一个典型的方法代码在1~30行之间。如果一个方法发代码超过30行,应该考虑将其分解为不同的方法。

n         方法名需能看出它作什么。别使用会引起误解的名字。如果名字一目了然,就无需用文档来解释方法的功能了。

n         一个方法只完成一个任务。不要把多个任务组合到一个方法中,即使那些任务非常小。

n         使用C# 的特有类型,而不是System命名空间中定义的别名类型。如:

              int age;

              string name;

              object contactInfo;

       而不是:

              Int16 age;

              String name;

              Object contactInfo;

这么做是基于如下两点原因:(1)规范性和一致性;(2)便于跨语言平台的移植。

n         别在程序中使用固定数值,用常量代替。别用字符串常数,尽量用资源文件。

n         避免使用很多成员变量,声明局部变量,并传递给方法。

n         不要在方法间共享成员变量,如果在几个方法间共享一个成员变量,那就很难知道是哪个方法在什么时候修改了它的值。必要时使用enum,别用数字或字符串来指示离散值。

n         别把成员变量声明为 public或 protected。都声明为private 而使用 public/protected 的Properties。

n         不在代码中使用具体的路径和驱动器名,使用相对路径,并使路径可编程。永远别设想你的代码是在"C:"盘运行。你不会知道,一些用户在网络或"Z:"盘运行程序。

n         应用程序启动时作些“自检”并确保所需文件和附件在指定的位置。必要时检查数据库连接,出现任何问题给用户一个友好的提示。

n         如果需要的配置文件找不到,应用程序需能自己创建使用默认值。如果在配置文件中发现错误值,应用程序要抛出错误,给出提示消息告诉用户正确值。错误消息需能帮助用户解决问题。

注释

n         别每行代码,每个声明的变量都做注释。在需要的地方注释。

n         可读性强的代码需要很少的注释,如果所有的变量和方法的命名都很有意义,会使代码可读性很强并无需太多注释。行数不多的注释会使代码看起来优雅。

n         如果因为某种原因使用了复杂艰涩的原理,必须为程序配备良好的文档和详细的注释。

n         对注释做拼写检查,保证语法和标点符号的正确使用。

 

二、数据库设计规范

表格分类与命名

n         数据表的分类

u       系统表   支撑业务模型的数据表,如流程模型、系统管理相关表。

u       业务表   产品提供的针对业务的通用功能模块相关表,如通用业务查询等。

u       用户表   用户二次开发使用的与具体业务相关的数据表。

n         数据表的命名

u       所有表格命名一律以字母“T”开头(Table),并且用实义单词以下划线“_”间隔。

u       系统表   系统表前缀为:TSYS_

u       业务表前缀为:TBIZ_

u       用户表由用户自行定义,但是建议不要与系统表和业务表的命名规则重复。

n         字段的命名

       字段的命名规则参照代码标识符的命名规则,但是注意避开数据库的保留字。比如不要采用这样的字段名:index,field,password,id,Oracle,SQL等等。

       对于涉及到技术核心的系统表,为了防止剖析,建议采用类似“F1,F2,F3……Fn”的方式命名。但是不要采用“F0”,因为这个名称在某些数据库中不被允许,比如Interbase。

索引的建立

n         索引是一把双刃剑,索引将提高查询的效率,但是却降低了insert/delete/update 的效率。

n         通常情况下,对数据的编辑频度和时限要求远远低于对数据库的查询要求,因此对于记录很多且频繁查询的数据表,必须建立索引。

n         大多数数据库为主键字段自动创建索引,注意为外键创建索引。

n         不要索引大字段,这样作会让索引占用太多的存储空间。

n         尽量不要索引频繁编辑的小型表。

n         identify字段不要作为表的主键与其它表关联,这将会影响到该表的数据迁移。如果考虑支持多数据库,建议主键采用程序生成的唯一值。

n         如果一个大型表需要频繁的做insert/delete/update操作,同时也需要做高并发量的查询,那么建议根据数据的访问频度对表作拆分,而后建立索引。

过程与函数

数据库厂商为了凸现自身的优势,都提供了丰富且个性化的过程与函数。

为了提升产品的伸缩性和数据无关性,请不要使用与特定数据库相关的过程与函数,也不推荐采用Store Procedure,建议使用应用服务器的中间层业务对象。

字段/域的定义

n         尽量避免使用Blob,如果一定要用,请不要索引blob,并且不要定义多个blob。

n         不要使用日期字段,改用字符串char(19)替代,如:2008-12-09 12:22:08。

n         对于确定长度的串,请固定字段类型的长度,如char(80),不要采用varchar。

n         对于值类型字段,请使用对应的数据库值类型,而不要用字符串。

三、Com和.Net互操作规范

.NET 技术已经成为微软平台的主流,但是在Win32时代开发了很多COM、DCOM组件,由于在开发COM组件时投入了大量的人力、财力,如何在.NET环境下重用这些COM组件就显得更有意义。

.NET支持运行时通过COM、COM+、本地WinAPI调用与未托管代码的双向互操作性,要实现互操作性,必须首先引入.NET Framework的 System.Runtime.InteropServices命名空间。

C#的语法为:

using System.Runtime.InteropServices;

1.NET访问API

.NET允许C#访问未托管的DLL的函数。如要调用Windows User32.dll的MessageBox函数:

int MessageBox(HWND hwnd,LPCTSTR lpText, LPCTSTR lpCaption,UINT uType)

可以声明一个具有DLLImport属性的static extern方法:

using System.Runtime.InteropServices;

[DllImport(“user32.dll”)]

static ertern int MessageBox(int hwnd,string text,string caption,int type);

然后在代码里面直接调用就可以了。这里要注意在调用返回字符串的API中使用StringBuilder对象。

2.NET访问COM组件

.NET调用COM组件比较容易,只要使用tlbimp.exe产生COM的装配形式的WarpClass,然后在.NET项目中调用即可。

注意COM的类型信息通过Type Library文件描述,.NET装配件是自描述的。Tlbimp的作用是从COM组件及其类型信息中产生自描述的装配件。

1.编写Com组件

编译生成一个ComAccount.dll。

2. 产生.NET可访问的包装类(assembly),使用TlbImp.exe产生.NET装配件。

TlbImp /out:NetAccount.dll ComAccount.dll

3.在.NET代码中访问

.NET代码只需引用NetAccount.dll,就可以像访问.NET的装配件一样访问COM组件。

四、异常处理

异常处理的原则

n         在应用程序级(线程级)错误处理器中处理所有的一般异常。遇到“意外的一般性错误”时,此刻错误处理器应该捕捉异常,给用户提示消息,在应用程序关闭或用户选择“忽略并继续”之前记录错误信息。

n         不必每个方法都用try-catch,当特定的异常可能发生时才使用。比如,当写文件时,处理异常FileIOException。

n         别写太大的 try-catch 模块。如果需要,为每个执行的任务编写单独的 try-catch 模块。这将有助于找出哪一段代码产生异常,并给用户发出特定的错误消息。

n         如果应用程序需要,可以编写自己的异常类。自定义异常不应从基类SystemException派生,而要继承于IApplicationException。

n         在开发阶段,不必在所有方法中捕捉一般异常。刻意的放纵异常,将帮助在开发周期发现大多数的错误。

异常处理的提示

n         不要捕捉了异常却什么也不做,看起来系统似乎在正常运行。如果这样隐藏了一个异常,将永远不知道异常到底是否发生,为什么发生。

n         发生异常时,给出友好的消息给用户。但要精确记录错误的所有可能细节,包括发生的时间,和相关方法,类名等。

n         永远别用像“应用程序出错”,“发现一个错误”等错误提示消息,而应给出类似“更新数据库失败,请确保登陆id和密码正确。”之类的具体消息。

n         显示错误消息时,还应提示用户如何解决问题。如:“更新数据库失败,请确保登陆id和密码正确。”,而不是仅仅说“更新数据库失败”。

n         显示给用户的消息要简短而友好。但要把所有可能的信息都记录下来,以助诊断问题。

异常处理的代码实例

推荐如下异常处理模式:

void ReadFromFile ( string fileName )

 {

  try

  {

   // 读文件.

  }

  catch (FileIOException ex)

  {

   // 记载异常日志

   // 重抛具有针对性的异常信息

   throw;

  }

 }

 

不推荐如下的异常处理模式:

void ReadFromFile ( string fileName )

 {

  try

  {

   // 读文件

  }

  catch (Exception ex)

  {

   // 捕捉一般异常将让我们永远不知道到底是文件错误还是其他错误

   // 隐藏异常将我们永远不知道有错误发生。

   return ""; 

  }

 }

 

五、对象实例的申请与释放

.Net平台的垃圾回收机制,可以自动的dispose不再引用的对象实例,所以很多开发人员并不主动释放申请的对象资源。事实上,在对象的生命周期结束之前是不会被释放的。

但是,很多时候当对象处于生命周期之内时,我们不再使用它,以便释放资源提升系统效率。因此,主动释放申请的资源显得很有必要。

永远不要把力所能及的事情交给操作系统,及时释放不再使用的资源是一个好习惯。

六、数据库访问

数据库访问永远是系统的瓶颈,选择高效、稳健的数据库访问模式是产品性能的基础保证。

n         永远不要假设你的应用系统构建与某个数据库之上,因此必须有统一的、透明的数据库访问机制。

n         采用ADO.Net访问数据库                基于效率和稳定性的考量,采用微软平台原生的数据库访问模式ADO.Net。使用ADO.Net可以通过OLEDB和ODBC两种模式访问数据库,我们建议使用数据库厂商提供的OLEDB模式,这种模式绕过了ODBC,使得数据库的游标性能大大提升,效率更佳。

n         不使用第三方的数据持久层              使用类似于Nhibernate之类的第三方数据持久层工具虽然可以提高开发的效率,但是却降低了系统的性能和弹性。性能对于产品而言,远远比开发效率重要的多,况且基于VS2005的开发,效率不是问题。请记住:第三方的工具永远不能成为你的产品核心技术;数据访问机制是系统的效率瓶颈,对

n         使用自主产权的数据对象                 直接采用ADO.Net封装最底层的数据访问方法:插入、删除和更新,以及事务管理等;客户端和服务器端采用相同的数据访问机制,并设立连接缓冲池提升数据访问效率。

七、分布式事务管理

对于多层分布式应用而言,数据库事务呈现出“远程、分布”的特色,导致事务难以管理。

对于Ado.Net而言,事务绑定了数据库连接,因此必须在数据访问对象中对每一个数据库连接管理各自的事务或嵌套事务。如果要访问数据库,服务器上的数据访问对象将自动分配一个特定的连接,根据该连接ID执行数据操作;无论该事务分布于多少个远程客户端进程,服务器数据对象只需要锁定连接ID即可轻松进行事务管理。

八、智能客户端

    智能客户端是易于部署和管理的客户端应用程序,它综合了瘦客户端和胖客户端的优点,通过统筹使用本地资源和到分布式数据资源的智能连接,提供快速响应的和丰富的交互式体验。

智能客户端分为Windows Form,Office Client,Mobile Client三种类型,具有如下特点:

n         利用本地资源

n         利用网络资源

n         支持偶尔连接的用户

n         提供智能安装和更新

n         提供客户端设备灵活性

    .NET 框架基类库内嵌了支持智能客户端的丰富程序集,通过使用公共语言运行库 (CLR),可以利用任何受到 .NET 支持的语言来开发智能客户端。

智能客户端是瘦客户段的强大替代品,也是微软推荐的客户端模式。尽量使用智能客户端而不要使用浏览器。如果可以,请把你的客户端系统构建在Office平台上,如Outlook。

posted @ 2008-03-20 14:02 noahsky 阅读(21) | 评论 (0)编辑

ObjectDataSource“ObjectDataSource1”未能找到带参数的非泛型方法

ObjectDataSource的使用的函数最好将变量名和数据库字段名统一,否则可能会出现错误虽然有解决办法,但没必要浪费时间去玩那个,不就是变量名吗,无所谓的啦。如果需要使用ObjectDataSource控件的更新和删除功能,一般是根据主键进行修改的,但是我在使用这个功能的时候,由于把主键这个字段设置为了readonly(主键不能更新),结果传回去的时候主键值就没有了,后来想了个办法,用cookieparam作为主键的来源,在gridview或者detailsview控件更新、删除之前(响应事件)将主键值写入cookie,感觉有点笨,不过功能没什么问题。

我发现自己真的很笨耶,晕死了。想要取回主键ID值,不用cookie那么麻烦去做,虽然可以实现功能,但肯定不好。
下面的方法应该才是正确的方法:
   指定GridView或者DetailsView的 DataKeyNames 属性,比如你的主键ID是ID,则指定DataKeyNames"ID"
在objectdatasource或者其他数据源控件指定参数,如下:

这样就可以传过去了
下面是我的一些代码:
DetailsView :
       

<yyc:SmartGridView ID="SmartGridView1" runat="server" AllowPaging="True" DataKeyNames="UserName"  DataSourceID="ObjectDataSource1" AutoGenerateColumns="False" AutoGenerateDeleteButton="True">
        <Columns>
        <asp:BoundField DataField="UserName" HeaderText="用户名" SortExpression="UserName" ReadOnly="True"/>
        <asp:BoundField DataField="Email" HeaderText="邮箱" SortExpression="Email"/>
        <asp:BoundField DataField="PasswordQuestion" HeaderText="问题" SortExpression="PasswordQuestion" />
        <asp:BoundField DataField="Comment" HeaderText="回答" SortExpression="Comment" />
        <asp:BoundField DataField="IsApproved" HeaderText="是否启用" SortExpression="IsApproved" />
        <asp:BoundField DataField="CreateDate" HeaderText="创建时间" SortExpression="CreateDate" />
        <asp:BoundField DataField="LastLoginDate" HeaderText="上次登录时间" SortExpression="LastLoginDate" />
        <asp:BoundField DataField="LastActivityDate" HeaderText="上次激活时间" SortExpression="LastActivityDate" />
        <asp:BoundField DataField="LastPasswordChangedDate" HeaderText="上次变更密码时间" SortExpression="LastPasswordChangedDate" />
        <asp:BoundField DataField="UserId" HeaderText="用户ID" SortExpression="UserId" />
        <asp:BoundField DataField="IsLockedOut" HeaderText="是否锁定" SortExpression="IsLockedOut" />
        <asp:BoundField DataField="LastLockoutDate" HeaderText="解除锁定时间" SortExpression="LastLockoutDate" />
        <asp:CommandField ShowDeleteButton="True" ButtonType="Button"/>
    </Columns>
</yyc:SmartGridView>

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetUsers" TypeName="UsersManager" DeleteMethod="DelUser">
       <DeleteParameters>
           <asp:Parameter Name="UserName" Type="String" />
       </DeleteParameters>
   </asp:ObjectDataSource>

业务层代码: 

[DataObjectMethod(DataObjectMethodType.Delete, true)]
public bool DelUser(string UserName)
{
    return Membership.DeleteUser(UserName);
}

posted @ 2008-03-20 13:12 noahsky 阅读(170) | 评论 (0)编辑

为gridview“删除”列添加确认对话框

如何为gridview控件里的“删除”列添加一个确认对话框?网上众说纷纭,大致见到了三种解决方案,现归纳如下:
1、继承Web.IO里的button控件,为其实现一个IPostback的接口用于回调,具体代码之复杂,只有作者自己想体会吧……
2、在gridview的ItemCreated事件中,遍历所有控件,若属于LinkButton类,且CommandName为“Delete”,就将其添加一个属性,具体做法是将遍历到的Control强制类型转化为LinkButton,然后调用其Atributes.Add方法,添加一个onclick事件内含confirm语句,即

lb.Attributes.Add("onclick", "return confirm('您真的要删除此行吗?')");

3、目前已知的最简洁的方法,如图,在删除按钮的那一列属性里面,把DeleteText属性设为

<div id="de" onclick="JavaScript:return confirm('确定删除吗?')">删除</div>

这个太强了,赞一个。

可问题是,.net输出的html代码如下:

<a href="javascript:__doPostBack('ctl00$ContentPlaceHolder1$GridView1','Delete$0')" style="color:#4A3C8C;"><div id="de" onclick="JavaScript:return confirm('确定删除吗?')">删除</div></a>

为什么内层<div>的onclick事件所返回的bool值能影响到外层<a>标签的的语句是否被执行呢?
仔细想了想,以前用asp直接写的删除标签是这个样子的:

<a href="deleteUser.asp?id=xxx"  onclick="return confirm('确定删除吗?')">删除</a>

也就是说,onclick事件接受一个bool值,其决定了click事件是否被触发。若click事件没有被触发,href内含的跳转动作就不会生效。同理,之前的那段代码中,<div>在<a>中,只有先触发<div>的click事件,<a>的跳转动作才会生效。而我们在<div>的onclick事件中若选择为其赋值false,随即取消了<div>的click事件,也同时取消了<a>的跳转。
之后,新的问题又来了。如果我们的CommandField中ButtonType是Button的话,这段代码就失效了。我想了下,可以通过将其转换为模板列的方式来解决。
先将该字段转换成模板,然后编辑这个模板列,选中用于删除的Button,将其onClientClick属性设为

return confirm('您确认删除要删除么?')

即可。

posted @ 2008-03-20 13:11 noahsky 阅读(13) | 评论 (0)编辑

HTTP协议中POST、GET、HEAD...

请求方法是请求一定的Web页面的程序或用于特定的URL。可选用下列几种:
  GET: 请求指定的页面信息,并返回实体主体。
  HEAD: 只请求页面的首部。
  POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。
  PUT: 从客户端向服务器传送的数据取代指定的文档的内容。
  DELETE: 请求服务器删除指定的页面。
  OPTIONS: 允许客户端查看服务器的性能。
  TRACE: 请求服务器在响应中的实体主体部分返回所得到的内容。
  PATCH: 实体中包含一个表,表中说明与该URI所表示的原内容的区别。
  MOVE: 请求服务器将指定的页面移至另一个网络地址。
  COPY: 请求服务器将指定的页面拷贝至另一个网络地址。
  LINK: 请求服务器建立链接关系。
  UNLINK: 断开链接关系。
  WRAPPED: 允许客户端发送经过封装的请求。
  Extension-mothed:在不改动协议的前提下,可增加另外的方法。
比如:
  GET /index.html HTTP/1.1
  Accept: text/plain /*纯ASCII码文本文件*/
  Accept: text/html /*HTML文本文件*/
  User-Agent:Mozilla/4.5(WinNT)
  说明浏览器使用Get方法请求文档/index.html。浏览器则只允许接收纯ASCII码文本文件和HTML文本文件,其使用的引擎是Mozilla/4.5(Netscape)。
  当服务器响应时,其状态行的信息为HTTP的版本号,状态码,及解释状态码的简单说明。现将5类状态码详细列出:
① 客户方错误
  100  继续
  101  交换协议
② 成功
  200  OK
  201  已创建
  202  接收
  203  非认证信息
  204  无内容
  205  重置内容
  206  部分内容
③ 重定向
  300  多路选择
  301  永久转移
  302  暂时转移
  303  参见其它
  304  未修改(Not Modified)
  305  使用代理
④ 客户方错误
  400  错误请求(Bad Request)
  401  未认证
  402  需要付费
  403  禁止(Forbidden)
  404  未找到(Not Found)
  405  方法不允许
  406  不接受
  407  需要代理认证
  408  请求超时
  409  冲突
  410  失败
  411  需要长度
  412  条件失败
  413  请求实体太大
  414  请求URI太长
  415  不支持媒体类型
⑤ 服务器错误
  500  服务器内部错误
  501  未实现(Not Implemented)
  502  网关失败
  504  网关超时
  505 HTTP版本不支持
  比如:(在《TELNET……》一文中用telnet登陆80端口,相同的方法用在HTTP/1.1中,会发现没有显示,下面补充说明之)
telnet www.fudan.edu.cn 80
HEAD / HTTP/1.1
host:www.fudan.edu.cn /*本行为输入内容*/
HTTP/1.1 501 Method Not Implemented
Date: Web, 01 Nov 2000 07:12:29 GMT /*当前的日期/时间*/
Server: Apache/1.3.12 (Unix) /*Web服务器信息*/
Allow: GET, HEAD, OPTION, TRACE /*支持的方法类型*/
Connection: close
Connect-Type: Text/html; charset=iso-8859-1/*连接的媒体类型*/
<!DOCTYPE HTML PUBLIG "-//IETF//DTD HTML 2.0//EN">
<HTML><HEAD>
<TITLE>501 Method
Not Implemented</TITLE>
</HEAD><BODY>
<H1>Method Not Implemented</H1>
head to /inde
x.html not supported.<P>
Invalid method in request head / htp/1.1<P>
<HR>
<ADDRESS>
Apache/1.3.12 Server at www.fudan.edu.cn Port 80</ADDRESS>
</BODY></HTML>
关于实体头部的内容还可以有:
Last Modified :请求文档的最近修改时间。
Expires :请求文档的过期时间。
Connect-length:文档数据的长度。
WWW-authenricate:通知客户端需要的认证信息。
Connect-encoding :说明有无使用压缩技术。
Transfer-encoding :说明采用的编码变换类型。

posted @ 2008-03-20 13:10 noahsky 阅读(85) | 评论 (0)编辑

Oracle日期时间加减法运算

 

当前系统日期加1天:

select to_date(sysdate,'YYYY-MM-DD') +1 from dual;

当前系统日期加1分钟:

select sysdate + 1/1440 from dual;

其中1440=1/(24*60)是一天之一分钟

给DATE列的日期加1秒: UPDATE 日期=日期+1秒

select to_char(sysdate,'yyyymmdd hh:mi:ss'),
to_char(sysdate + interval '1' second,'yyyymmdd hh:mi:ss') m1,
to_char(sysdate + numtodsinterval(1,'second'),'yyyymmdd hh:mi:ss') m2 from dual;

TO_CHAR(SYSDATE,' M1                M2
----------------- ----------------- -----------------
20060830 10:21:06 20060830 10:21:07 20060830 10:21:07

两种方法都可以,当然还可以用day来换算

posted @ 2008-03-20 10:29 noahsky 阅读(158) | 评论 (0)编辑

(转)面向对象设计思想(C#)

有了翅膀才能飞,欠缺灵活的代码就象冻坏了翅膀的鸟儿。不能飞翔,就少了几许灵动的气韵。我们需要给代码带去温暖的阳光, 让僵冷的翅膀重新飞起来。结合实例,通过应用OOP、设计模式和重构,你会看到代码是怎样一步一步复活的。 为了更好的理解设计思想,实例尽可能简单化。但随着需求的增加,程序将越来越复杂。此时就有修改设计的必要, 重构和设计模式就可以派上用场了。最后当设计渐趋完美后,你会发现,即使需求不断增加,你也可以神清气闲,不用为代码设计而烦恼了。

 

      假定我们要设计一个媒体播放器。该媒体播放器目前只支持音频文件mp3和wav。如果不谈设计,设计出来的播放器可能很简单: Chinaz^com

public class MediaPlayer
{  
   private void PlayMp3()
   {
      MessageBox.Show("Play the mp3 file.");
   }

   private void PlayWav()
   {
      MessageBox.Show("Play the wav file.");
   }

   public void Play(string audioType)
   {     
      switch (audioType.ToLower())
      {
          case ("mp3"):
             PlayMp3();
             break;
          case ("wav"):
             PlayWav();
             break;            
      }     
   }
}

       自然,你会发现这个设计非常的糟糕。因为它根本没有为未来的需求变更提供最起码的扩展。如果你的设计结果是这样,那么当你为应接不暇的需求变更而焦头烂额的时候,你可能更希望让这份设计到它应该去的地方,就是桌面的回收站。仔细分析这段代码,它其实是一种最古老的面向结构的设计。如果你要播放的不仅仅是mp3和wav,你会不断地增加相应地播放方法, 然后让switch子句越来越长,直至达到你视线看不到的地步。 Chinaz~com

       好吧,我们先来体验对象的精神。根据OOP的思想,我们应该把mp3和wav看作是一个独立的对象。那么是这样吗?

public class MP3
{
   public void Play()
   {
       MessageBox.Show("Play the mp3 file.");
   }
}

 

       好样的,你已经知道怎么建立对象了。更可喜的是,你在不知不觉中应用了重构的方法,把原来那个垃圾设计中的方法名字改为了 统一的Play()方法。你在后面的设计中,会发现这样改名是多么的关键!但似乎你并没有击中要害, 以现在的方式去更改MediaPlayer的代码,实质并没有多大的变化。
       
既然mp3和wav都属于音频文件,他们都具有音频文件的共性,为什么不为它们建立一个共同的父类呢?

public class AudioMedia
{
   public void Play()
   {
       MessageBox.Show("Play the AudioMedia file.");
   }
}
Chinaz@com

        现在我们引入了继承的思想,OOP也算是象模象样了。得意之余,还是认真分析现实世界吧。其实在现实生活中,我们播放的只会是某种具体类型的音频文件,因此这个AudioMedia类并没有实际使用的情况。对应在设计中,就是:这个类永远不会被实例化。所以,还得动一下手术,将其改为抽象类。好了,现在的代码有点OOP的感觉了:

 

        看看现在的设计,即满足了类之间的层次关系,同时又保证了类的最小化原则,更利于扩展(到这里,你会发现play方法名改得多有必要)。 即使你现在又增加了对WMA文件的播放,只需要设计WMA类,并继承AudioMedia,重写Play方法就可以了,
MediaPlayer类对象的Play方法根本不用改变。

 

       是不是到此就该画上圆满的句号呢?然后刁钻的客户是永远不会满足的,他们在抱怨这个媒体播放器了。因为他们不想在看足球比赛的时候,只听到主持人的解说,他们更渴望看到足球明星在球场奔跑的英姿。也就是说,他们希望你的媒体播放器能够支持视频文件。你又该痛苦了,因为在更改硬件设计的同时,原来的软件设计结构似乎出了问题。因为视频文件和音频文件有很多不同的地方,你可不能偷懒,让视频文件对象认音频文件作父亲啊。你需要为视频文件设计另外的类对象了,假设我们支持RM和MPEG格式的视频:

 

public abstract class VideoMedia
{
   public abstract void Play();
}
Www.Chinaz.com

public class RM:VideoMedia
{
   public override void Play()
   {
       MessageBox.Show("Play the rm file.");
   }
}

 

public class MPEG:VideoMedia
{
   public override void Play()
   {
       MessageBox.Show("Play the mpeg file.");
   }
}

 

        糟糕的是,你不能一劳永逸地享受原有的MediaPlayer类了。因为你要播放的RM文件并不是AudioMedia的子类。 中国站长.站

        不过不用着急,因为接口这个利器你还没有用上(虽然你也可以用抽象类,但在C#里只支持类的单继承)。虽然视频和音频格式不同,别忘了,他们都是媒体中的一种,很多时候,他们有许多相似的功能,比如播放。根据接口的定义,你完全可以将相同功能的一系列对象实现同一个接口: 中国站.长站

public interface IMedia
{
   void Play();
}
站.长站

public abstract class AudioMedia:IMedia
{
   public abstract void Play();
}

 

public abstract class VideoMedia:IMedia
{
   public abstract void Play();
}

       再更改一下MediaPlayer的设计就OK了:

public class MediaPlayer

   public void Play(IMedia media)
   {     
       media.Play();
   }
}

        现在可以总结一下,从MediaPlayer类的演变,我们可以得出这样一个结论:在调用类对象的属性和方法时,尽量避免将具体类对象作为传递参数,而应传递其抽象对象,更好地是传递接口,将实际的调用和具体对象完全剥离开,这样可以提高代码的灵活性。

        不过,事情并没有完。虽然一切看起来都很完美了,但我们忽略了这个事实,就是忘记了MediaPlayer的调用者。还记得文章最开始的switch语句吗?看起来我们已经非常漂亮地除掉了这个烦恼。事实上,我在这里玩了一个诡计,将switch语句延后了。虽然在MediaPlayer中,代码显得干净利落,其实烦恼只不过是转嫁到了MediaPlayer的调用者那里。
例如,在主程序界面中: 
 
Public void BtnPlay_Click(object sender,EventArgs e)
{
    switch (cbbMediaType.SelectItem.ToString().ToLower())
    {
        IMedia media;
        case ("mp3"):
             media = new MP3();
             break;
        case ("wav"): 
             media = new WAV();
             break;  
        //其它类型略;
    }
    MediaPlayer player = new MediaPlayer();
    player.Play(media);
}

       用户通过选择cbbMediaType组合框的选项,决定播放哪一种文件,然后单击Play按钮执行。
Www_Chinaz_com

 

       现在该设计模式粉墨登场了,这种根据不同情况创建不同类型的方式,工厂模式是最拿手的。先看看我们的工厂需要生产哪些产品呢?虽然这里有两种不同类型的媒体AudioMedia和VideoMedia(以后可能更多),但它们同时又都实现IMedia接口,
所以我们可以将其视为一种产品,用工厂方法模式就可以了。首先是工厂接口:

public interface IMediaFactory
{
   IMedia CreateMedia();
}

       然后为每种媒体文件对象搭建一个工厂,并统一实现工厂接口:

public class MP3MediaFactory:IMediaFactory
{
   public IMedia CreateMedia()
   {
       return new MP3();
   }
}
public class RMMediaFactory:IMediaFactory
{
   public IMedia CreateMedia() 中国站.长站
   {
       return new RM();
   }
}
//其它工厂略;

 

写到这里,也许有人会问,为什么不直接给AudioMedia和VideoMedia类搭建工厂呢?很简单,因为在AudioMedia和VideoMedia中,分别还有不同的类型派生,如果为它们搭建工厂,则在CreateMedia()方法中,仍然要使用Switch语句。而且既然这两个类都实现了IMedia接口,可以认为是一种类型,为什么还要那么麻烦去请动抽象工厂模式,来生成两类产品呢? 可能还会有人问,即使你使用这种方式,那么在判断具体创建哪个工厂的时候,不是也要用到switch语句吗?我承认这种看法是对的。不过使用工厂模式,其直接好处并非是要解决switch语句的难题,而是要延迟对象的生成,以保证的代码的灵活性。当然,我还有最后一招杀手锏没有使出来,到后面你会发现,switch语句其实会完全消失。 Www@Chinaz@com

还有一个问题,就是真的有必要实现AudioMedia和VideoMedia两个抽象类吗?让其子类直接实现接口不更简单?对于本文提到的需求, 我想你是对的,但不排除AudioMedia和VideoMedia它们还会存在区别。例如音频文件只需要提供给声卡的接口,而视频文件还需要提供给显卡的接口。如果让MP3、WAV、RM、MPEG直接实现IMedia接口,而不通过AudioMedia和VideoMedia,在满足其它需求的设计上也是不合理的。当然这已经不包括在本文的范畴了。

 

现在主程序界面发生了稍许的改变:
Public void BtnPlay_Click(object sender,EventArgs e)
{
IMediaFactory factory = null;
    switch (cbbMediaType.SelectItem.ToString().ToLower())
    {
        case ("mp3"):
             factory = new MP3MediaFactory();
             break;
        case ("wav"):
             factory = new WAVMediaFactory();
             break;  
        //其他类型略;
    }
    MediaPlayer player = new MediaPlayer();


    player.Play(factory.CreateMedia());
}

 

写到这里,我们再回过头来看MediaPlayer类。这个类中,实现了Play方法,并根据传递的参数,调用相应媒体文件的Play方法。在没有工厂对象的时候,看起来这个类对象运行得很好。如果是作为一个类库或组件设计者来看,他提供了这样一个接口,供主界面程序员调用。然而在引入工厂模式后,在里面使用MediaPlayer类已经多余了。所以,我们要记住的是,重构并不仅仅是往原来的代码添加新的内容。当我们发现一些不必要的设计时,还需要果断地删掉这些冗余代码。
Public void BtnPlay_Click(object sender,EventArgs e)
{
IMediaFactory factory = null;
    switch (cbbMediaType.SelectItem.ToString().ToLower())
    {       
        case ("mp3"):
             factory = new MP3MediaFactory();
             break;
        case ("wav"): 
             factory = new WAVMediaFactory();
             break;  
        //其他类型略;
    }
    IMedia media = factory.CreateMedia();
    media.Play();
}

 

如果你在最开始没有体会到IMedia接口的好处,在这里你应该已经明白了。我们在工厂中用到了该接口;而在主程序中,仍然要使用该接口。使用接口有什么好处?那就是你的主程序可以在没有具体业务类的时候,同样可以编译通过。因此,即使你增加了新的业务,你的主程序是不用改动的。

 

不过,现在看起来,这个不用改动主程序的理想,依然没有完成。看到了吗?在BtnPlay_Click()中,依然用new创建了一些具体类的实例。如果没有完全和具体类分开,一旦更改了具体类的业务,例如增加了新的工厂类,仍然需要改变主程序,何况讨厌的switch语句仍然存在,它好像是翅膀上滋生的毒瘤,提示我们,虽然翅膀已经从僵冷的世界里复活,但这双翅膀还是有病的,并不能正常地飞翔。 中.国.站长站

是使用配置文件的时候了。我们可以把每种媒体文件类类型的相应信息放在配置文件中,然后根据配置文件来选择创建具体的对象。
并且,这种创建对象的方法将使用反射来完成。首先,创建配置文件:

 

<appSettings>
  <add key="mp3" value="WingProject.MP3Factory" />
  <add key="wav" value="WingProject.WAVFactory" />
  <add key="rm" value="WingProject.RMFactory" />
  <add key="mpeg" value="WingProject.MPEGFactory" />
</appSettings>

 

然后,在主程序界面的Form_Load事件中,读取配置文件的所有key值,填充cbbMediaType组合框控件:
public void Form_Load(object sender, EventArgs e)
{
cbbMediaType.Items.Clear();
foreach (string key in ConfigurationSettings.AppSettings.AllKeys)
{
   cbbMediaType.Item.Add(key);
}
cbbMediaType.SelectedIndex = 0;
}
站.长.站

最后,更改主程序的Play按钮单击事件:
Public void BtnPlay_Click(object sender,EventArgs e)
{
string mediaType = cbbMediaType.SelectItem.ToString().ToLower();
string factoryDllName = ConfigurationSettings.AppSettings[mediaType].ToString();
//MediaLibray为引用的媒体文件及工厂的程序集;
IMediaFactory factory = (IMediaFactory)Activator.CreateInstance("MediaLibrary",factoryDllName).Unwrap();
IMedia media = factory.CreateMedia();
media.Play();
}

现在鸟儿的翅膀不仅仅复活,有了可以飞的能力;同时我们还赋予这双翅膀更强的功能,它可以飞得更高,飞得更远!

 

享受自由飞翔的惬意吧。设想一下,如果我们要增加某种媒体文件的播放功能,如AVI文件。那么,我们只需要在原来的业务程序集中创建AVI类,并实现IMedia接口,同时继承VideoMedia类。另外在工厂业务中创建AVIMediaFactory类,并实现IMediaFactory接口。假设这个新的工厂类型为WingProject.AVIFactory,则在配置文件中添加如下一行:
<add key="AVI" value="WingProject.AVIFactory" />。
而主程序呢?根本不需要做任何改变,甚至不用重新编译,这双翅膀照样可以自如地飞行!
站.长.站

  中国.站.长站

public abstract class AudioMedia
{
   public abstract void Play();
}

 

public class MP3:AudioMedia
{
   public override void Play()
   {
       MessageBox.Show("Play the mp3 file.");
   }
}

 

public class WAV:AudioMedia
{
   public override void Play()
   {
       MessageBox.Show("Play the wav file.");
   }
}

 

public class MediaPlayer

   public void Play(AudioMedia media)
   {     
       media.Play();
   }
}

public class WAV
{
   public void Play()
   {
       MessageBox.Show("Play the wav file.");
   }
}

 

 

posted @ 2008-03-10 15:54 noahsky 阅读(55) | 评论 (1)编辑

DataSet与XML操作之间接转换

1,服务器端读取DataSet中的数据到XML的形式的string:

DataSet ds = new DataSet("CertResult");
            DataTable dt 
= access.GetCZRKInfo(idcard);//读取数据库

            
if (dt.Rows.Count == 0return "";
            ds.Tables.Add(dt);
            
return ds.GetXml();
2,客户端接收XML形式的string生成DataSet:
        private DataSet CreateDSBYString(string result)
        
{
            DataSet ds 
= new DataSet();
            StringReader sr 
= new StringReader(result);//System.IO;
            ds.ReadXml(sr);
            
return ds;
        }

XmlDocument doc = new XmlDocument();
doc.LoadXml(DataSet.GetXml());

//对doc进行操作查找就是了.

posted @ 2008-02-26 20:17 noahsky 阅读(36) | 评论 (0)编辑