CEJson: https://ardjson.codeplex.com This link includes the before and after projects (full source) as a zip file.
The Sensor/Telemetry app as presented in Part 1 of this series could have been developed as new projects and developed from the ground up. With a functioning ToDo Tasks app communicating with Microsoft Azure Mobile Services, it was decided instead to “morph” the existing app into the new one.
A file compare app such as Beyond Compare (the one I use) is a useful tool for seeing the "wood for the trees". The tool was used to make a comparison of the before and and after solution projects. A screen capture of the files that were modified is:
The first change involved using an XML configuration file to specify the Mobile Service name and its app key, which [1]means they could be changed without the apps being recompiled. To do this the file Config.xml was added the Shared project and code was added to App.xaml.cs to read those values in using Linq to XML. The instantiation of the mobile service class then uses those values. Note that this initialization must be placed after InitializeComponent() in the App constructor.
<?xml version="1.0" encoding="utf-8" ?> <configuration> <service>telemetrysvc/service> <appKey>KKKKKKKKKKKKKKKKKKKK</appKey> </configuration>
Config.xml The service and appKey values in Config.xml for the specific Mobile Service (can be changed there).
class Config{ public string Service; public string AppKey;}
XDocument doc = XDocument.Load("Config.XML");var configs = (from xElem in doc.Descendants("configuration") select new Config { Service = xElem.Element("service").Value, AppKey = xElem.Element("appKey").Value }).ToList(); // Should only be one configurationif (configs.Count == 1){ service = service.Replace("telemetrymobileservice", configs[0].Service); appKey = configs[0].AppKey;} MobileService = new MobileServiceClient( service, appKey);
A number of the subsequent changes involve name changes such as for the solution Namepace. The original namespace is sportronicsdj and was changed to TelemetryMobileService. One might be tempted to just do a simple bulk textual substitution of the former by the latter but that would lead to a corruption in the source code because the original namespace text is used in other contexts in the solution in full and as part of symbolic names. The change in namespace was effected by Refactoring it to the new name throughout the solution. Refactoring was then used for a number of other symbolic names throughout the solution code.
This file was renamed to TelemetryItem.cs. The class name was changed to Telemetry using refactoring as was the renaming of the Text property to Sensor. The Value property was added. File comparison tools are useful for identifying the changes between projects at file level.
public class TodoItem { public string Id { get; set; }
public string Text { get; set; }
[JsonProperty(PropertyName = "complete")] public bool Complete { get; set; } }
public class Telemetry { public string Id { get; set; }
public string Sensor { get; set; }
[JsonProperty(PropertyName = "complete")] public bool Complete { get; set; }
[JsonProperty(PropertyName = "value")] public int Value { get; set; } }
Changes to the data class, which is passed between the app and Azure. The app uses the public Pascal cased property names. The service uses the lowercase Json propertynames.
The changes in the code with respect to (wrt) the data classes above were in the main automatically taken care of throughout the solution using refactoring. Some required changes were embedded within strings and needed manually editing. Also because there is an extra property in the class a UI element needed to be added to both the input and output part of the UI.
In the desktop and phone projects, the TexttInput was renamed SensorInput, again using refactoring. This was done in the MainPage.xaml file in both projects in the TexBlock control immediately before the Save button control.
private async void ButtonSave_Click(object sender, RoutedEventArgs e){ var todoItem = new TodoItem { Text = TextInput.Text }; await InsertTodoItem(todoItem);}
private async void ButtonSave_Click(object sender, RoutedEventArgs e){ var telemetryItem = new Telemetry { Sensor = SensorInput.Text, Value = int.Parse(ValueInput.Text) }; await InsertTelemetryItem(telemetryItem);}
<CheckBox Name="CheckBoxComplete" IsChecked="{Binding Complete, Mode=TwoWay}" Checked="CheckBoxComplete_Checked" Content="{Binding Text}" Margin="10,5" VerticalAlignment="Center"/>
<CheckBox Name="CheckBoxComplete" IsChecked="{Binding Complete, Mode=TwoWay}" Checked="CheckBoxComplete_Checked" Content="{Binding Senssor}" Margin="10,5" VerticalAlignment="Center"/>
<TextBlock x:Name="ValueTextBlock" Text="{Binding Value}" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal" Margin="72,0,0,0"> <TextBlock VerticalAlignment="Center">Sensor: </TextBlock> <TextBox x:Name="SensorInput" Margin="5" MinWidth="300" VerticalAlignment="Center"/> <TextBlock VerticalAlignment="Center">Value: </TextBlock> <TextBox x:Name="ValueInput" Margin="5" MinWidth="75" VerticalAlignment="Center" InputScope="Number"/> <Button x:Name="ButtonSave" Click="ButtonSave_Click" IsEnabled="False" Content="Save"/></StackPanel>
<ListView.ItemTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="50"/> <ColumnDefinition Width="125"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <CheckBox Grid.Column="0" Name="CheckBoxComplete" IsChecked="{Binding Complete, Mode=TwoWay}" Checked="CheckBoxComplete_Checked" Content="" Margin="10,5" VerticalAlignment="Center"/> <TextBlock Grid.Column="1" x:Name="SensorTextblock" Text="{Binding Sensor}" VerticalAlignment="Center" /> <TextBlock Grid.Column="2" x:Name="ValueTextBlock" Text="{Binding Value}" VerticalAlignment="Center" TextAlignment="Right" Width="100" /> </Grid> </DataTemplate></ListView.ItemTemplate>
These actions are rather cosmetic in that they aren't required from a functional perspective but make for good coding. They involve changing any remaining toDo references in symbol names to telemetry. They are largely local variable and parameter names within procedures in MainPaige.cs in the Shared project. Again reflection is used rather than bulk substitution so as to make these changes safely.
Telemetry telemetryItem = cb.DataContext as Telemetry;
Note that with some of the changes above there is more than one instance of the variable name to be changed. By using refactoring in one location all instances are changed together. Bulk substitution can be quite erroneous .. I've tried it!
When the sensor names are entered, it makes sense to always capitalise the first letter in the anme (Pascal case). This was implement in the Shared project when the data is submitted to the service in MainPage.cs:
//Clean up of Sensor names.private string Normalise(string strn){ string strnTemp =""; if (strn != "") { strnTemp = strn.Substring(1).ToLower(); strnTemp = strn.ToUpper()[0].ToString() + strnTemp; } return strnTemp;}
private async void ButtonSave_Click(object sender, RoutedEventArgs e) { //var telemetryItem = new Telemetry { Sensor = SensorInput.Text, Value = int.Parse(ValueInput.Text) }; var telemetryItem = new Telemetry { Sensor = Normalise(SensorInput.Text) , Value = int.Parse(ValueInput.Text)}; await InsertTelemetryItem(telemetryItem); }
Coming: A Video of the comparison of the two projects
Next: Command line Manipulation of the Mobile Service through Curl.
<Watch this space>
I tried to create a template for the second solution but the actions fails in what appears to be a known bug not yet resolved. Discussion points the Phone app being the source of the problem.
--------------------------- Export Template Wizard --------------------------- Template export failed for the following reason: Illegal characters in path. --------------------------- OK ---------------------------
Just a note: To change the table that you use with the same Mobile Service do a Refactor of the its class name in the class file (eg Telemetry.cs) of the Shared Project.. That's all that is required.