Adding an In-Memory Data Source
The Reveal SDK allows you to create dashboards using data that has been generated at application run-time. This data is usually backed by a business object (POCO class) that is used within the application. This type of data is referred to as in-memory data.
There are three primary steps to add an application's in-memory data as a data source item in the Reveal SDK.
Step 1 - Create a class that implements IRVDataProvider
. This class will provide the actual in-memory data to be returned to the Reveal SDK. You'll want to check the RVInMemoryDataSourceItem.DatasetId
property to know what data to return.
class MyInMemoryDataProvider : IRVDataProvider
{
public Task<IRVInMemoryData> GetData(RVInMemoryDataSourceItem dataSourceItem)
{
if (dataSourceItem.DatasetId == "MyDataSetId")
{
return Task.FromResult<IRVInMemoryData>(new RVInMemoryData(data));
}
else
{
throw new Exception("Invalid datasetId");
}
}
}
The GetData
method returns a Task<IRVInMemoryData>
. This means that any in-memory data you want to use must be wrapped by the RVInMemoryData
object. Simply create a new instance of the RVInMemoryData
object and pass your in-memory data as a parameter to the object constructor.
Step 2 - Set the RevealSdkSettings.DataProvider
to an instance of the class that implements IRVDataProvider
RevealSdkSettings.DataProvider = new MyInMemoryDataProvider();
Step 3 - Create an RVInMemoryDataSourceItem
in the RevealView.DataSourcesRequested
event.
Add an event handler to the RevealView.DataSourcesRequested
<rv:RevealView x:Name="_revealView" DataSourcesRequested="RevealView_DataSourcesRequested" />
In the event handler, create a new instance of the RVInMemoryDataSourceItem
object and provide a unique name/ID as a parameter. This ID is used in the IRVDataProvider
to indicate which data source is requesting the data.
private void RevealView_DataSourcesRequested(object sender, DataSourcesRequestedEventArgs e)
{
List<RVDashboardDataSource> datasources = new List<RVDashboardDataSource>();
List<RVDataSourceItem> datasourceItems = new List<RVDataSourceItem>();
var inMemoryDataSourceItem = new RVInMemoryDataSourceItem("MyDataSetId")
{
Title = "My Data"
};
datasourceItems.Add(inMemoryDataSourceItem);
e.Callback(new RevealDataSources(datasources, datasourceItems, true));
}
Example: Implement In-Memory Data Source
Create the Business Objects
For this example, we need to create 3 business objects; A Product
, Seller
, and a Sale
object. These objects will be used to hold the data that will be represented in our dashboards.
public class Product
{
public string Name { get; set; }
public double UnitPrice { get; set; }
}
public class Seller
{
public string Name { get; set; }
public string City { get; set; }
}
public class Sale
{
internal Product Product { get; set; } = new Product();
internal Seller Seller { get; set; } = new Seller();
public string SalesPerson
{
get { return Seller.Name; }
set { Seller.Name = value; }
}
public DateTime Date { get; set; }
public string City
{
get { return Seller.City; }
set { Seller.City = value; }
}
public string ProductName
{
get { return Product.Name; }
set { Product.Name = value; }
}
internal double Value { get; set; }
internal string Quarter { get; set; }
public int NumberOfUnits { get; set; }
public double UnitPrice
{
get { return Product.UnitPrice; }
set { Product.UnitPrice = value; }
}
public int AmountOfSale
{
get { return (int)UnitPrice * NumberOfUnits; }
set { Product.UnitPrice = value / NumberOfUnits; }
}
}
Generate the In-Memory Data
Next, we need to generate some data that will be used to build our Reveal Dashboards. For this, we will create a helper class called SalesDataGenerator
that will generate some random data for use in our dashboards.
public class SalesDataGenerator
{
private static string[] _products = new string[4] { "Apple", "Grape", "Orange", "Banana" };
private static string[] _sellerNames = new string[8] { "Ellen Adams", "Lisa Andrews", "William Fox", "Walter Harp", "Jessica Oxley", "Misty Shock", "Chris Meyer", "Jay Calvin" };
private static string[] _cities = new string[6] { "Tokyo", "Shanghai", "Beijing", "Singapore", "New York", "Seoul" };
private static readonly Random Random = new Random();
public static List<Sale> GenerateSales(int numberOfSales)
{
List<Sale> sales = new List<Sale>();
for (double i = 0; i < numberOfSales; i++)
{
Sale sale = new Sale
{
Quarter = "Q " + i,
Value = GetRandomPrice(),
Date = GetRandomDate(),
Product = GerRandomProduct(),
NumberOfUnits = GetRandomNumUnits(),
Seller = GetRandomSeller()
};
sales.Add(sale);
}
return sales;
}
private static Seller GetRandomSeller()
{
return new Seller
{
City = GetRandomCity(),
Name = GetRandomSellerName()
};
}
private static string GetRandomSellerName()
{
Random a = new Random(Random.Next());
int length = _sellerNames.Length;
int RandomMaxLength = a.Next(length) % 2 == 0 ? a.Next(length) : length;
return _sellerNames[a.Next(RandomMaxLength)];
}
private static string GetRandomCity()
{
Random a = new Random(Random.Next());
int length = _cities.Length;
int RandomMaxLength = a.Next(length) % 2 == 0 ? a.Next(length) : length;
return _cities[a.Next(RandomMaxLength)];
}
private static int GetRandomNumUnits()
{
Random a = new Random(Random.Next());
return a.Next(1, 100);
}
private static Product GerRandomProduct()
{
return new Product
{
Name = GetRandomProductName(),
UnitPrice = GetRandomPrice()
};
}
private static double GetRandomPrice()
{
Random a = new Random(Random.Next());
return a.NextDouble() * 1000;
}
private static string GetRandomProductName()
{
Random a = new Random(Random.Next());
int length = _products.Length;
int RandomMaxLength = a.Next(length) % 2 == 0 ? a.Next(length) : length;
return _products[a.Next(RandomMaxLength)];
}
private static DateTime GetRandomDate()
{
Random a = new Random(Random.Next());
int day = a.Next(1, 28);
int month = a.Next(1, 13);
int year = a.Next(2016, 2020);
return new DateTime(year, month, day);
}
}
In the constructor of our MainWindow.cs
file in our application, we are going to create 10000
records of sales data.
public MainWindow()
{
InitializeComponent();
var salesData = SalesDataGenerator.GenerateSales(10000);
}
Create the Data Provider
Now that we have created the data that will be used in our dashboards, the next step is to make that data available to the Reveal SDK. To do this, we need to create a new class that implements the IRVDataProvider
. This interface is used specifically for in-memory data implementations within the Reveal SDK.
Let's create a new class called InMemoryDataProvider
and implement the IRVDataProvider
interface. Notice that we also defined a constructor that accepts an IEnumerable<Sale>
. This allows us to pass in our generated sales data from our previous step.
class InMemoryDataProvider : IRVDataProvider
{
RVInMemoryData<Sale> _salesInMemoryData;
public InMemoryDataProvider(IEnumerable<Sale> sales)
{
_salesInMemoryData = new RVInMemoryData<Sale>(sales);
}
public Task<IRVInMemoryData> GetData(RVInMemoryDataSourceItem dataSourceItem)
{
if (dataSourceItem.DatasetId == "SalesRecords")
{
return Task.FromResult<IRVInMemoryData>(_salesInMemoryData);
}
else
{
throw new Exception("Invalid datasetId");
}
}
}
As you can see, in the GetData
method, we are checking the DatasetId
for a specific value. If this id matches our SalesRecords
data source item, then we will then use the in-memory business object collection that we passed in class constructor as the data source for the dashboard.
Now that we have our data and our data provider, we need to set the RevealSdkSettings.DataProvider
property to an instance of our InMemoryDataProvider
class.
public MainWindow()
{
InitializeComponent();
var salesData = SalesDataGenerator.GenerateSales(10000);
RevealSdkSettings.DataProvider = new InMemoryDataProvider(salesData);
}
Now you may be asking, "Where does the DataSetId
value come from?". This happens in the next step when we create the data source item.
Handle the DataSourcesRequested Event
The next step is to add an event handler to the RevealView.DataSourcesRequested
event.
<rv:RevealView x:Name="_revealView" DataSourcesRequested="RevealView_DataSourcesRequested" />
private void RevealView_DataSourcesRequested(object sender, DataSourcesRequestedEventArgs e)
{
List<RVDashboardDataSource> datasources = new List<RVDashboardDataSource>();
List<RVDataSourceItem> datasourceItems = new List<RVDataSourceItem>();
var inMemoryDataSourceItem = new RVInMemoryDataSourceItem("SalesRecords")
{
Title = "Sales Records"
};
datasourceItems.Add(inMemoryDataSourceItem);
e.Callback(new RevealDataSources(datasources, datasourceItems, true));
}
The source code to this sample can be found on GitHub.