RSS

Monthly Archives: 一月 2004

春节快乐!

虽然公司放假已经几天了,今天忍不住溜到公司,嘿嘿,有人,于是上网来看看,估计春节假期除非自己买个Modem在家里拨号,否则是上不了网了悲伤的脸,苦啊…今天春节不回家,在深圳过。

不能上网只好在家里看书了,准备看完两本一直想看完但一直没时间看的书,呵呵。

 

Posted by on 2004/01/19 in 未分类

3 Comments

在SPS中加入自定义WebService

前几天写了一个WinForms的客户端软件,可以远程连接SPS站点并(断点续传)上传文件。

图片:
登录:http://blog.joycode.com/kaneboy/gallery/image/761.aspx
设置每次上传的Block Size:http://blog.joycode.com/kaneboy/gallery/image/762.aspx
上传:http://blog.joycode.com/kaneboy/gallery/image/763.aspx
界面简陋哈,呵呵,现在主要是实现功能阶段。

WinForms客户端程序是通过连接SPS站点中自定义的WebService来获取信息、上传文件的。由于SPS接管了ASP.NET的WebService处理,所以加入自定义的WebService有点麻烦,先讲讲SPS自己怎么处理自己的WebService的:

SPS把所有的WS都放在_vti_bin虚拟目录中,这个虚拟目录映射的是Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\ISAPI目录,呵呵,这个目录下就放着所有SPS自带的WS,如果有客户端连接这里的WS,SPS会通过执行wsdisco.aspx和wswsdl.aspx这两个文件来返回客户端所请求的WebService的Disco和WSDL信息。

wsdisco.aspx的执行流程很简单:比如客户端请求versions.asmx这个WebService,那么wsdisco.aspx会在这个目录中查找名称为versionsdisco.aspx的文件,然后用Server.Execute()方法来执行它,versiondisco.aspx更简单,就是把version.asmx的Disco信息输出。wswsdl.aspx的流程类似。

知道了SPS处理WS的方式,加入自己的WS就很简单了。如果你根本用不着它自带的WS,而且想用自己的方式来返回Disco和WSDL信息,那么可以改写wsdisco.aspx和wswsdl.aspx这两个文件,让它们实现自己的处理方式。或者按照SPS的方式来,把自己的WS、Disco信息、WSDL信息都用和SPS相同的方式来加入就行了。我是用的后者。

如果看看SDK里面SPS自带的WS,可以看到它们都是把请求的数据用XML来描述并返回XmlNode对象,这是个好方法,我自己的WS也都是采用类似的方式返回XmlNode。

自己还有一个想法,就是让用户可以用Office Research Service来直接在Office里面对SPS文档库进行全文检索的搜索,呵呵,同样是WS的应用,实现并不困难。

另外说说别的事,关于.Net Assembly的Decompiler的,最近常常用.Net Reflector的Decompiler功能看自己写的Assembly,主要看看C#编译器怎么处理一些代码的。昨晚灵机一动,用VS2003的VC#和Delphi8分别写了一段一模一样的代码,创建一个Array然后用一个循环赋值后用IndexOf来查找一个值,分别生成Assembly后再用Decompiler来看,看看他们处理的代码是否一样。

结果发现C#编译器生成的MSIL几乎被忠实的反编译成了和源码一模一样的代码,包括for循环,而D8编译器生成的MSIL的循环则被还原成用一个if和goto实现的循环。

哈哈,我想这不能证明D8的编译器比C#编译器更能优化代码,而是证明Decompiler更熟悉C#编译器编译的MSIL的风格(也就是说,这个Decompiler的作者专门研究了C#编译器编译for、if…的结果),所以更能忠实的还原代码。

 

Posted by on 2004/01/14 in 未分类

7 Comments

Tags:

有意思的质疑

一、SOA不就是Interfaced-Based编程吗?

终于有人忍不住发出了质疑的声音,SOA,听起来很玄,其实不就是Interfaced-Based编程嘛?我们以前OO编程的时候不是一直都这么干的吗?充其量用Web Services来包装了一下,这就是所谓的“革命性”吗?

有人就接道,“革命性”就是指的WEB Service啊,可以穿透网络啊。

当然有人会反驳了,理由就是:Interfaced-Based不能能跨平台吗嘛,和SOA、AOP差别大了,而且SOA确切的说是“Message-Based”编程,而且别忘了还有AOP呢。

二、有必要学XAML吗?

