Persistence
The implementation demonstrated in this article can also be reviewed in the Persist QueryableDataProvider SDK Example of the SDK Examples Browser
This article will go through the process of persisting the current state of QueryableDataProvider via RadPersistenceFramework. Through it the state of the control can be persisted and loaded the next time the application is started.
- Using the DataContract Attribute
- Defining the IValueProvider
- Specifying the KnownTypes
- Registering the PersistenceProvider
Using the DataContract Attribute
The DataContract attribute is added to all classes used by the QueryableDataProvider. This enables easy serialization with the DataContractSerializer. Below is a sample definition of such classes. The DataContract attribute is added on class level and the DataMember one is added for each property.
Example 1: Using the DataContract attribute
[DataContract]
public class DataProviderSettings
{
[DataMember]
public object[] Aggregates { get; set; }
[DataMember]
public object[] Filters { get; set; }
[DataMember]
public object[] Rows { get; set; }
[DataMember]
public object[] Columns { get; set; }
[DataMember]
public int AggregatesLevel { get; set; }
[DataMember]
public PivotAxis AggregatesPosition { get; set; }
}
Defining the IValueProvider
The next step is to create a new class, which implements the Telerik.Windows.Persistence.Services.IValueProvider interface. It provides two methods that need to be implemented - ProvideValue and RestoreValue. The first one is used when the data is saved. The second one is used when the data is restored from a previously saved state. When saving the provider, an instance of the DataProviderSettings class has to be created with all of its properties set. Then, the instance can be saved to a file or a stream. Below is a sample implementation of the interface.
Example 2: Implementing the IValueProvider interface
public abstract class DataProviderValueProvider : IValueProvider
{
public abstract IEnumerable<Type> KnownTypes { get; }
public string ProvideValue(object context)
{
string serialized = string.Empty;
IDataProvider dataProvider = context as IDataProvider;
if (dataProvider != null)
{
MemoryStream stream = new MemoryStream();
DataProviderSettings settings = new DataProviderSettings()
{
Aggregates = dataProvider.Settings.AggregateDescriptions.OfType<object>().ToArray(),
Filters = dataProvider.Settings.FilterDescriptions.OfType<object>().ToArray(),
Rows = dataProvider.Settings.RowGroupDescriptions.OfType<object>().ToArray(),
Columns = dataProvider.Settings.ColumnGroupDescriptions.OfType<object>().ToArray(),
AggregatesLevel = dataProvider.Settings.AggregatesLevel,
AggregatesPosition = dataProvider.Settings.AggregatesPosition
};
DataContractSerializer serializer = new DataContractSerializer(typeof(DataProviderSettings), KnownTypes);
serializer.WriteObject(stream, settings);
stream.Position = 0;
var streamReader = new StreamReader(stream);
serialized += streamReader.ReadToEnd();
}
return serialized;
}
public void RestoreValue(object context, string savedValue)
{
IDataProvider dataProvider = context as IDataProvider;
if (dataProvider != null)
{
var stream = new MemoryStream();
var tw = new StreamWriter(stream);
tw.Write(savedValue);
tw.Flush();
stream.Position = 0;
DataContractSerializer serializer = new DataContractSerializer(typeof(DataProviderSettings), KnownTypes);
var result = serializer.ReadObject(stream);
dataProvider.Settings.AggregateDescriptions.Clear();
foreach (var aggregateDescription in (result as DataProviderSettings).Aggregates)
{
dataProvider.Settings.AggregateDescriptions.Add(aggregateDescription);
}
dataProvider.Settings.FilterDescriptions.Clear();
foreach (var filterDescription in (result as DataProviderSettings).Filters)
{
dataProvider.Settings.FilterDescriptions.Add(filterDescription);
}
dataProvider.Settings.RowGroupDescriptions.Clear();
foreach (var rowDescription in (result as DataProviderSettings).Rows)
{
dataProvider.Settings.RowGroupDescriptions.Add(rowDescription);
}
dataProvider.Settings.ColumnGroupDescriptions.Clear();
foreach (var columnDescription in (result as DataProviderSettings).Columns)
{
dataProvider.Settings.ColumnGroupDescriptions.Add(columnDescription);
}
dataProvider.Settings.AggregatesPosition = (result as DataProviderSettings).AggregatesPosition;
dataProvider.Settings.AggregatesLevel = (result as DataProviderSettings).AggregatesLevel;
}
}
}
Specifying the KnownTypes
In the previous example a collection of KnownTypes is passed to the DataContractSerializer. It consists of all types needed for serializing the QueryableDataProvider. For this purpose we created a new QueryablePivotSerializationHelper class which has a static member - KnownTypes.
Example 3: Specifying the KnownTypes
public class QueryableDataSourceValueProvider: DataProviderValueProvider
{
public override IEnumerable<Type> KnownTypes
{
get
{
return QueryablePivotSerializationHelper.KnownTypes;
}
}
}
Registering the PersistenceProvider
The final step is to register a persistence provider and implement the logic needed for saving and loading the state of the QueryableDataProvider.
Example 4: Registering the PersistenceProvider
Stream stream = new MemoryStream();
ServiceProvider.RegisterPersistenceProvider<IValueProvider>(typeof(QueryableDataProvider), new QueryableDataSourceValueProvider());
//saving
PersistenceManager manager = new PersistenceManager();
this.stream = manager.Save(this.PivotGrid.DataProvider);
//loading
this.stream.Position = 0;
PersistenceManager manager = new PersistenceManager();
manager.Load(this.PivotGrid.DataProvider, this.stream);