代号 WinFS:革命性的文件存储系统...

王朝vc·作者佚名  2006-01-17
窄屏简体版  字體: |||超大  

代号 WinFS:革命性的文件存储系统

——基于内容的搜索和文件管理

原著:Richard Grimes

翻译:牛阿牛

原文出处:MSDN Magazine Jan 2004(Code Name WinFS...)

本文内容基于微软专业开发人员大会前夕所生成的代号为“Longhorn”的微软 Windows 操作系统。这里包含的所有信息都有可能改变。

注:本文是在“Longhorn”

Windows产品发布前写出来的,所以,我们不保证此文包含的任何细节与正式售卖品完全一致。本文付诸印刷时,所有描述该产品的信息仅能应用于计划目的。有关信息在任何时候都有可能改变,恕不事先通知。

摘要

当今许多组织面临的巨大问题之一是聚合以不同格式存储的信息,知识工作者长期以来期望能够搜索与格式无关的内容。下一版代号为“Longhorn”的Windows操作系统自诩将会有一个新的存储子系统使该任务更容易完成。这个子系统的代号是“WinFS”,该系统允许用户实现基于存储项之元数据的搜索,与它是哪一种文件类型或是哪个应用创建的无关。本文内容包括WinFS的基本体系结构以及如何使用WinFS受管API。

逐年来,随着硬盘越来越大、越来越快。软件随之生成更多的数据。硬盘常用来保存个人信息:信件、通讯录、工作文档等。这些信息通常都被作为单独的实体互不相关,然而在某种意义上它们又是相互关联的。E-Mail

来自通讯录,它左右着你将要做的工作,因此决定着你将生成的文件。一旦你的数据量非常大时,基于属性和内容灵活有效搜索特定数据的机制就显得非常重要。直到现在,诸如

Outlook、Windows® Contact

List 以及 Windows

媒体播放器媒体库等应用使用的信息存储和搜索机制都是不相关的技术。现在,随着代号为“Longhorn”的Microsoft®

Windows®的问世,一切都将成为过去。

Longhorn 中代号为“WinFS”的存储子系统可以存储所有类型的数据,但它使用的却是同一种数据存取机制。这对于依赖包括注册表、事件日志、联系信息和e-mail或者象图像、音频这样的肥胖文件在内的多种不兼容数据存储机制的操作系统来说是个巨大的变化。因为

WinFS 是通过数据库技术实现的,这样你就有了一个高效灵活的机制来查询数据,具备了将数据复制到其它机器存储的能力,利于对数据进行备份和恢复。

我将在本文中概述WinFS的基本框架及描述数据存取方式,检索方法及各种为操作系统提供的服务。与 Longhorn 一样,WinFS

拥有自己的受管 API,我将集中在下面讨论这个话题。

基本结构

就数据管理而言,长期以来所面临的问题是数据量太大,难以轻松找到所需要的数据。当机器的硬盘以兆来计量时,这个问题将变得更加严重。硬盘越大,存储的数据就越多,找到需要的数据就越难。依据识别有特性的项来查找数据就意味着对文件必须分门别类。

例如,你有几千张数字图片,一般来说,你的相机和桌面软件会将这些数据按照顺序命名,并且可能按照拍摄日期将它们放到某个目录中,以这种方式存放数据,搜索特定的照片时必须记住日期和照片编号。如果你既不知道日期,也不知道编号,那么你不得不通过缩略图的方式查找。当然,如果你有耐心,你可以在下载的时候命名每一张照片。象Win32这种纹理粗糙的文件和文件夹系统难以灵活存储各种需要的文件。

桌面软件可以弥补这些不足,它允许你建立目录来保存其它描述性信息,诸如相机设置、光线配置及对每一个图片的常规描述等。这类软件当然依赖某个数据库实现,但其关键之处在于弥补了文件系统的不足。你可能会争辩说:这些图象文件——元数据——实际上是照片对象的一部分。数字照片在图像的