XAML真是很火啊,特别是Dan Box和Chris Anderson出了两个MSDN TV短片之后。但是有人就说了,XAML是定义程序界面用的一种标记语言,就像今天用HTML定义Web界面一样,可以想象以后VS.NET肯定可以用WYSIWYG的方式来制作程序的界面,就像今天可以用N多的工具来制作Web界面一样,我们有必要去学这些XAML的标记、用法吗?

反驳的人当然有了,“我们制作Web网页,而不懂HTML实际吗?看看现在做网页的那些工具所谓的WYSIWYG的效果…”、“有必要学数学吗?反正有计算器了…”

但是,“HTML不同啊,当初就没个统一的界面标准,结果被大家搞乱了,而XAML是微软自己的,当然可以精确定义了…”、“数学和XAML没有可比性啊,可以拿IL比比,能读懂IL当然有好处,可是谁会用IL写代码呢?”

 

Posted by on 2004/01/07 in 未分类

10 Comments

用ObjectSpaces重建IBuySpy的数据访问层

ObjectSpaces这个ORM框架可能已经被大家听过N次了,它诞生很早,但开发周期拖了很长,虽然很早它的测试版本就已经有人使用了,但只到.Net Framework 1.2才计划正式将它包含其中,并放在了Object.Data.ObjectSpaces这个NameSpace中。

 

ObjectSpaces的流传版本也是甚多,被很多人研究过的一个EAPEarly Adopter Preview)版的版本号是1.0.1081,我在Gotdotnet.com里面看过一个1.0.3328.4的版本做的东西,这里用的这个ObjectSpaces是现在大家普遍使用的.Net Framework 1.2 Alpha测试版里面带的,版本号同.Net Framework保持一致,1.2.30703.27

 

从版本号的变更就可以看出,ObjectSpaces从开始到现在变化很大。上面提到的三个版本都各有差别,而且差别不小。现在最新的这个版本我想应该和最终版本差别不会很大了。我以前的一篇文章就是基于EAP版本的,比现在的简单不少。

 

进入正题:在IBuySpy的架构中,实际上是没有BLL(商业逻辑层)的,甚至没有创建Entity Class,数据通过DAL(数据访问层)从数据库中获得,然后将DataSet或者DataReader直接从DAL传递给构成IBuySpy页面的Moduleascx文件,再直接绑定到DataList之类的web control上。

 

我们这里将IBuySpyAnnouncement模块的DALObjectSpaces重新写过:

 

一、先来看看IBuySpy原来的Announcements模块

 

DALAnnouncementsDB.cs文件

界面控件页面:用于显示的Announcements.ascx控件和用于新增修改的EditAnnouncements.aspx页面

 

里面都是最最标准的写法,没什么讲的。

 

二、Entity Class

 

新创建一个Announcement类和一个AnnouncementCollection集合类,也没什么讲的。

 

三、建立ObjectSpacesRSDOSDMSD

 

ObjectSpaces的核心就是用来描述Schema的三个xml文件:

一个描述数据库表结构的Relational Schema Definition,一个描述实体类结构的Object Schema Definition,和一个描述表结构和实体类映射关系的Mapping Schema Definition

ObjectSpace最主要(也是最烦人)的工作就是把这三个Schema写出来。EAP版里面还只有一个xml文件要写,现在要写三个了L

 

AnnouncementRSD.xml

AnnouncementOSD.xml

AnnouncementMSD.xml

 

四、重写DAL

 

创建一个新的类文件AnnouncementOSDB.cs,包含一个新的类AnnouncementOSDB,里面的方法签名对照着IBuySpy原本的AnnouncementDB类就行了。原本的AnnouncementDB是用ADO.NET,返回DataSetDataReader,我们的AnnouncementOSDB就用ObjectSpaces,返回实体集合类或实体类。

 

根据moduleId返回Announcement集合:

