The Wonderful World of Schemas


Your first consideration when building an Exchange Server application is whether to use the schema support that's built into the product. As mentioned earlier, Exchange Server allows you to create schemas for your applications ”which means you can define classes of items, such as myproject or mycontact . On those classes of items, you can have default sets of properties, such as myname or mylocation .

You don't have to create schemas in order to use built-in or custom properties on items in Exchange Server. For example, you can append new properties to an item by using ADO and store that item in Exchange Server. You must create a custom schema if you want to reuse your properties across multiple items rather than with a single, one-off item. Also, if you want your users to easily discover your properties, you must use a schema. You can access your properties through ADO when you run a SQL SELECT statement against your application, or it can be with the Web Storage System forms you create. The Training application uses many custom properties across many items, so it creates schemas in its setup program. We'll discuss that part of the program next .

Overview of the Exchange Server Schemas

You need to know a few things before you attempt to create schemas. First, Exchange Server ships with some schemas already in place for the default items it understands, such as messages, documents, appointments, and contacts. The schemas for these items are stored in a hidden folder in your Exchange server. You can view this hidden folder by writing a simple program or by looking at the folder through the MDBVUE utility. Figure 15-17 shows how to view the Schema folder using MDBVUE.

click to expand
Figure 15-17: MDBVUE browsing the hidden Schema folder for Exchange Server.
Note  

You'll find the Schema folder under the Public Folders tree in the non-ipm subtree . This folder contains XML files that define the Exchange Server schemas. Exchange Server supports defining schemas through XML, but in the setup program for the Training application, I used ADO to create my schema definitions.

Second, you should understand the idea of a content class as part of the base Exchange Server schema. For example, a message has a content class of urn:content-classes:message . Content classes are groupings of properties that define a certain type of item. If you set the content class of a particular item to message , the item will receive all the properties from your schema that are associated with the message content class.

If you're coming from the Exchange Server 5.5 development environment, you might be wondering how the content class and the message class are related . They actually have no explicit relationship; you can have a content class that's entirely independent from a message class. If you need to support Outlook or previous versions of OWA, just be sure to explicitly set both the content class and the message class in your application. Don't worry about the custom properties in your content class. Exchange Server makes those available in the PS_PUBLIC_STRINGS namespace in MAPI so that Outlook or any MAPI application can retrieve those custom properties.

Note  

Outlook supports field names of up to 32 characters in its Field Chooser. If you create a property with a name longer than 32 characters , you might not be able to retrieve it from Outlook. Also, Outlook does not automatically recognize new properties added via ADO or CDO. To get these properties to display properly in Outlook, create a new field in Outlook with the same name as the field you created using ADO or CDO. Then add this new property to your Outlook view to check whether the property value is correctly displayed.

Creating Custom Content Classes

The first step when you create a custom content class is to decide what to call it. Be sure the name you select for your content class doesn't clash with other content class definitions. After you name your content class, you'll want to think about the properties contained in it. Exchange Server supports a wide range of built-in content classes, so you can inherit your content class from an existing one. For example, the Training application has training events. These events require properties that already exist on the appointment content class. However, the training events also need some extra properties that will exist on all training event items. Using inheritance, the Training application inherits the properties from both the appointment content class and the item content class.

Exchange Server supports multiple inheritance for content classes. Here are the default content classes that Exchange Server supports:

  • urn:content-classes:appointment

  • urn:content-classes:calendarfolder

  • urn:content-classes:calendarmessage

  • urn:content-classes:contactfolder

  • urn:content-classes:contentclassdef

  • urn:content-classes:document

  • urn:content-classes:dsn

  • urn:content-classes:folder

  • urn:content-classes:freebusy

  • urn:content-classes:item

  • urn:content-classes:journalfolder

  • urn:content-classes:mailfolder

  • urn:content-classes:mdn

  • urn:content-classes:message

  • urn:content-classes:notefolder

  • urn:content-classes:object

  • urn:content-classes:person

  • urn:content-classes:propertydef

  • urn:content-classes:recallmessage

  • urn:content-classes:recallreport

  • urn:content-classes:taskfolder

Once you figure out which content classes you want to inherit from, you'll need to create some property definitions for your content class. Property definitions describe the name, type, value, and special characteristics of the properties you want in your content class. Be aware that a property definition you create can be used across multiple content classes. You'll see this in the Training application, in which both instructors and training events share survey result property definitions.