EXIF (Exchangeable Image File

Format)头中拥有其它信息,这个头中的信息是在拍摄的时候获取的。为了在候选图片中包含这些元数据,并将它与查询条件进行比较。复杂的图片处理软件在图片被传到桌面时能析取EXIF信息并将它们作为额外的元数据包含到数据库中。

这正是WinFS的优势所在。在WinFS中存储的每一个信息都是一个数据项且每一项数据都具备通过某种方案描述的元数据属性。追随特定方案的各数据项被作为连续的.NET对象存储在WinFS中,并且通过准许存取数据项属性的

T-SQL 视图来存取。查找存储在磁盘上的某个图片必须针对图像数据的元数据属性进行查询,该信息从 EXIF

头中获得,其它的文件信息如创建日期以及组成图像的位信息也从 EXIF 头中获得。

图一WinFS

的文件

图一所示表现了WinFS对象的概念结构。每一个数据项存在于某容器文件夹中,这些容器文件夹被用于控制数据项的生命期,文件夹本身也是数据项,并且每个文件夹必须包含在另一个文件夹中。这个容器的唯一一个例外是根容器文件夹。数据项被存储在卷中,卷是最大的数据项自治容器。每一台机器上的WinFS服务按卷存储、删除、修改和定位数据项。

WinFS的存储

WinFS提供了数据项表的T-SQL视图——每种类型的数据项有一个视图。为了加强存储的安全性和数据的一致性,只能通过视图来读取数据,只能通过存储过程来修改数据。例如:下面的命令语句

返回媒体库中所有符合条件的音轨信息。

SELECT Artist, Title, Album FROM Audio.[Track!V1]

有些对象同时又包含了其它的对象。如 Track

对象包含一个叫 Authors 成员,该成员又是Author 对象的一个集合,其中每个对象又有一个 DisplayName

属性,该属性是创建此音轨的艺术家的名字。于是可以构造一个查询来获取归属于某个特定艺术家的所有音轨信息,该查询可以交叉引用你的个人通讯簿,根据音轨信息查看是否有艺术家的手机号码。(不过

你得有一本名人通讯薄。)

并不是每个人都是数据库开发人员,能构造复杂的 select 查询语句,所以 WinFS 提供了高级 API 来方便数据信息的插入,删除和查询。

WinFS 类型和 .Net 类型

当你用受管API存取WinFS数据信息时,是通过.NET对象来做的。每一个WinFS数据项类型都有对应的.Net框架类,在其类层次中少不了

System.Storage.Item。数据项类型有一个 ItemID_Key 属性和一个 GUID,GUID为WinFS数据项提供身份标识。当你获得某个WinFS对象时,API返回对应的.NET类型,由WinFS对象元数据初始化。如果你获得具有相同ItemID_Key的两个数据项实例,那么它们在该存储中便是相同的数据项,即便会有两个不同的.NET引用。在比较两个WinFS对象时,系统存储结构中都有相应的类。每一个对象类都有唯一的ID

键值-GUID;当然要检索一个对象,API函数就会返回与WinFS对象的数据元对应的.NET类型。当比较两个WinFS 对象时,用疑问方式而不是使用

ReferenceEquals 比较每个数据项的 ItemID_Key 的值是很重要的。

WinFS中,WinFS类型就是一个数据项,它由元素构成,这些元素要么是另外的数据项,也就是嵌套元素,要么是标量类型——或者是任何这些类型的集合。数据项的单位必须连贯一致,这样在拷贝或删除时,该数据项中的所有元素也同时被拷贝或删除。嵌套元素只能是某个单一数据项的一部分。这种行为与在.NET中的操作模式是不同的。例如,某个System.Storage.Contacts.Person

对象具有叫做 PrimaryName 的属性,它是个 FullName 类型的嵌套元素。如果你用相同的 PrimaryName 创建两个

Person 对象(例如,父亲和儿子有相同的名字),那么你必须创建两个 FullName 对象,每个 Person 对象有一个 FullName