public AnnouncementCollection GetAnnouncements(int moduleId)

              {

                     ObjectSpace os = new ObjectSpace(_sMapFilePath, _conn);

                     // 条件是ModuleID等于参数moduleIdExpireDate大于当前时间

                     ObjectQuery query = new ObjectQuery(typeof(Announcement), “ModuleID = ” + moduleId.ToString() + ” and ExpireDate > #” + DateTime.Now.ToString() + “#”);

                     // 取数据

                     ObjectReader reader = os.GetObjectReader(query);

 

                     AnnouncementCollection result = new AnnouncementCollection();

                     // ObjectReader中取值不需要另外造型

                     foreach (Announcement ann in reader)

                     {

                            result.Add(ann);

                     }

 

                     return result;

              }

 

根据参数返回一个Announcement

              public Announcement GetSingleAnnouncement(int itemId)

              {

                     ObjectSpace os = new ObjectSpace(_sMapFilePath, _conn);

                     ObjectQuery query = new ObjectQuery(typeof(Announcement), “ItemID = ” + itemId.ToString());

                     return (Announcement) os.GetObject(query);

              }

 

根据参数删除一个Announcement:

              public void DeleteAnnouncement(int itemId)

              {

                     ObjectSpace os = new ObjectSpace(_sMapFilePath, _conn);

                     ObjectQuery query = new ObjectQuery(typeof(Announcement), “ItemID = ” + itemId.ToString());

                     Announcement ann = (Announcement) os.GetObject(query);

                     os.MarkForDeletion(ann);

                     os.PersistChanges(ann);

              }

 

新增一个Announcement

              public void AddAnnouncement(int moduleId, int itemId, String userName, String title, DateTime expireDate, String description, String moreLink, String mobileMoreLink)

              {

                     Announcement ann = new Announcement();

                     ann.SetModuleID(moduleId);

                     ann.SetItemID(-1);

                     ann.CreatedByUser = userName;

                     ann.CreatedDate = DateTime.Now;

                    

 

                     ObjectSpace os = new ObjectSpace(_sMapFilePath, _conn);    

                     os.StartTracking(ann, InitialState.Inserted);

                     os.PersistChanges(ann);

              }

 

修改一个Announcement

              public void UpdateAnnouncement(int moduleId, int itemId, String userName, String title, DateTime expireDate, String description, String moreLink, String mobileMoreLink)

              {

                     ObjectSpace os = new ObjectSpace(_sMapFilePath, _conn);

                     ObjectQuery query = new ObjectQuery(typeof(Announcement), “ItemID = ” + itemId.ToString());

                     Announcement ann = (Announcement) os.GetObject(query);

 

                     ann.CreatedByUser = userName;

                     ann.Title = title;

                    

 

                     os.PersistChanges(ann);

              }

 

五、最后修改界面层

 

原本界面层是把AnnouncementDB返回的DataSet绑定到Web Control上,只要改成将AnnouncementOSDB返回的实体集合类绑定到Web Control上就可以了,改动量很少很少。

 

比如:

原本将数据取出并绑定的代码(在Announcemenets.ascx.cs中):

AnnouncementsDB announcements = new AnnouncementsDB();

myDataList.DataSource = announcements.GetAnnouncements(ModuleId);

myDataList.DataBind();

只要将第一句改成:

AnnouncementOSDB announcements = new AnnouncementOSDB();

实际上就是改从哪个DAL取数据就OK了。

 

六、讲讲ObjectSpaces

 

ObjectSpaces的架构是这样的:

ObjectSpace类管理数据映射,它负责(隐形的通过ObjectEngine)从数据源(IDbConnection或者ObjectSources)取数据和将数据更新回数据源(更新时会自动隐形启用Transaction)。它通过ObjectSpace.GetObject()返回单个对象,通过ObjectSpace.GetObjectSet()返回ObjectSet对象(这个对象类似于DataSet,表示一组数据对象),通过ObjectSpace.GetObjectReader()返回ObjectReader对象(这个对象类似于DataReader,是一个快速的forward-only的数据对象读取器)。它通过内含的ObjectContext来维护数据对象的原始值和监视数据对象的值的修改。

 

我上面的代码演示目的是为了展示ObjectSpaces,并没有完整的给IBuySpy加一个BLL。我也没有演示数据之间的RelationsObjectSpaces可以支持非常丰富的RelationsOneToOneManyToManyOneToMany等等,而且也提供了LazyLoading(在真正需要使用Relation的数据的时候才真正去取这些数据)。

 

不过如果你也试着用ObjectSpaces来重建你的项目中的DAL,我不知道你的感觉会不会和我一样,那就是“比现在更麻烦”。比如,不支持存储过程(难道支持而文档里面不提一句吗?EAP版本还支持的),手工写RSDOSDMSD太繁琐了(PDC2003上出现过一个Mapper Utility,希望Whidbey会提供自动化工具),灵活性降低不少(所有ORM框架的问题)。

 

一些资源:

一篇短小通俗易懂的介绍文章

一群人在对ObjectSpaces进行批判,拿它和其他的ORM框架对比

Microsoft.ObjectSpaces新闻组

PDC2003ObjectSpaces的演示例程

ObjectSpaces开发组中一个家伙的Blog

如果没有.Net Framework 1.2,那么就从这里看看ObjectSpacesReference

 

Posted by on 2004/01/04 in 未分类

6 Comments

AnnouncementMSD.xml

<map:MappingSchema xmlns:map=”http://schemas.microsoft.com/data/2002/09/28/mapping”>
  <map:DataSources>
    <map:DataSource Name=”Portal” Type=”SQL Server” Direction=”Source”>
      <map:Schema Location=”AnnouncementRSD.xml” />
      <map:Variable Name=”ASPNetPortal.Announcement_Rows” Select=”Announcements” />
    </map:DataSource>
    <map:DataSource Name=”AnnouncementOSD” Type=”Object” Direction=”Target”>
      <map:Schema Location=”AnnouncementOSD.xml” />
    </map:DataSource>
  </map:DataSources>
  <map:Mappings>
    <map:Map SourceVariable=”ASPNetPortal.Announcement_Rows” TargetSelect=”ASPNetPortal.Announcement”>
      <map:FieldMap SourceField=”ItemID” TargetField=”_iItemID” />
      <map:FieldMap SourceField=”ModuleID” TargetField=”_iModuleID” />
      <map:FieldMap SourceField=”CreatedByUser” TargetField=”_sCreatedByUser” />
      <map:FieldMap SourceField=”CreatedDate” TargetField=”_dCreatedDate” />
      <map:FieldMap SourceField=”Title” TargetField=”_sTitle” />
      <map:FieldMap SourceField=”MoreLink” TargetField=”_sMoreLink” />
      <map:FieldMap SourceField=”MobileMoreLink” TargetField=”_sMobileMoreLink” />
      <map:FieldMap SourceField=”ExpireDate” TargetField=”_dExpireDate” />
      <map:FieldMap SourceField=”Description” TargetField=”_sDescription” />
    </map:Map>
  </map:Mappings>
</map:MappingSchema>

 

Posted by on 2004/01/04 in 未分类

Leave a comment

AnnouncementOSD.xml

<osd:ExtendedObjectSchema Name=”AnnouncementOSD” xmlns:osd=”http://schemas.microsoft.com/data/2002/09/20/persistenceschema”>
  <osd:Classes>
    <osd:Class Name=”ASPNetPortal.Announcement”>
      <osd:Member Name=”_iItemID” Alias=”ItemID” Key=”true” KeyType=”AutoIncrement” />
      <osd:Member Name=”_iModuleID” Alias=”ModuleID” />
      <osd:Member Name=”_sCreatedByUser” Alias=”CreatedByUser” />
      <osd:Member Name=”_dCreatedDate” Alias=”CreatedDate” />
      <osd:Member Name=”_sTitle” Alias=”Title” />
      <osd:Member Name=”_sMoreLink” Alias=”MoreLink” />
      <osd:Member Name=”_sMobileMoreLink” Alias=”MobileMoreLink” />
      <osd:Member Name=”_dExpireDate” Alias=”ExpireDate” />
      <osd:Member Name=”_sDescription” Alias=”Description” />
    </osd:Class>
  </osd:Classes>
</osd:ExtendedObjectSchema>

 

Posted by on 2004/01/04 in 未分类

Leave a comment

AnnouncementRSD.xml

<rsd:Database Name=”Portal” xmlns:rsd=”http://schemas.microsoft.com/data/2002/09/28/rsd”>
  <rsd:Schema Name=”dbo”>
    <rsd:Tables>
      <rsd:Table Name=”Announcements”>
        <rsd:Columns>
          <rsd:Column Name=”ItemID” SqlType=”int” AutoIncrement=”true” IncrementStep=”1″ />
          <rsd:Column Name=”ModuleID” SqlType=”int” />
          <rsd:Column Name=”CreatedByUser” SqlType=”nvarchar” Precision=”100″ />
          <rsd:Column Name=”CreatedDate” SqlType=”datetime” />
          <rsd:Column Name=”Title” SqlType=”nvarchar” Precision=”150″ />
          <rsd:Column Name=”MoreLink” SqlType=”nvarchar” Precision=”150″ />
          <rsd:Column Name=”MobileMoreLink” SqlType=”nvarchar” Precision=”150″ />
          <rsd:Column Name=”ExpireDate” SqlType=”datetime” />
          <rsd:Column Name=”Description” SqlType=”nvarchar” Precision=”2000″ />
        </rsd:Columns>
        <rsd:Constraints>
          <rsd:PrimaryKey Name=”PK_Announcements”>
            <rsd:ColumnRef Name=”ItemID” />
          </rsd:PrimaryKey>
        </rsd:Constraints>
      </rsd:Table>
    </rsd:Tables>
  </rsd:Schema>
</rsd:Database>

 

Posted by on 2004/01/04 in 未分类

Leave a comment