Let's look at some code from the setup program that creates the property definitions for a training event. We'll then examine exactly what's happening in the code.

    CreateSchemaPropDef "instructoremail", "string", False, True, _                     False, CStr("") CreateSchemaPropDef "prereqs", "string", False, True, False, CStr("") CreateSchemaPropDef "seats", "string", False, True, False, CStr("") CreateSchemaPropDef "authorization", "string", False, True, False, _                     CStr("") CreateSchemaPropDef "category", "string", False, True, False, CStr("") CreateSchemaPropDef "surveycount", "string", False, True, False, CStr("") CreateSchemaPropDef "discussionurl", "string", False, True, False, _                     CStr("") CreateSchemaPropDef "materialsfilepath", "string", False, True, _                     False, CStr("") CreateSchemaPropDef "materialshttppath", "string", False, True, _                     False, CStr("")    Private Sub CreateSchemaPropDef(strName, strType, bMultiValued, _                                 bIndexed, bReadOnly, varDefaultValue)     Dim rec As New ADODB.Record     With rec         .Open strAppSchemaFolder & "/" & strName, , adModeReadWrite, _               adCreateNonCollection + adCreateOverwrite              'Create new property definition         .Fields("DAV:contentclass") = "urn:content-classes:propertydef"              'Give it a name         .Fields("urn:schemas-microsoft-com:xml-data#name").Value = _                 strSchema & strName              'Set the data type for the property         .Fields("urn:schemas-microsoft-com:datatypes#type").Value = _                 strType              'Set the other fields for the property         .Fields("urn:schemas-microsoft-com:" & _                 "exch-data:ismultivalued").Value = bMultiValued         .Fields("urn:schemas-microsoft-com:" & _                 "exch-data:isindexed").Value = bIndexed         .Fields("urn:schemas-microsoft-com:" & _                 "exch-data:isreadonly").Value = bReadOnly         .Fields("urn:schemas-microsoft-com:" & _                 "exch-data:default").Value = varDefaultValue         .Fields.Update         .Close     End With End Sub 

This code creates a new item for my property definition in the Schema folder for the Training application. I recommend that you avoid modifying the built-in schemas for Exchange Server unless absolutely necessary (if you wanted a new property on all default messages, for example). Instead, you can inherit from them. Create a Schema subfolder for your applications and place your property and content class definitions in it.

Note  

You must create property definition names in a unique namespace. The setup program asks you what schema namespace you want to use. By default, you should include your Internet domain name, if you have one, because it's guaranteed to be unique. For example, a good namespace might be http://yourdomainname/schema/ . You can then append to that namespace the names of the custom properties for your application.

You should also be aware that property names are case sensitive. I recommend lowercasing your entire property name to make your coding easier.

Once the new item is created, the code sets the content class property in the DAV namespace to urn:content-classes:propertydef . Exchange Server defines a number of new namespaces for properties, such as DAV , http://schemas.microsoft.com/exchange/ , or urn:schemas-microsoft-com:office:office . The code then sets the urn:schemas-microsoft-com:xml-data#name property to the fully qualified name of the property. Next the code sets the data type, which can be one of a number of values. The most common ones you'll use are String , Float , Boolean , DateTime , i2 , i4 , i8 , Uuid , Int , and Bin.base64 . You should always use the most suitable data types in your application.

Once the data type is set, you can set other values for your properties. One such value is the ismultivalued property. By setting this property to True , you tell the Web Storage System that the property supports multiple values of the data type you specified. When you work with multivalue properties, you should set and get the values by using arrays. Examples of built-in multivalue properties are urn:schemas-microsoft-com:xml-data#element and urn:schemas-microsoft-com:xml-data#extends , which are properties on the content class definition item. We'll discuss creating a content class definition item shortly.