对象。如果两个 Person 对象使用相同的 FullName 对象,那么当你试图保存 Person

对象时会抛出异常,因为它们虽然是有效的.NET对象,但不是有效的 WinFS 对象。

WinFS 中的受管 API

图二是WinFS体系结构的概观。WinFS可以通过几种API来进行存取,在这个版本中,应用程序可以使用 SQL Server Provider

的 ADO.NET,OLE

DB 的原始API,基于COM的API及受管API。通常开发人员会使用API而不是T-SQL,但仍然需要使用T-SQL来存取,因为大量的工作借助 API

难以完成,只能通过T-SQL来处理。

专注于.NET构架的开发人员将会使用 System.Storage 程序集中的 WinFS API,它包含一些普通的 WinFS API。开发人员

也会用到 System.Storage.Schema 程序集,它拥有处理标准 Longhorn

类型(Track、Image、Folder、Message等)的API。除了用于 WinFS 数据信息的公共语言运行时(CLR)类型定义之外,System.Storage.Schema

程序集还包含WinFS 数据信息数据模型映射。这个映射向 WinFS

指明了用于存取数据信息的数据源,并且将来自受管类型的属性映射到数据存储表中的各个域。深入到 WinFS API

内部,该映射信息被用于构造T-SQL查询来存取存储系统中的数据信息。

图二 WinFS 的体系结构

所有的WinFS API 代码必须在 ItemContext (数据项上下文)中运行。ItemContext 的含义很丰富。首先,WinFS API

必须创建和存储系统的连接;只要 ItemContext 处于激活状态,连接就不能断开。其次,因为 ItemContext

代表着某个数据连接,它提供一种事物处理机制。如果你愿意,可以将存储系统作为原子单位进行多次修改。最后,ItemContext

通过定义你的搜索要遵循的数据项背景来描述你的操作范围。

首先要做的就是用静态方法 Open 打开一个 ItemContext。Open

方法的无参数版本会打开用于所有数据项的上下文,但你可以用其它冲载方法限制上下文的的范围。WinFS

使用的命名方案使你可以确定卷标和反斜线分割的文件夹路径。

ItemContext 提供了通过 BeginTransaction 和 EndTransaction 存取数据连接的方法,BeginTransaction

创建一个新的事务并返回一个 Transaction 对象。一旦你做完这一步,你在该事务项下对存储的所有修改将会被执行,直到你调用 EndTransaction

并将所有对存储的更改当作一种原子行为。

需要提醒的是:当你完成存取时务必要调用 ItemContext 上的 Close 释放数据连接。如果你对数据进行了修改,一定要调用 Update 来确保所有的修改都被保存。在WinFS的预览版中

,如果创建了文件夹对象,务必先保存然后才可以向该文件夹添加数据信息。为此可以调用Folder.Save,这个方法仅仅是 ItemContext.Save 的一个

封装而已。

在文件夹中容纳数据项

大量的对象范畴都可以通过文件夹来完成,WinFS文件夹被用于容纳数据项,但它与Win32的文件系统中的文件夹的概念不一样。在 WinFS

中,单个的数据项可以存在于一个以上的 WinFS

文件夹中。如果你想创建自己的文件夹层次,必须先将你的文件夹添加到一个已经存在的文件夹中。为此,你得将容器文件夹对象和新的文件夹名作为构造函数的参数来创建一个新的

Folder 对象。容器文件夹可以是任何现有的文件夹,你可以通过获取根文件夹横贯整个层次(调用 Folder.GetRootFolder

即可),然后列举根文件夹包含的所有文件夹对象。特别指出的是,不能在最顶层文件夹下保存数据项,而是要在更深层的文件夹中保存数据。

每个卷的根文件夹中有四个默认的文件夹:系统数据(System Data)、卷(Volume)、方案(Schema) 和 存储信息(Store

Information)。你打算创建的文件夹应该在 SystemData 下面。为此,你可以先搜索 SystemData,再打开

