Document last updated: 5/10/2001 (samc) Using LAFcommon.COMCollection ============================= A COM "collection" is an object that exposes a specific minimum interface that allows the building of a conceptual collection of objects. The interface allows the members of the collection to be accessed by index or as a linear list that can be iterated through in a loop. Many collections, such as those found in ADO, contain specific types of objects and cannot be used as a general-purpose datastore. In ASP, however, a pair of collections are available for general-purpose use: "Session" and "Application". Within these two collections, arbitrary data may be stored and retrieved at a later date. The lifetime of the data thus stored is dictated by which object is used. "Session" will store data through the lifetime of the current visitor's visit. This is implemented by placing a cookie on the browser that functions as an index to the user's "Session" data. A user's data is deleted if the "Session" collection is not accessed within the time limit (20 minutes by default). The "Application" object is similar except that (1) it does not require a cookie, (2) all visitors to the site share the same "Application" data and (3) the data is not deleted until the IIS "Application" is unloaded (usually when the web server is shut down). "Session" seems to lend itself well to quick tasks where arbitrary data must be stored temporarily and accessed occassionally. Because it is indexed by arbitrary strings, it appears to function well as a replacement for an array to cut down on the time to search for a particular piece of data. Unfortunately, "Session" is not meant for this purpose. If the user does not have cookies enabled, it will not function properly. If the user somehow changes or obscures the value of their "Session" cookie, they could lose their data or access another user's data. Only one "Session" is available to each user, so care must be taken to remove all temporary data when it is no longer needed. Most importantly, "Session"'s performance is atrocious -- it bogs down very quickly under moderately large datasets and/or moderately heavy load. LAFcommon.COMCollection, on the other hand, was created with all of this in mind and corrects for many of "Session"'s deficiencies as well as providing capabilities that the "Session" paradigm simply does not allow for. Starting with the basics, LAFcommon.COMCollection stores Variants (in ASP, everything is a Variant; like it or lump it) and indexes them by strings. This means that like "Session", LAFcommon.COMCollection will store objects, arrays, strings, numbers or anything else. Unlike "Session", LAFcommon.COMCollection indexes everything very efficiently (using AVL trees), anticipating large datasets with lots of accesses. LAFcommon.COMCollection also allows the programmer to use case-sensitive indeces, to iterate through the indeces instead of the values and to specify the tree traversal order that is to be used when iterating through the collection. Where LAFcommon.COMCollection really shines relative to "Session" is in two ways. First, LAFcommon.COMCollection can be instantiated many times simultaneously, allowing the programmer to separate the data more efficiently. Second and most importantly, LAFcommon.COMCollection supports the concept of "shared collections" whereby two completely unrelated instances of LAFcommon.COMCollection can access the same data store through the use of an arbitrary index. These "shared collections" provide similar persistance across instances for LAFcommon.COMCollection data stores as "Session" does for all of its data but because the indeces are arbitrarily decided by the programmer, two users hitting ASP pages can use the same data store if they use the same index. Additionally, the expiration time for a "shared collection" can be set by the programmer, allowing a collection to persist unused for much longer than a "Session" object would be able to. NOTE: While LAFcommon.COMCollection provides an interface into a thread-safe shared data store, the LAFcommon.COMCollection interface itself is not thread-safe. This means that instantiating LAFcommon.COMCollection and using that object in multiple places simultaneously will result in unpredictable behavior (probably a bad crash). If two threads need access to the same data store, use the "shared collection" capability within LAFcommon.COMCollection instead of sharing the same object. NOTE: When shared collections are in use, keeping track of processes becomes important. Because LAFcommon is an in-process COM server, only applications running in the same process will have access to each other's shared collections. This means that in ASP, all applications that do _not_ have the "Run in separate memory space" check box checked will have access to the same shared collections (even across different websites!). However, each application that _does_ have the "Run in separate memory space" checked will have its own pool of shared collections, even if the applications in question are two virtual directories on the same website. Below, "collection-level" refers to a setting or value that is retained with the current collection. Another instance of LAFcommon.COMCollection with the same SharedIndex value will have access to the same "collection-level" values or settings. An "instance-level" setting or value is one that only effects the current instance of LAFcommon.COMCollection. Another instance, even with the same SharedIndex value, will not share the "instance-level" settings and values. The following properties are available: CaseSensitive (read/write, Boolean) CaseSensitive determines if LAFcommon.COMCollection sorts indeces in lexographic (case-sensitive) or alphabetic (case-insensitive) order. A collection's case sensitivity can only be changed if it is empty. The default is False. CaseSensitive is a collection-level setting. Count (read-only, Long) Count returns the number of objects in the collection. Count is a collection-level value. EnumerateKeys (read/write, Boolean) EnumerateKeys determines what items are enumerated when LAFcommon.COMCollection is used in a "For Each" loop. When set to False, the objects that are stored in the collection are enumerated. When set to True, the indeces are enumerated instead. The default is False. EnumerateKeys is an instance-level setting. EXAMPLE: The following VBScript ASP code: Set collection_obj = Server.CreateObject("LAFcommon.COMCollection") collection_obj("foo") = "one" collection_obj("bar") = "two" collection_obj("baz") = "three" collection_obj.EnumerateKeys = False For Each thing In collection_obj Response.Write(thing) Next Will produce the following output: two three one However, the following VBScript ASP code: Set collection_obj = Server.CreateObject("LAFcommon.COMCollection") collection_obj("foo") = "one" collection_obj("bar") = "two" collection_obj("baz") = "three" collection_obj.EnumerateKeys = True For Each thing In collection_obj Response.Write(thing) Next Will produce the following output: bar baz foo ErrorMessage (read-only, String) ErrorMessage provides an English explanation for the last error that occurred. If the last method/property call did not produce an error, ErrorMessage contains an empty string. ErrorMessage is an instance-level setting. Item(Variant) (read/write, Variant, default) Item is the property through which most activity takes place. It accepts a Variant argument that must be either a String, an Integer, a Long, a Float or a Double that is the index to use. On the left side of an assignment, it accepts a Variant to store in the index. Assigning the keyword Empty to Item will remove the given index from the collection. Used as a value, Item will return the Variant stored under the given index or Empty if the index was not found. Item is the default property, meaning that the following: collection_obj.Item("foo") = "one" is equivalent to: collection_obj("foo") = "one" Item is a collection-level value. MaxIdleSecs (read/write, Long) MaxIdleSecs sets the number of seconds a shared collection can persist unused before it is automatically deleted. MaxIdleSecs must be given a positive integer greater than 0 and must be called after SharedIndex has been set. The default is 1200 (20 minutes). MaxIdleSecs is a collection-level value. NamedConstant(String) (read-only, Long) NamedConstant translates the LAFcommon.COMCollection-related constant names provided in "lafcommon_vbs.txt" to their values. This function is much slower than using the defined constants and is intended only for use in environments where importing the values from "lafcommon_vbs.txt" is not possible (such as the Windows Scripting Host). SharedIndex (read/write, Variant) SharedIndex provides an index to a "shared collection" -- one that will persist after the current instance of LAFcommon.COMCollection has been destroyed. Shared collections are thread-safe, meaning that many independent threads may safely access the same shared collection simultaneously. SharedIndex accepts a Variant argument that must be either a String, an Integer, a Long, a Float or a Double that is the shared collection's index within the collection-of-collections. SharedIndex may be changed arbitrarily during the lifetime of a single instance of LAFcommon.COMCollection -- each method or property called will affect the collection referenced by the current SharedIndex value. Setting SharedIndex to Empty will cause the current collection to become the default non-shared collection. NOTE: Use of SharedIndex is discouraged unless absolutely needed -- shared collections are at least two times slower than their non-shared counterparts due to index searching and mutex locking (best case -- performance degrades with traffic volume). NOTE: Because all in-process IIS applications have access to the same pool of shared collections, using appropriate values for SharedIndex becomes important so that two completely unrelated websites do not interfere with one another. For that reason, it is recommended that the name of the website be for the first part of SharedIndex. An example might be: www.foocorp.com: Shopping Cart The default value is Empty. SharedIndex is an instance-level setting. ThrowErrors (read/write, Boolean) ThrowErrors has no effect on the behavior of LAFcommon.COMCollection's functionality, only on the behavior of this interface. If ThrowErrors is set to True, ATL errors will be generated whenever ErrorMessage is set to a non-empty value. The ATL error will contain the text of ErrorMessage. On an ASP page, unless "On Error Resume Next" is specified, an ATL error will stop processing and display the error message. ThrowErrors is an instance-level setting. TraversalOrder (read/write, Long) TraversalOrder determines the order in which indeces are enumerated in a "For Each" loop. The valid values and their meanings are (defined in "lafcommon_vbs.txt"): LAFCOMMON_traversal_inorder The tree is traversed "in order", which produces the output most callers expect -- a sorted list that starts with the smallest index. LAFCOMMON_traversal_preorder The tree is traversed "pre order", which gives each node's parent node before the children. LAFCOMMON_traversal_postorder The tree is traversed "post order", which gives each node's parent node after both its children. One of the only uses for this setting is for debugging purposes -- given two different traversals of the same tree, it is possible to reconstruct the shape of the tree. The default is LAFCOMMON_traversal_inorder. TraversalOrder is an instance-level setting. The following methods are available: Function Empty() As Long Empty() removes all entries from the current collection. No other settings are reset by this action (MaxIdleSecs, etc). Sub VersionInfo(ByRef Variant file_major, ByRef Variant file_minor, ByRef Variant file_revision, ByRef Variant file_build, ByRef Variant product_major, ByRef Variant product_minor, ByRef Variant product_revision, ByRef Variant product_build, ByRef Variant additional_info) VersionInfo() provides information on the version of LAFcommon that is running without requiring a visit to the server's console. The first eight parameters can be used to assemble the file version and the product version in the form W.X.Y.Z where W is the major version, X is the minor version, Y is the revision number and Z is the build number. The last parameter, additional_info, can be used to return other relevant information that the author saw fit to provide. The following return codes are defined: LAFCOMMON_COMCOLLECTION_success The operation succeeded. LAFCOMMON_COMCOLLECTION_fail_not_found The given index was not found. LAFCOMMON_COMCOLLECTION_error_variant_copy A call to VariantCopyInd() failed. This may indicate a memory corruption in the garbage collection system. LAFCOMMON_COMCOLLECTION_error_internal An unexpected error occurred, most likely in a call to CComObject::QueryInterface(). This may indicate a COM-related programming error. LAFCOMMON_COMCOLLECTION_error_bad_index The given index cannot be used. Indexes must be VARIANT-contained Strings (BSTRs), Longs (I4s) or Doubles (R8s). LAFCOMMON_COMCOLLECTION_error_mutex_timeout An attempt to lock a mutex timed out without succeeding. This may indicate a programming error or the system may be very busy. LAFCOMMON_COMCOLLECTION_error_cannot_become_sensitive The collection cannot become case-sensitive while it contains elements. Empty the collection before switching to case-sensitivity. LAFCOMMON_COMCOLLECTION_error_cannot_become_insensitive The collection cannot become case-insensitive while it contains elements. Empty the collection before switching to case-insensitivity. LAFCOMMON_COMCOLLECTION_error_bad_traversal_order An invalid traversal order was supplied. Check to ensure a pre-defined constant is being used. LAFCOMMON_COMCOLLECTION_error_bad_constant An invalid named constant was supplied. Check to ensure a pre-defined constant is being used. LAFCOMMON_COMCOLLECTION_error_not_shared This action is not possible unless the current collection has a shared index. LAFCOMMON_COMCOLLECTION_error_bad_max_idle_secs An invalid value for the maximum idle seconds was given. Valid values are positive integers.