Support for Tags

ScaleOut Software NamedCache API

A tag is a keyword associated with an object stored in StateServer. You can associate tags with objects, remove tag associations from objects and test for the presence of a tag on an object by using the extension methods defined in TagExtensions. Once a tag or set of tags have been associated with an object, the tags are stored as part of the object state and are also stored in the property index for the the object so that searches for objects with a given set of tags can occur on the server. This section provides an overview of how to use tags in the StateServer environment.

The ITaggable Interface

To associate tags with objects stored in a NamedCache, the object's type must implement the ITaggable interface. This interface requires an implementation that creates a well-known property, TagHolder, in the implementing object that the NamedCache's tag implementation uses to store a bitmap representing the tags associated with the object. Other than declaring the property, the implementating object does not need to supply any behavior in support of the object. Management of the tags is automatic and handled by the NamedCache subsystem.

The set of strings used as tags on objects is scoped by the NamedCache in which the objects are stored. Whenever you associate a tag with an object, the NamedCache checks to see if the tag is known within a tag dictionary managed by the object's NamedCache. If not, the tag is added to the dictionary which is then published via StateServer so that update is available to all servers in the server farm.

The TagExtensions extension methods such as AddTags and RemoveTags must, therefore, be able to access the NamedCache that a tagged object will be written to so it has access to the appropriate tag dictionary when adding, removing, or testing tags. The second property required by the ITaggable, interface, NamedCache, provides that access.

The Product class below implements ITaggable.

[Serializable]
class Product : ITaggable
{
    private static readonly NamedCache _namedCache = CacheFactory.GetCache("ProductCache");

    SparseBitmap ITaggable.TagHolder
    {
        get; set;    
    }

    NamedCache ITaggable.NamedCache
    {
        get { return _namedCache; }
    }

    public string Name { get; set; }
    public decimal Price { get; set; }
}
In this case, we chose to create an explicit interface implementation to avoid mixing up the properties having to do with the Product with the ITaggable implementation.

The implementation for TagHolder is just a compiler-generated getter/setter. The NamedCache implementation simply returns the NamedCache, instance cached in the static initializer.

Manipulating Tags on Objects

Since the Product class defined in the previous section implements ITaggable, we can use Product instances with the extension methods in TagExtensions. For example, here we create three Product instances and associate each of them with a set of tag values by using the AddTags method.

Product p1 = new Product()
{
    Name = "Beanbag Cellphone Chair",
    Price = 9.99m
};
p1.AddTags("Gadget", "cellphone", "cellphone stand");

Product p2 = new Product()
{
    Name = "Keychain Speaker Amp",
    Price = 9.99m
};
p2.AddTags("Gadget", "cellphone", "audio");

Product p3 = new Product()
{
    Name = "JuiceBar Portable Solar Charger",
    Price = 49.99m
};
p3.AddTags("Gadget", "cellphone", "charger", "solar");

We can test objects to see if all tags in a specified list are present on an object by using the HasAllTags method like this:

bool t = p1.HasAllTags("Gadget", "cellphone");            // true
bool f = p1.HasAllTags("Gadget", "cellphone", "audio");    // false
In this case, the first test returns true since Productp1 has both "Gadget" and "cellphone" associated with it. The second test return false because p1 does not have the tag "audio" associated with it.

Similarly, we can test objects to see if any tags in a specified list are present on an object using the HasAnyTag method like this:

bool t = p2.HasAnyTag("apples", "oranges", "audio");    // true
bool f = p2.HasAnyTag("apples", "oranges", "bananas");    // false
In this case, the first test returns true since Productp2 has the tag "audio" associated with it. The second test returns false because none of the specified tags are associated with p2.

We can test to see that certain tags are present while at the same time certain tags are missing by using the HasTags method like this:

string[] mustBePresent = { "Gadget", "audio" };
string[] mustBeMissing = { "solar" };
bool p1HasTagsTest = p1.HasTags(mustBePresent, mustBeMissing);    // false
bool p2HasTagsTest = p2.HasTags(mustBePresent, mustBeMissing);    // true
bool p3HasTagsTest = p3.HasTags(mustBePresent, mustBeMissing);    // false
With this method we supply a specific list of tags that must be present together with a specific list of tags that must not be present. The result value p1HasTagsTest is false because the Beanbag Cellphone Chair does not have the tag "audio" associated with it. The result value p2HasTagsTest is true because the Keychain Speaker Amp is an "audio" "Gadget" and not a "solar" one. The result value p3HasTagsTest is false because the JuiceBar Portable Solar Charger is not an "audio" product and also because it is a "solar" product.

Note Note

The HasAllTags, HasAnyTag, and HasTags methods may also be used in LINQ where clauses in which case the methods are executed remotely on the server. See Querying Tags for more details.

Other Tag Operations

We can enumerate the set of tags currently associated with an object by using the GetTags method like this:

foreach (string tag in p3.GetTags()) {
    Console.WriteLine(tag);
}

You can remove a tag association from an object with the RemoveTags method like this

// Remove specified tags from the object - they don't have to exist 
// on the object
p2.RemoveTags("Gadget", "oranges");

To remove all tags from an object, you can combine GetTags with RemoveTags like this:

// Remove all tags on an object
p2.RemoveTags(p2.GetTags());

The strings used in tag operations are case-independent. If isGadget is true aftre executing the following example, then isStillGadget will be false.

bool isGadget = p1.HasAllTags("Gadget");
p1.RemoveTags("gadget");
bool isStillGadget = p1.HasAllTags("Gadget");

See Also

Reference

Other Resources