Another property you can set is the isindexed property. This property has nothing to do with content indexing (which we'll look at later); instead, it corresponds to the Exchange Server database index. If you set this property to True , Exchange Server will create an index for the property so that sorting, grouping, and other operations on it execute quickly. One caveat about using isindexed : if you are concerned about the load on your Exchange server, don't set this property to True for all your properties. Instead, just determine which properties you really need indexing on, and set this property to True for those properties. To create the index for your custom properties, you must issue the CREATE INDEX command in ADO. You'll learn more about this command and ADO access to Exchange Server later in this chapter.

The next property you can set is the isreadonly property. You don't have to set this property because only properties provided by the Web Storage System can be set to read-only. However, I threw in this property just to expose you to it. You can query the value of this property from your applications to see whether a Web Storage System property is read-only.

The final property you'll want to set is the default property. This property specifies what default value, if any, your property should have when it's created by the application or the user . You don't have to set this property, but you should do so in order to initialize your properties to a default value.

Once the code finishes setting these properties using ADO, a call to the Update method of the Fields collection is required. When I cover ADO support in Exchange Server later in the chapter, we'll discuss the use of transactions with ADO and Exchange Server. I highly recommend that you use transactions where appropriate in your application in case you receive an error while processing your ADO commands. You can then roll back the transaction rather than leave your application's data in an inconsistent state. Although the previous code segment doesn't show it, the entire schema creation process is wrapped in a local OLE DB transaction.

Creating Content Class Definition Items

Now that we've created the custom property definitions for our content class, we need to create a content class definition in our schema. To do this, we create an item in the Schema folder that has the correct content class and properties set so that Exchange Server knows we want to create a custom content class. The following code shows how to create a definition of a custom content class:

    arrTrainingEventProps = BuildSchemaArray(Array("instructoremail", _     "prereqs", "seats", "authorization", "category", "surveycount", _     "discussionurl", "materialsfilepath", "materialshttppath", _     "publiccomments", "overallscore")) CreateContentClass "trainingevent", _     "urn:content-classes:trainingevent", _     Array("urn:content-classes:item", "urn:content-classes:appointment"), _     arrTrainingEventProps    Private Function BuildSchemaArray(arrProperties)     Dim tmpArray()     For i = LBound(arrProperties) To UBound(arrProperties)         ReDim Preserve tmpArray(i)         tmpArray(i) = strSchema & arrProperties(i)     Next     BuildSchemaArray = tmpArray End Function      Private Sub CreateContentClass(strItemName, strContentClassName, _                                arrExtends, arrFields)     Dim rec As New ADODB.Record     With rec         'Create item in application Schema folder         .Open strAppSchemaFolder & "/" & strItemName, , adModeReadWrite, _               adCreateNonCollection + adCreateOverwrite         '         'Create new content class definition         '         .Fields("DAV:contentclass") = _                 "urn:content-classes:contentclassdef"              'Name the content class         .Fields("urn:schemas-microsoft-com:xml-data#name").Value = _                 strContentClassName              'Enter what other content classes this one extends         .Fields("urn:schemas-microsoft-com:xml-data#extends").Value = _                 arrExtends              'Enter the fields that exist on the content class         .Fields("urn:schemas-microsoft-com:xml-data#element") = arrFields              'Save change         .Fields.Update         .Close     End With End Sub 

The code first calls the function BuildSchemaArray to create an array containing the custom schema properties that the new content class will contain. This is an example of how to create values for a multivalue property.

Once the array is built, the code calls the CreateContentClass subroutine, which creates a new item in the Schema folder using ADO. The code sets the content class of the item to urn:content-classes:contentclassdef , which tells Exchange Server that this is a new content class definition item. The code then sets the name of the new content class definition to the name passed to the subroutine. In this example, the content class name is urn:content-class:trainingevent .

The code sets the extends property next. This multivalue string property contains a list of all the other content classes that this content class inherits from. A content class can either have no inheritance or can inherit from one or more content classes. What you want to accomplish in your application and which properties you need on your custom content class will dictate which content classes you inherit from. Because the trainingevent content class is so similar to the appointment content class in terms of properties required, the code inherits from the appointment content class. The code also inherits from the item content class, which most other content classes inherit from.

Finally, the code sets the element property. This is a multivalue property that should contain all the custom fields that your content class implements. Once this property is set, you can call the Update method on the ADO Fields collection to write the data to Exchange Server.

You can also use XML to create content class definitions and property definitions. If you create a correctly structured XML document and place it into the schema folder for your application, Exchange Server will allow you to use these new content classes and properties. The following example shows what two XML files ”one for the content class definition and one for the property definition ”look like in XML:

 This XML file is for the content class definition <?xml version="1.0"?> <Schema     name='ExchangeSchema'     xmlns="urn:schemas-microsoft-com:xml-data"     xmlns:d="DAV:" xmlns:ex="http://schemas.microsoft.com/exchange/"     xmlns:cc="urn:content-classes:"     xmlns:dt="urn:schemas-microsoft-com:datatypes"     xmlns:s="urn:schemas-microsoft-com:exch-data:"     xmlns:m="urn:schemas:httpmail:"     xmlns:h="urn:schemas:mailheader:" xmlns:cs="http://customschemaname/">     <ElementType name="cs:customprop" d:contentclass="cc:contentclassdef">         <extends type="cc:item"/>         <element type="cs:mycustompropname"/>     </ElementType> </Schema>      This XML file is for the property definition <?xml version="1.0"?> <Schema     name='ExchangeSchema'     xmlns="urn:schemas-microsoft-com:xml-data"     xmlns:d="DAV:" xmlns:cc="urn:content-classes:"     xmlns:dt="urn:schemas-microsoft-com:datatypes"     xmlns:s="urn:schemas-microsoft-com:exch-data:"     xmlns:m="urn:schemas:httpmail:" xmlns:cs="http://customschemaname/">     <ElementType         name="cs:PNAC"         dt:type="string"         s:ismultivalued="0"         d:contentclass="cc:propertydef"         s:isindexed="1"         s:isreadonly="0"         s:isrequired="0"         s:isvisible="1">     </ElementType> </Schema> 

The expected-content-class Property

You must take a few more steps when you use custom schemas. First you must set the property urn:schemas-microsoft-com:exch-data:expected-content-class on the folders for your application. This multivalue property tells Exchange Server what content classes to expect in the folder. This property can specify multiple content classes. The query processor in Exchange Server uses this list of expected content classes when you issue a SELECT * statement in the folder to determine which properties ”besides the built-in schema ones ”the query should return.

You should try to limit your use of SELECT * statements because the query processor has to retrieve every built-in and custom property in your content class, which can take a long time. If you know the specific properties you need to retrieve as part of your result set, you should specify them in your query. Here's an example of setting the expected-content-class property in code:

 oFolderRecord.Fields _   ("urn:schemas-microsoft-com:exch-data:expected-content-class").Value = _    Array("urn:content-classes:appointment", _    "urn:content-classes:mycontentclass") 

The schema-collection-ref Property

In addition to setting the expected content class property, you need to set the urn:schemas-microsoft-com:exch-data:schema-collection-ref property on your application's folders. This property contains a URL that links to the first folder in which you want the Exchange server to look for schema definition items. In your custom applications, you set this property to the Schema subfolder. When we discuss using ADO with Exchange Server later in the chapter, you'll see how to determine the URL that will be the value of this property. If you do not set this property on your folders, Exchange Server will default to its built-in Schema folder in the non-ipm subtree. You can also use the ##SCHEMAURI## macro, which we will discuss later in this chapter. Here is an example of setting this property in code:

 oFolderRecord.Fields _   ("urn:schemas-microsoft-com:exch-data:schema-collection-ref").Value = _    "file://./backofficestorage/domain/public folders/myapp/schema" 

The baseSchema Property

You have one final task to perform to make your application's schema work well: setting the urn:schemas-microsoft-com:exch-data:baseschema property on your schema subfolder. This multivalue property provides URLs that Exchange Server will search if it doesn't find schema definitions in your schema subfolder. The server will search the set of URLs you provide in the order they're set in the property. So if Exchange Server finds the correct schema definition, it will stop searching subsequent URLs. By setting the schema-collection-ref property for your folders, you can have Exchange Server search your schema folder first and then search the built-in schema folder. The following is an example of setting this property:

 strBaseSchemaFolder = _     "file://./backofficestorage/domainname/public folders/" & _     "non_ipm_subtree/schema/" 'Or you can use ##SCHEMAURI## discussed below strBaseSchemaURI = "http://server/virtdir/##SCHEMAURI##/microsoft/ExchangeV1" With oSchemaFolder     .Fields("urn:schemas-microsoft-com:exch-data:baseschema") = _             Array (strBaseSchemaFolder)    .Fields.Update End With 

Using the ##SCHEMAURI## Macro

When working with global schema, such as the schema located at http://server/public//non_ipm_subtree/schema/ , you should use the ##SCHEMAURI## convention to point at the schema. A number of problems can arise if you use hardcoded schema paths. First, accessibility is nondiscoverable. If you point your base schema to http://server/public/non_ipm_subtree/schema/ while in a non-MAPI top-level hierarchy (TLH), rather than the Public Folders hierarchy, you will get an error because Exchange does not support crossing database boundaries. Your non-MAPI TLH is in a separate database from your MAPI TLH.

Also, when new versions of the schema are installed, such as with Microsoft SharePoint Portal Server 2001, which extends the Exchange schema with new properties and content classes, having a single schema folder does not provide for a scalable environment. Finally, if you create a virtual root that points to the middle of a folder hierarchy (such as a virtual root called Foo) and the root points directly to a folder called Bar in the third level of the folder hierarchy, you cannot point to the base schema folder if you did not create a virtual root that points at the root of the TLH.

##SCHEMAURI## is a macro that is expanded by the Web Storage System when you make a request for a schema lookup; you can use this macro instead of hardcoding the path to a base schema folder. Because Exchange supports versioned schema folders, you will see multiple schema folders depending on which applications you install on the server. If you install Exchange and SPS 2001 on the same machine, there will be multiple versioned schemas because SPS extends the Exchange schema, as mentioned earlier. Here are some examples of uses of ##SCHEMAURI##:

  • http://server/public/##SCHEMAURI## is the location of the root for the base schema introduced in Exchange 2000 and is equivalent to the old path http://server/public/non_ipm_subtree .

  • http://server/public/##SCHEMAURI##/microsoft/ExchangeV1 is the location of the schema folder corresponding to the Exchange 2000 version of the schema and is equivalent to http://server/public/non_ipm_subtree/schema/ .

  • http://server/sharepoinportal server/##SCHEMAURI##/microsoft/TahoeV1t is the location of the schema folder corresponding to the SPS 2001 version of the schema.

  • http://server/public/##SCHEMAURI##/default is an empty folder made available to add any non-Microsoft schemas and to create subfolders of custom schemas.

    Note  

    The ##SCHEMAURI## will not be supported in the private store (mailboxes) on Exchange.

If you are building an application on Exchange, I recommend using the macro instead of the non_imp_subtree way of accessing schemas because this new macro will be the default location to perform schema discovery. In the case of SPS, the non_ipm_subtree will be inaccessible, and you should always use ##SCHEMAURI##.

If you are using a non-MAPI TLH, you must replace public with the vroot that points to your non-MAPI TLH or use relative URL paths. The ##SCHEMAURI## needs to know which TLH to search to find the base schema. You can also use the ##SCHEMAURI## in setting your SCR and baseschema properties. An example might make this clearer. The Training application has been updated to take advantage of the new ##SCHEMAURI## code. The following code generates the schema properties for the application using this new macro:

 'Figure out base schema folder 'Schema folders are stored in each folder tree 'Pull it off the path entered by the user strBaseSchemaFolder = "http://" & strServerName & "/" & strRootFolder _                     & "##schemauri##/microsoft/ExchangeV1" With oSchemaFolder     .Fields("urn:schemas-microsoft-com:exch-data:baseschema") = _                     Array(strBaseSchemaFolder)     .Fields.Update End With 

The code points to the ExchangeV1 schema folder because it uses only content classes and properties from that schema. SPS installs this schema folder as well as its own schema folder. The Training application will work on either Exchange or SPS because it uses the default Exchange base schema, which is available on both systems.

You can also use relative paths when you set the base schema or schema collection reference properties, which means the following path is valid as well:

 urn:schemas.microsoft.com:exch-data:baseschema =  _     Array("/##SCHEMAURI##/microsoft/ExchangeV1) 

Global Schema Definitions

You can also create database-wide properties, content classes, and form registrations by adding these items to the global schema folder. Here global means the default schema folder for each public folder TLH because schemas are per TLH, not per server. An example is the schema folder for the default public folders root, http://server/public/##SCHEMAURI##/microsoft/ExchangeV1 . This folder contains the default content classes and properties for that TLH (such as those added by Exchange 2003). One word of caution: working with the global schema can adversely affect your server's health and performance if you extend the global schema incorrectly.

Through code, the use of XML files, or tools, you can add new definitions to the global schema folder, and you can create your own schema folders (for example, http://server/public/##SHEMAURI##/default/myfolder ). Create new property definition items, set the isindexed attribute to TRUE , and store the item in a global schema folder.

Besides using ADO and Web Distributed Authoring and Versioning (WebDAV) to create the new definitions, you can also extend the global schema via XML. This is the way the schema is initially populated by Exchange Server itself. If you look in the Exchange Server directory under the schema directory, you will see a number of files with an .xml extension. These are the XML files used to populate the Exchange schema using a system event. If you want to use the same method, take the following steps:

  1. Create XML files to define your content classes, property definitions, or form registrations. (When you create the properties, remember to set the isindexed flag to 1 , which is Boolean true.) You can find great examples of these XML files in the Schema directory mentioned earlier.

  2. Copy your new XML files to the Exchange 2003 global schema folder (for example, http://server/public/##SCHEMAURI##/microsoft/ExchangeV1 ).

Once the items are stored in the schema folder, an OnSyncSave store event will kick off EXSCHEMA.EXE. This process will promote the XML documents to schema definition items.




Programming Microsoft Outlook and Microsoft Exchange 2003
Programming MicrosoftВ® OutlookВ® and Microsoft Exchange 2003, Third Edition (Pro-Developer)
ISBN: 0735614644
EAN: 2147483647
Year: 2003
Pages: 227
Authors: Thomas Rizzo

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net