SystemData 文件夹,然后往里添加文件夹。使用 WinFS API 较容易存取专门为你的数据项分配的文件夹,静态方法

UserDataFolder.FindMyUserDataFolder 返回当前用户专用的 SystemData

项下的文件夹,某些数据项类型具有它们自己标准的文件夹。例如,UserDataFolder.FindMyPersonalContactsFolder

方法返回一个叫做 Personal Contacts 的文件夹。

一旦你可以存取文件夹之后,便可以用 Folder.AddMember 方法往里放数据(包括其它文件夹)。删除数据项也很简单——调用

Folder.RemoveMember 方法,将数据项或数据项名传给它即可。当你把某个数据项放入文件夹时,你使用的就是一个固有链接(holding

link)。文件夹对象只不过是另外一个数据项,它为每个所包含的数据项把持一个固有链接。固有链接很特别,因为它确定引用的数据项的生命期,而且某个数据项上的每个固有链接都是一个引用计数。固有链接只能被应用于某个数据项,并突出数据项于嵌套元素之间的差别。当某个数据项上的所有固有链接被删除后,该数据项也被删除。WinFS

中的文件夹不一定要强制为其所包含的数据项的类型。也就是说如果你的文件夹叫做“Summer Vacation

2003”,它也可以保存所有你在假期里拍摄的照片、e-mail信息回复、书籍,甚至是 Madonna 的“Holiday” WAV文件。

存取文件夹中的数据项很简单:文件夹数据项有一个属性叫Members,它实际上是一个 Link项的集合。每个Link都是固有链接,其SourceItem

属性提供了对相应数据项的存取。此外,Folder 类也有可以存取特定名称或特定类型的成员。

数据项的搜索

一旦有了上下文并确定了数据项所在的文件夹,接着就是数据项的存取,最简单是遍历文件夹中所有的数据项,然后逐一比较,如

Figure 3 所示,FindResult

对象包含 GetAllMembers完成的查询结果,它应该包含所有该文件夹中的所有数据项。WinFS 为每一个数据项创建类型为 Item 的 CLR

对象,并将它们放入 FindResult 对象。当然,GetAllMembers 是个相当粗糙的工具,但你可以通过使用

GetAllMembersOfType 只返回文件夹中的 Person 数据项来提炼这个处理过程。

using (FindResult result

= myDataFolder.GetAllMembersOfType(typeof(Person)))

{

foreach(Person person in result)

{

if (person.DisplayName == "Richard Grimes")

{

// use person here

break;

}

}

}

通过向 Find 方法之一传递过滤条件,搜索可以被进一步提炼:

Person person = (Person)myDataFolder.GetOneMemberOfType(

typeof(Person), "DisplayName=''Richard Grimes''");

这个方法完成了前面代码所做的全部工作。它在一个特定的文件夹里搜索特定类型的对象,这个特定的类型还具有过滤串中指定的属性值。过滤串用一种新的叫

WinFS OPath 的查询语言写成。你可以提供一个过滤条件来检查被传递到方法中的该类型的属性,此处我实现了一个对

Person.DisplayName 属性的检查。在搜索期间,WinFS 会吸取它找到的每一个 Person 对象的 DisplayName

属性并将它与我给出的文字串进行比较。WinFS OPath非常灵活,允许你用 && 或者 || 操作符按多个条件查询。此外,你甚至可以用 like

操作符:

FindResult result = myDataFolder.GetAllMembersOfType(

typeof(Person), "DisplayName like ''R% Grimes''");

上面语句将会返回所有 DisplayName 中以‘R’开头并且名叫“Grimes”的 Person 数据项。

一些属性是集合,因而 WinFS OPath 提供一种语法允许集合中数据项的属性值。例如,Person 类有 PersonalNames

的属性,该属性是 FullName 对象的集合(允许某些人可以有一个以上的名字)。如果想搜索所有个人名字为 Grimes 的人,可以使用下面的方法:

FindResult result = Person.FindAll(

ctx, "PersonalNames[Surname=''Grimes'']");

这里的方括号表示 PersonalNames

是一个集合,我们将集合中每一项的Surname与给定字符串进行比较,找出符合条件的项。上述例子中:我们使用了静态函数-Person.FindAll,这个方法查找给定上下文中所有

Person 数据项。

数据项类可以基于数据项的 ID 进行搜索,也可以根据特定的种类(category)进行搜索。

这里所说的种类指的是依附于某个数据项上的名称。数据项都有一个属性叫

Categories,这个属性即是依附于该数据项上的所有种类的一个集合,每一个种类都是一个 CategoryRef 对象。Categories 有个

Key 属性,它是表示该种类的名称,还有一个 Type 属性,它是标示该种类的GUID。你可以创建自己的种类,或者将预定义的种类作为 GeneralCategories

类型的静态成员来使用。为了给某个数据项添加种类,必须创建新的 CategoryRef 对象并添加到 Categories

属性中。你不能简单地添加一个 GeneralCategories 成员到该数据项。Figure 4 中的代码在运行时回失败,因为在存储中 Categories 的成员是嵌套元素,被单个数据项所拥有,不能在数据项之间共享。嵌套元素有一个

ItemKey 属性,每个嵌套元素的这个值是唯一的。在 Figure 4 中 WinFS 代码会尝试用相同的 ItemKey 向两个数据项添加嵌套元素。Figure 5 中的解决方法简单明了:新的 CategoryRef 对象被添加到 Categories 属性,并用来自 GeneralCategories

类的一个值初始化。新的 CategoryRef 对象被创建后,它就有一个唯一的 ItemKey。

如果你知道了你的数据的分类,便可以用 FindByAllCategories、FindByAnyCategory 或者 FindByCategory

查找具有全部指定种类的数据项,以及任何指定的种类之一或者单一指定种类的数据项。

通知

使用 WinFS 的时候,常常使你感觉不到正在存取的数据信息就是某个数据存储(data store)中的数据项。WinFS

在这时可以明显地通知你。作为命名建议,WinFS 通知机制是一种允许你编写代码,以便当数据改变时通知你。WinFS 使用.NET

事件模型预定和处理通知。预定的有效期一直到应用程序退出或应用程序取消预定。ItemWatcher

对象可以被创建用来监视特定的数据项,该对象有一个事件叫 ItemChanged:

public event ItemChangedEventHandler ItemChanged

该委托有一个 ItemChangedEventArgs 参数。ItemChangedEventArgs.Details 为 ItemChangedDetail

对象集合,它表示发生在该数据项上变化的类型。所以,一个应用程序可以存取某个储备信息并添加该数据项的 ItemChanged

处理例程。当另一个应用程序修改该数据项时,第一个应用程序的 ItemChanged 事件便被触发。

侦听应用程序只需要添加一个对 ItemWatcher.ItemChanged

事件的委托,任何时候只要数据项被改变,该应用程序则会收到通知。这种变化可能来自相同线程中的代码,也可能来自另外一个线程,或者另外一个机器。如果变化来自另一台机器,那么在你的机器上这种变化是由于同步产生的。

同步

前面已经提到,WinFS 是一个数据存储服务,因而很自然地它就支持将数据复制到其它机器上的另外一个存储:对 WinFS

来说就是同步。为了实现同步,WinFS 首先要确定什么数据已经改变,然后决定需要复制什么数据。在 WinFS

中,元素是数据项存储方案中最小的处理单位。如果某个数据项的元素改变了,WinFS 就会知道该数据项已经改变。数据项是 WinFS

数据一致性的最小单元,因而它们是可以被复制到另外一台机器上的数据的最小单元——某个数据项要么被完整复制,要么根本就没有复制。

