The Optimizely Dynamic Data Store (DDS) is an Object-Relational mapper. When used with compile time data types (.NET classes), all properties that have a public **getter** and a **setter** (setter does not need to be public) are mapped to a column in a database table. For runtime data types, each property added to a `PropertyBag
` is also mapped in the same way.
DDS is a component offering an API and infrastructure for the saving, loading and searching of both compile time data types (.NET object instances) and runtime data types (property bags) in shared tables in SQL Server. The component is shipped as part of the Framework package.
DDS has the following advantages:
Easy to use for simple data structures
Supports LINQ for querying data
Does not require custom tables, easy to upgrade/install
Supports multiple database tables to isolate stores
Supports both typed model and property bags
Built-in cache
You should not use DDS for high performance and scalability requirements, or when storing very large object graphs (there is no lazy loading support for example). The dynamic nature of DDS might become a bottleneck rather than working in your advantage in these situations and we recommend storing these kind of data in custom tables where the table design and API can be optimized for a specific use case. Alternative technologies for working with database tables in SQL Server include Microsoft’s Entity Framework and NHibernate for .NET.
## Map DDS data types
In DDS, data types are stored in one database table. This table contains many columns, several of each data type that the Dynamic Data Store supports. When a data structure is saved, the .NET CLR type of each property is mapped against an internal list of supported types.
The following types of mapping supported:
### Map inline
Inline mapping is where a property of a class or `PropertyBag
` can be mapped directly against one of the supported database columns. The following types can be mapped inline:
System.Byte (and arrays of)
System.Int16
System.Int32
System.Int64
System.Enum
System.Single
System.Double
System.DateTime
System.String
System.Char (and arrays of)
System.Boolean
System.Guid
EPiServer.Data.Identity
### Map collection
A property is mapped as a collection if it implements the `System.IEnumerable
` interface. In this case, all elements of the collection (both keys and values in the case of `System.IDictionary
`) are stored in a special reference table.
Even though the `EPiServer.Data.Dynamic.PropertyBag
` implements `System.IEnumerable
`, it will actually be treated as a reference type (see below).
### Map reference
All properties that cannot be mapped inline or as a collection (plus the `EPiServer.Data.Dynamic.PropertyBag
` type) are mapped as references. This means that their properties are mapped in turn as a sub-type, and a link row is added in the reference table to link the parent data structure with the child data structure. This allows for complex trees of data structures (object graphs) to be saved in DDS.
### Default table
The default DDS table is called `tblBigTable
`, which contains the following fixed columns (meaning mandatory columns):
**pkId** is the store ID and primary key of each data structure stored.
**Row** is the row index. Each structure may span 1 or more rows in the big table.
**StoreName** is the name of the store the data structure belongs to.
**ItemType** is the .NET CLR Type that contained the properties saved to the current row.
The default big table also contains the following optional columns:
**BooleanXX** (where XX is 01 through to 05) x 5
**IntegerXX** (where XX is 01 through to 10) x 10
**LongXX** (where XX is 01 through to 05) x 5
**DateTimeXX** (where XX is 01 through to 05) x 5
**GuidXX** (where XX is 01 through to 03) x 3
**FloatXX** (where XX is 01 through to 07) x 7
**StringXX** (where XX is 01 through to 10) x 10
**BinaryXX** (where XX is 01 through to 05) x 5
**Indexed\_Boolean01**
**Indexed\_IntegerXX** (where XX is 01 through to 03) x 3
**Indexed\_LongXX** (where XX is 01 through to 02) x 2
**Indexed\_DateTime01**
**Indexed\_Guid01**
**Indexed\_FloatXX** (where XX is 01 through to 03) x 3
**Indexed\_StringXX** (where XX is 01 through to 03) x 3
**Indexed\_Binary01** (not Oracle)
The columns whose name starts with **Indexed** have database indexes created on them.
You may want to **add and remove columns in this table** to suit the type of data you are saving. This is particularly useful if you are going to store a data type with more than, for example, 10 strings. By default, the 11th to 20th strings would be stored in a 2nd row for the type, which means a join has to be done at runtime when reading the data. By adding String11, String12 and so on to the big table, you limit the chance of a row overspill and therefore increase performance. If you require more indexes, then add columns with names starting with Indexed and make sure an index is created on them.
### Add a custom table
You can also add your own table. This is particularly useful if you are going to store a type that only contains strings for example. Along with the mandatory columns (**pkId**, **Row**, **StoreName**, **ItemType**), you can add about 20 StringXX columns.
Add the `EPiServerDataTableAttribute
` with the `TableName
` property given as the name of the custom table, to use the custom big table.
### SQL Server mappings
This table lists the database columns types in the default big table and the .NET CLR “inline” types they are mapped to:
Database Column Type | .NET CLR “Inline” Types |
varbinary(max) <br>varbinary(900) | System.Byte\[] |
int | System.Byte, System.Int16, System.Int32, System.Enum |
bigint | System.Int64 |
float | System.Single, System.Double |
datetime | System.DateTime |
uniqueidentifier | System.Guid |
nvarchar(max) <br>nvarchar(450) | System.String, System.Char, System.Char\[], EpiServer.Data.Identity |
bit | System.Boolean |
### Store database views
Each store is actually represented in the database by a view. The views can be used as normal including cross joining with other tables and views in the database.
## Assembly and namespaces
The `EPiServer.Data
` assembly contains the following main namespaces:
`
EPiServer.Data
` namespace contains important classes used in the Dynamic Data Store, most notably the `Identity
` class.`
EPiServer.Data
` configuration contains the configuration classes for the Dynamic Data Store.`
EPiServer.Data.Dynamic
` namespace contains the `DynamicDataStoreFactory
` and `DynamicDataStore
` classes and their support classes and data structures.
### Manage stores
Use the `DynamicDataStoreFactory
` class to create, obtain and delete stores. The class has a single instance which is obtained from the static Instance property. Alternatively, stores can be automatically created for .NET classes by decorating them with the `EPiServerDataStoreAttribute
` and setting the `AutomaticallyCreateStore
` property to true.
See the `UsingStores
` class in the [DDS sample project](🔗) for examples on creating, obtaining and deleting stores.
### Save and load data
Data can be saved and loaded using compile time data types (.NET classes) and runtime data types via the `EPiServer.Data.Dynamic.PropertyBag
` class. The Dynamic Data Store is divided into logical stores which are identified by name. Stores are not polymorphic which means only one property set may be saved in a store although it is possible to re-map stores and achieve a level of polymorphism though the use of interfaces and template types.
See the `LoadSaveType
` and `LoadSavePropertyBag
` classes in the the [DDS sample project](🔗) for examples of loading and saving data.
### Search data
You can search data in the Dynamic Data Store in the following ways:
**Simple Find method** – Find data structures by matching one or more name-value pairs with data in the store.
**LINQ** – Find data structures using Microsoft’s Language Integrated Query technology.
See the `UsingLinq
` and `UsingFind
` classes in the [DDS sample project](🔗) for examples of searching for data.