Extending
While documents provide us with a dynamic schema, they force us to give up .NET type-safety for working with type less key-value pairs. GigaSpaces supports extending the SpaceDocument class to provide a type-safe wrapper for documents which is much easier to code with, while maintaining the dynamic schema.
Do not confuse this with Document-Object interoperability, which is a different feature.
Creating the Extension Class
Let's create a type-safe document wrapper for the Product type described in the Document Support page. The extensions are:
- Provide a parameter less constructor, since the type name is fixed.
- Provide type-safe properties, but instead of using private fields to store/retrieve the values, use the index operator of the SpaceDocument class.
Here's an example (only parts of the properties have been implemented to keep the example short):
public class ProductDocument : SpaceDocument
{
private const String TypeName = "Product";
private const String PropertyCatalogNumber = "CatalogNumber";
private const String PropertyName = "Name";
private const String PropertyPrice = "Price";
public ProductDocument() : base(TypeName)
{
}
public String CatalogNumber
{
get { return this[PropertyCatalogNumber]; }
set { this[PropertyCatalogNumber] = value; }
}
public String Name
{
get { return this[PropertyName]; }
set { this[PropertyName] = value; }
}
public float Price
{
get { return this[PropertyPrice]; }
set { this[PropertyPrice] = value; }
}
}
Registering the Extension Class
If your only intention is to write/update document entries, creating the extension class is sufficient - from the space's perspective it is equivalent to a SpaceDocument
instance. However, if you attempt to read/take entries from the space, the results will be SpaceDocument
instances, and the cast to ProductDocument
will throw an exception.
To overcome that, we need to include the document wrapper type in the type introduction:
public void RegisterProductType(ISpaceProxy spaceProxy)
{
// Create type descriptor:
SpaceTypeDescriptorBuilder typeDescriptorBuilder = new SpaceTypeDescriptorBuilder("Product");
// ... Other type settings
typeDescriptorBuilder.DocumentWrapperType = typeof(ProductDocument);
// Register type:
spaceProxy.TypeManager.RegisterTypeDescriptor(typeDescriptorBuilder.Create());
}
This wrapper type-registration is kept in the proxy and not propagated to the server, so that from the server's perspective this is still a virtual document type with no affiliated Concrete object class.
Using the Extension Class
The following code snippet demonstrate usage of the ProductDocument
extensions we've created to write and read documents from the space.
public void example(ISpaceProxy spaceProxy)
{
// Create a product document:
ProductDocument product = new ProductDocument({CatalogNumber = "hw-1234", Name = "Anvil", Price = 9.99F});
// Write a product document:
spaceProxy.Write(product);
// Read product document using a template:
ProductDocument template = new ProductDocument({Name = "Anvil"});
ProductDocument result1 = spaceProxy.Read(template);
// Read product document using a SQL query:
SqlQuery<ProductDocument> query = new SqlQuery<ProductDocument>("Product", "Price > ?");
query.SetParameter(1, 5.5f);
ProductDocument result2 = spaceProxy.Read(query);
// Read product document by ID:
ProductDocument result3 = spaceProxy.ReadById(new IdQuery<ProductDocument>("Product", "hw-1234"));
}
Inheritance Support
SpaceDocument query supports inheritance relationships so that entries of a sub-class are visible in the context of the super class, but not the other way around. For example, suppose class EmployeeDoc extends class PersonDoc and PersonDoc extends from SpaceDocument
, you can register
the the sub classes in the following way:
SpaceTypeDescriptorBuilder spaceTypeDescriptorBuilder = new SpaceTypeDescriptorBuilder(
"Subclass Document Type Name", parentSpaceTypeDescriptor);
Here is an example:
namespace document
{
public class PersonDoc:SpaceDocument
{
public const String DocName = "PersonDoc";
public const String PropertyTypeId = "ID";
public const String PropertyFirstName = "FirstName";
public const String PropertyLastName = "LastName";
public PersonDoc () : base (DocName)
{
}
public PersonDoc (String type) : base (type)
{
}
public String Id {
get { return (String)this [PropertyTypeId]; }
set { this [PropertyTypeId] = value; }
}
public String FirstName {
get { return (String)this [PropertyFirstName]; }
set { this [PropertyFirstName] = value; }
}
public String LastName {
get { return (String)this [PropertyLastName]; }
set { this [PropertyLastName] = value; }
}
}
}
namespace document
{
public class EmployeeDoc : PersonDoc
{
public const String DocName = "PersonDoc";
public const String PropertyEmployeeNumber = "EmployeeNumber";
public EmployeeDoc () : base (EmployeeDoc.DocName)
{
}
public String EmployeeNumber {
get { return (String)this [EmployeeNumber]; }
set { this [EmployeeNumber] = value; }
}
}
}
public void registerDocument (ISpaceProxy spaceProxy)
{
// Create the Person descriptor
SpaceTypeDescriptorBuilder personDescriptor = new SpaceTypeDescriptorBuilder (PersonDoc.DocName);
personDescriptor.DocumentWrapperType = typeof(PersonDoc);
personDescriptor.SetIdProperty (PersonDoc.PropertyTypeId);
ISpaceTypeDescriptor personTypeDescriptor = personDescriptor.Create ();
// Register type:
spaceProxy.TypeManager.RegisterTypeDescriptor (personTypeDescriptor);
// Create the Employee descriptor
SpaceTypeDescriptorBuilder employeeDescriptor = new SpaceTypeDescriptorBuilder (EmployeeDoc.DocName,
personTypeDescriptor);
employeeDescriptor.DocumentWrapperType = typeof(EmployeeDoc);
ISpaceTypeDescriptor employeeTypeDescriptor = employeeDescriptor.Create ();
// Register type:
spaceProxy.TypeManager.RegisterTypeDescriptor (employeeTypeDescriptor);
}
}
namespace document
{
public class Program
{
public Program ()
{
// Create the Space Where GigaSpaces data is stored. It is the logical cache that holds data objects in memory and might also hold them in layered in tiering. Data is hosted from multiple SoRs, consolidated as a unified data model.
ISpaceProxy spaceProxy = new EmbeddedSpaceFactory ("mySpace").Create ();
registerDocument (spaceProxy);
PersonDoc doc1 = new PersonDoc ();
doc1 [PersonDoc.PropertyTypeId] = "1";
doc1 [PersonDoc.PropertyFirstName] = "John";
doc1 [PersonDoc.PropertyLastName] = "Fellner";
spaceProxy.Write (doc1);
EmployeeDoc doc2 = new EmployeeDoc ();
doc2 [PersonDoc.PropertyTypeId] = "2";
doc2 [PersonDoc.PropertyFirstName] = "John";
doc2 [PersonDoc.PropertyLastName] = "Walters";
doc2 [EmployeeDoc.PropertyEmployeeNumber] = "1234";
spaceProxy.Write (doc2);
SqlQuery<PersonDoc> query1 = new SqlQuery<PersonDoc> (
PersonDoc.DocName, "");
PersonDoc[] result1 = spaceProxy.ReadMultiple<PersonDoc> (query1);
// You should see two objects
Console.WriteLine (result1.Length);
SqlQuery<EmployeeDoc> query2 = new SqlQuery<EmployeeDoc> (
EmployeeDoc.DocName, "");
EmployeeDoc[] result2 = spaceProxy.ReadMultiple<EmployeeDoc> (query2);
// You should see one object
Console.WriteLine (result2.Length);
}
}