用户必须确定参与同步的机器。为此,用户创建一个可以被所有机器使用的共有文件夹,并将它关联到本地机器的实际文件夹。应用程序可以通过WinFS

API来启动同步,也可作为一个调度的任务来处理。两种情况,启动该行为的应用程序都会为 WinFS

提供一个同步配置文件。该配置文件中有关于被同步的文件夹的信息:包括同步方向(仅发送、仅接收或者发送和接收)以及应该被同步的数据项。此外,配置文件还包含一些信息,告诉你如何处理冲突。冲突策略可以简单地拒绝复制,它可以通过一段称为冲突处理器的代码自动处理,也可以人为干预。

WinFS 和 Win32 的联系

WinFS 是令人兴奋的、功能丰富的工具,开发者开发新的应用可以充分利用其优点。当然了,被移植到 longhorn 的大多数应用都留有

Win32 的痕迹,因而会出现两者怎样协作的问题。

首先要指出的是 WinFS 将取代 NTFS—— 运行 Longhorn 的机器仍然具备基于 NTFS 的驱动器。当前 WinFS

仅应用于“Documents and Settings”中的文件夹。所有其它的文件夹仍然由 NTFS 控制。特别是 %systemroot% 和

Program Files 仍然是 NTFS 文件夹,也就是说实际的可执行文件不存在于 WinFS 存储中。

当然,基于 Win32 的程序仍然可以读写“Documents and Settings”文件夹中的文件。在Longhorn 中已经实现的

Win32 文件处理 API 充分利用了 WinFS 存储中数据项的优点。Documents and Settings 中的所有从 Win32

移植而来的数据项都是有文件备份的,也就是说 WinFS

的数据项将会存在一个文件表示形式。已知的文件类型将都会有一个关联的元数据处理器,它负责读取文件和解析以属性形式保存的元数据。例如:与

Microsoft Word 关联的元数据处理器将加载某个 Word 文件,然后读取 SummaryInformation OLE Property

(OLE 属性信息)集并在 WinFS 存储中用这些信息初始化 Document WinFS 类型。

存储中以备份文件形式存在的数据项和元数据属性的二元性意味着 Win32 可以继续存取 “Documents and Settings”中的

数据项,并且 WinFS 可以用属性来构造一个查询。这些属性在 WinFS 中是只读的。当基于 Win32 的程序修改了某个文件时,元数据处理器便会

用新的属性值更新的存储中数据。在实际应用中,当你向运行 WinFS 的机器上的 My

Documents 文件夹拷贝某个 Word 文档时,文件信息将被保存到 WinFS 存储中,以便应用程序能进行基于文件属性的搜索。不管怎样,Win32 仍然可以存取

该文件,因而 Word 字处理器既可以加载文件,也可以编辑该文件。当 Win32 程序修该了该文件时,WinFS 读取更新的属性并将它们写入存储中。

所以当你向

Word 文档添加一页并保存该文档时,WinFS 会更新 Document.DocPageCount 属性,以便基于该属性的搜索按照期望执行。

总结

作为新的 Longhorn 文件存储系统,WinFS 是 Longhorn 上所有应用的基石,我在本文中只是肤浅地勾勒出其表面功能。WinFS 提供了

所有你需要持续化数据项的特性,数据项可以被归类,可以根据数据项类型的属性来搜索。你的任务只是使用这些工具来创建新一代的基于Windows 的应用程序。

作者简介

Richard Grimes 概括了.NET 的框架 。他是Programming with Managed Extensions for

Microsoft Visual C++ .NET(微软出版社,2002年)和Developing Applications with Visual

Studio .NET()的作者。他的关于Longhorn的开发书籍将会在Longhorn发行时非常有用。

联系方式:longhorn.dev@grimes.demon.co.uk.

本文出自

MSDN Magazine

January 2004 期刊,可通过当地

报摊获得,或者最好是 订阅

本文由 VCKBASE MTT 翻译

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
 
© 2005- 王朝網路 版權所有 導航