HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideLegal TermsGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Dynamic Data Store

Describes the Optimizely Dynamic Data Store (DDS), which saves, loads and searches compile-time data types (.NET object instances) and run-time data types (property bags) in shared tables in SQL Server.

The Optimizely Dynamic Data Store (DDS) is an Object-Relational mapper. When used with compile time data types (.NET classes), properties with 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 that offers an API and infrastructure for saving, loading, and searching compile-time data types (.NET object instances) and runtime data types (property bags) in shared tables in SQL Server. It 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
  • It does not require custom tables, is easy to upgrade or install
  • Supports multiple database tables to isolate stores
  • Supports typed model and property bags
  • Built-in cache

You should not use DDS for high performance and scalability requirements or when storing large object graphs (there is no lazy loading support, for example). The dynamic nature of DDS might become a bottleneck rather than working to your advantage in these situations, and you should store this kind of data in custom tables where you can optimize the table design and API 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

DDS stores data types in one database table. This table contains many columns, several of each data type that the Dynamic Data Store supports. When DDS saves a data structure, it maps the .NET CLR type of each property against an internal list of supported types.

The following types of mapping are 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. You can map the following types 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, elements of the collection (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 is treated as a reference type (see below).

Map reference

Properties that you cannot map inline or as a collection (plus the EPiServer.Data.Dynamic.PropertyBag type) are mapped as references, so you map their properties 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 saves complex trees of data structures (object graphs) in DDS.

Default table

The tblBigTable is the default DDS table, 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 store name to which the data structure belongs.
  • ItemType is the .NET CLR Type that contains 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 names start with Indexed have database indexes created for them.

You may want to add and remove columns in this table to suit the type of data you are saving, especially if you want to store a data type with more than ten strings. By default, DDS stores the 11th to 20th strings in a 2nd row for the type, which means it joins them at runtime when reading the data. Adding String11, String12, and so on to the big table limits the chance of a row overspill and increases performance. If you require more indexes, add columns with names starting with Indexed and make sure it creates an index on them.

Add a custom table

You can also add a table if you want 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, giving the custom table name in the TableName property to use the custom big table.

SQL Server mappings

The following table lists the database columns types in the default big table and the .NET CLR “inline” types to which they are mapped:

Database Column Type.NET CLR “Inline” Types
varbinary(max)
varbinary(900)
System.Byte[]
intSystem.Byte, System.Int16, System.Int32, System.Enum
bigintSystem.Int64
floatSystem.Single, System.Double
datetimeSystem.DateTime
uniqueidentifierSystem.Guid
nvarchar(max)
nvarchar(450)
System.String, System.Char, System.Char[], EpiServer.Data.Identity
bitSystem.Boolean

Store database views

Each store is represented in the database by a view you can use, including cross-joining with other tables and views.

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 obtained from the static Instance property. Alternatively, you can automatically create stores 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 of creating, obtaining, and deleting stores.

Save and load data

You can save and load data using compile-time data types (.NET classes) and runtime data types through the EPiServer.Data.Dynamic.PropertyBag class. The Dynamic Data Store has logical stores identified by name. Stores are not polymorphic, meaning only one property set may be saved in a store, although you can re-map stores and achieve a level of polymorphism through interfaces and template types.

See the LoadSaveType and LoadSavePropertyBag classes in 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 – Use Microsoft’s Language Integrated Query technology to find data structures.

See the UsingLinq and UsingFind classes in the DDS sample project for examples of searching for data.