The Revit API allows you to create your own class-like Schema data structures and attach instances of them to any Element in a Revit model. Schema-based data is saved with the Revit model and allows for higher-level, metadata-enhanced, object-oriented data structures. Schema data can be configured to be readable and/or writable to all users, just a specific application vendor, or just a specific application from a vendor.
The following steps are necessary to store data with Elements in Revit:
The first step to creating extensible storage is to define the schema. A schema is similar to a class in an object-oriented programming language. Use the SchemaBuilder class constructor to create a new schema. SchemaBuilder is a helper class used to create schemas. Once a schema is finalized using SchemaBuilder, the Schema class is used to access properties of the schema. At that stage, the schema is no longer editable.
Although the SchemaBuilder constructor takes a GUID which is used to identify the schema, a schema name is also required. After creating the schema, call SchemaBuilder.SetSchemaName() to assign a user-friendly identifier for the schema. The schema name is useful to identify a schema in an error message.
The read and write access levels of entities associated with the schema can be set independently. The options are Public, Vendor, or Application. If either the read or write access level is set to Vendor, the VendorId of the third-party vendor that may access entities of the schema must be specified. If either access level is set to Application, the GUID of the application or add-in that may access entities of the schema must be supplied.
Once the schema has been created, fields may be defined. A field is similar to a property of a class. It contains a name, documentation, value type and unit type. Fields can be a simple type, an array, or a map. The following simple data types are allowed:
Type |
Default Value |
int |
0 |
short |
0 |
byte |
0 |
double |
0.0 |
float |
0.0 |
bool |
false |
string |
Empty string ("") |
GUID |
Guid.Empty {00000000-0000-0000-0000-000000000000} |
ElementId |
ElementId.InvalidElementId |
Autodesk.Revit.DB.XYZ |
(0.0,0.0,0.0) |
Autodesk.Revit.DB.UV |
(0.0,0.0) |
Additionally, a field may be of type Autodesk.Revit.DB.ExtensibleStorage.Entity. In other words, an instance of another Schema, also known as a SubSchema or SubEntity. The default value for a field of this type is Entity with null schema, and guid of Guid.Empty.
A simple field can be created using the SchemaBuilder.AddSimpleField() method to specify a name and type for the field. AddSimpleField() returns a FieldBuilder, which is a helper class for defining Fields. If the type of the field was specified as Entity, use FieldBuilder.SetSubSchemaGUID() to specify the GUID of the schema of the Entities that are to be stored in this field.
Use the SchemaBuilder.AddArrayField() method to create a field containing an array of values in the Schema, with a given name and type of contained values. Array fields can have all the same types as simple fields.
Use the SchemaBuilder.AddMapField() method to create a field containing an ordered key-value map in the Schema, with given name, type of key and type of contained values. Supported types for values are the same as for simple fields. Supported types for keys are limited to int, short, byte, string, bool, ElementId and GUID.
Once the schema is finalized using SchemaBuilder, fields can no longer be edited using FieldBuilder. At that stage, the Schema class provides methods to get a Field by name, or a list of all Fields defined in the Schema.
After all fields have been defined for the schema, SchemaBuilder.Finish() will return the finished Schema. A new Entity can be created using that schema. For each Field in the Schema, the value can be stored using Entity.Set(), which takes a Field and a value (whose type is dependent on the field type). Once all applicable fields have been set for the entity, it can be assigned to an element using the Element.SetEntity() method.
To retrieve the data later, call Element.GetEntity() passing in the corresponding Schema. If no entity based on that schema was saved with the Element, an invalid Entity will be returned. To check that a valid Entity was returned, call the Entity.IsValid() method. Field values from the entity can be obtained using the Entity.Get() method.
To determine Entities stored with an element, use the Element.GetEntitySchemaGuids() method, which returns the Schema guids of any Entities for the Element. The Schema guids can be used with the static method Schema.Lookup() to retrieve the corresponding Schemas.
The following is an example of all these steps put together.
Code Region 22-9: Extensible Storage |
// Create a data structure, attach it to a wall, populate it with data, and retrieve the data back from the wall void StoreDataInWall(Wall wall, XYZ dataToStore) { Transaction createSchemaAndStoreData = new Transaction(wall.Document, "tCreateAndStore"); createSchemaAndStoreData.Start(); SchemaBuilder schemaBuilder = new SchemaBuilder(new Guid("720080CB-DA99-40DC-9415-E53F280AA1F0")); schemaBuilder.SetReadAccessLevel(AccessLevel.Public); // allow anyone to read the object schemaBuilder.SetWriteAccessLevel(AccessLevel.Vendor); // restrict writing to this vendor only schemaBuilder.SetVendorId("ADSK"); // required because of restricted write-access schemaBuilder.SetSchemaName("WireSpliceLocation"); // create a field to store an XYZ FieldBuilder fieldBuilder = schemaBuilder.AddSimpleField("WireSpliceLocation", typeof(XYZ)); fieldBuilder.SetUnitType(UnitType.UT_Length); fieldBuilder.SetDocumentation("A stored location value representing a wiring splice in a wall."); Schema schema = schemaBuilder.Finish(); // register the Schema object Entity entity = new Entity(schema); // create an entity (object) for this schema (class) // get the field from the schema Field fieldSpliceLocation = schema.GetField("WireSpliceLocation"); // set the value for this entity entity.Set<XYZ>(fieldSpliceLocation, dataToStore, DisplayUnitType.DUT_METERS); wall.SetEntity(entity); // store the entity in the element // get the data back from the wall Entity retrievedEntity = wall.GetEntity(schema); XYZ retrievedData = retrievedEntity.Get<XYZ>(schema.GetField("WireSpliceLocation"), DisplayUnitType.DUT_METERS); createSchemaAndStoreData.Commit(); } |
Creating a schema by adding fields, units, sub-entities, and description strings is not only a means for storing data. It is also implicit documentation for other users and a way for others to create entities of the same schema later with an easy adoption path.
Because schema entities are stored on a per-element basis, there is no need to necessarily read all extensible storage data in a document (e.g. all data from all beam family instances) when an application might only need data for the currently selected beam. This allows the potential for more specifically targeted data access code and better data access performance overall.