There are several examples in this documentation showing how to integrate Virtuoso's RDF data store into a Windows forms application or ADO.NET Data Service using the Virtuoso ADO.NET Data Provider. This example demonstrates how it is is possible to bypass ADO.NET completely and integrate RDF data from Virtuoso into a Silverlight application by directly querying the SPARQL endpoint on the Virtuoso Server using the .NET WebClient.
MainPage.xaml
.
Drag a TextBlock from the Toolbox onto MainPage.xaml
between the <Grid>
and </Grid>
tags.
Add the title text, "Example RDF Browser" and some formatting so that it looks like:
<TextBlock Text="Example RDF Browser" FontSize="20" ></TextBlock>
Below the title we want a box where we can type in the address of a SPARQL endpoint and a button to click to get a list of graphs available on that endpoint. So drag another TextBlock, a TextBox and a Button onto the grid. We will now add some row and column formatting to the grid as well.
<Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> <RowDefinition Height="80"/> <RowDefinition Height="40"/> <RowDefinition Height="40"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="60" /> <ColumnDefinition Width="60" /> <ColumnDefinition Width="350"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <TextBlock Text="Example RDF Browser" FontSize="20" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" HorizontalAlignment="Center" VerticalAlignment="Center"/> <TextBlock Text="Endpoint:" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Row="1" Grid.Column="1"></TextBlock> <TextBox Text="{Binding Mode=OneWay}" x:Name="txtEndpoint" HorizontalAlignment="Left" VerticalAlignment="Center" Width="290" Height="30" Grid.Row="1" Grid.Column="2"></TextBox> <Button Content="Get Graphs" Click="Button_Click" VerticalAlignment="Center" Grid.Row="1" Grid.Column="3"/> </Grid>
Note the binding of the TextBox to a variable called txtEndpoint. This makes text typed into the TextBox available to the code. Also note that the button click handler method, Button_Click. When the Get Graphs button is clicked we want to send a query to the endpoint and get back a list of graphs.
We will need to create the Button_Click method in the code behind file, MainPage.xaml.cs
.
MainPage.xaml.cs
and paste the following method beneath the MainPage method.
System.IO and System.Text will need to be added to the using block.
private void Button_Click (object sender, RoutedEventArgs e) { StringBuilder sb = new StringBuilder(); sb.Append("http://" + txtEndpoint.Text + "/sparql?&query=select+distinct+%3Fg+where+{graph+%3fg+{%3Fs+%3Chttp%3A//www.w3.org/1999/02/22-rdf-syntax-ns%23type%3E+%3Fo}}&format=text%2Fxml"); try { WebClient client = new WebClient(); client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted); client.DownloadStringAsync (new Uri(sb.ToString())); } catch (WebException webEx) { Console.WriteLine(webEx.ToString()); if (webEx.Status == WebExceptionStatus.ConnectFailure) { Console.WriteLine("Are you behind a firewall? If so, go through the proxy server."); } } }
SELECT DISTINCT ?g WHERE { GRAPH ?g { ?s <http://www.w3.org/1999/02/22-rdf-syntax-ns/type> ?o } }
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Error == null) { List<String> graphs = new List<string>(); using (XmlReader reader = XmlReader.Create(new StringReader(e.Result))) { reader.Read(); while (reader.ReadToFollowing("literal")) { graphs.Add(reader.ReadElementContentAsString()); } } }
MainPage.xaml
and define another row in the grid:
<Grid.RowDefinitions> <RowDefinition Height="80"/> <RowDefinition Height="40"/> <RowDefinition Height="40"/>
<TextBlock x:Name="graphLabel" Text="Graphs:" Visibility="Collapsed" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Column="1" Grid.Row="2" /> <ComboBox x:Name="graphList" Visibility="Collapsed" SelectedItem="{Binding Mode=OneWay}" HorizontalAlignment="Left" VerticalAlignment="Center" Width="290" Grid.Row="2" Grid.Column="2"></ComboBox> <Button x:Name="getTypeButton" Content="Get Types" Visibility="Collapsed" Click="Button_Click2" VerticalAlignment="Center" Grid.Row="2" Grid.Column="3"/>
MainPage.xaml.cs
and update the method so it looks like this.
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Error == null) { List<String> graphs = new List<string>(); using (XmlReader reader = XmlReader.Create(new StringReader(e.Result))) { reader.Read(); while (reader.ReadToFollowing("literal")) { graphs.Add(reader.ReadElementContentAsString()); } } graphList.ItemsSource = graphs; graphLabel.Visibility = Visibility.Visible; graphList.Visibility = Visibility.Visible; getTypeButton.Visibility = Visibility.Visible; } else { graphList.ItemsSource = null; } }
private void Button_Click2(object sender, RoutedEventArgs e) { }
The Get Types button does nothing so far. We need to add a body to the Button_Click2 method to issue another SPARQL query using the selected graph. This query will fetch a list of the types of object held in the graph. The query we want to send is
SELECT DISTINCT ?o FROM <i>graph name</i> WHERE {?s <http://www.w3.org/1999/02/22-rdf-syntax-ns/type> ?o}
where the from clause is the graph selected form the ComboBox.
1. Update the Button_Click2 method with the following code:
private void Button_Click2 (object sender, RoutedEventArgs e) { StringBuilder sb = new StringBuilder(); sb.Append("http://" + txtEndpoint.Text + "/sparql?default-graph-uri="+graphList.SelectedItem+"&query=select+distinct+%3Fo%0D%0Awhere+%0D%0A{%3Fs+%3Chttp%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23type%3E+%3Fo}&format=text%2Fxml"); try { WebClient client2 = new WebClient(); client2.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client2_DownloadStringCompleted); client2.DownloadStringAsync(new Uri(sb.ToString())); } catch (WebException webEx) { Console.WriteLine(webEx.ToString()); if (webEx.Status == WebExceptionStatus.ConnectFailure) { Console.WriteLine("Are you behind a firewall? If so, go through the proxy server."); } } }
void client2_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Error == null) { List<String> types = new List<string>(); using (XmlReader reader = XmlReader.Create(new StringReader(e.Result))) { reader.Read(); while (reader.ReadToFollowing("literal")) { types.Add(reader.ReadElementContentAsString()); } } typeList.ItemsSource = types; typeLabel.Visibility = Visibility.Visible; typeList.Visibility = Visibility.Visible; getItemButton.Visibility = Visibility.Visible; } else { typeList.ItemsSource = null; } }
MainPage.xaml
and a TextBlock, a ComboBox and a Button.
<Grid.RowDefinitions> <RowDefinition Height="80"/> <RowDefinition Height="40"/> <RowDefinition Height="40"/> <RowDefinition Height="40"/>
<TextBlock x:Name="typeLabel" Text="Types:" Visibility="Collapsed" HorizontalAlignment="Right" VerticalAlignment="Center" Grid.Column="1" Grid.Row="3" /> <ComboBox x:Name="typeList" Visibility="Collapsed" SelectedItem="{Binding Mode=OneWay}" HorizontalAlignment="Left" VerticalAlignment="Center" Width="290" Grid.Row="3" Grid.Column="2"></ComboBox> <Button x:Name="getItemButton" Content="Get Items" Visibility="Collapsed" Click="Button_Click3" VerticalAlignment="Center" Grid.Row="3" Grid.Column="3"/>
private void Button_Click3(object sender, RoutedEventArgs e) { }
When the Get Items button is clicked we want to query the endpoint for a list of all the entities in the selected graph whose type matches the type selected on the Types ComboBox. The SPARQL query we want to issue is
select ?s from <i>Graph URI</i> where {?s <> <i>Type URI</i>}
private void Button_Click3(object sender, RoutedEventArgs e) { StringBuilder sb = new StringBuilder(); sb.Append("http://" + txtEndpoint.Text + "/sparql?default-graph-uri=" + graphList.SelectedItem + "&query=select+distinct+%3Fs%0D%0Awhere+%0D%0A{%3Fs+%3Chttp%3A%2F%2Fwww.w3.org%2F1999%2F02%2F22-rdf-syntax-ns%23type%3E+%3C" + typeList.SelectedItem + "%3E}&format=text%2Fxml"); //The selected type URI may contain a # - we need %23 instead String queryString; if (sb.ToString().IndexOf('#') > 0) queryString = sb.ToString().Replace("#", "%23"); else queryString = sb.ToString(); try { WebClient client3 = new WebClient(); client3.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client3_DownloadStringCompleted); client3.DownloadStringAsync(new Uri(queryString)); } catch (WebException webEx) { Console.WriteLine(webEx.ToString()); if (webEx.Status == WebExceptionStatus.ConnectFailure) { Console.WriteLine("Are you behind a firewall? If so, go through the proxy server."); } } }
void client3_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { if (e.Error == null) { List<String> items = new List<string>(); using (XmlReader reader = XmlReader.Create(new StringReader(e.Result))) { reader.Read(); while (reader.ReadToFollowing("literal")) { items.Add(reader.ReadElementContentAsString()); } } uriGrid.ItemsSource = items; uriGrid.Visibility = Visibility.Visible; } else { uriGrid.ItemsSource = null; } }
MainPage.xaml
and add another row to the grid
<Grid.RowDefinitions> <RowDefinition Height="80"/> <RowDefinition Height="40"/> <RowDefinition Height="40"/> <RowDefinition Height="40"/> <RowDefinition Height="220"/>
MainPage.xaml
after the last button.
Then replace <data:DataGrid>
</data:DataGrid>
tags with the following:
<data:DataGrid x:Name="uriGrid" Visibility="Collapsed" AutoGenerateColumns="False" Grid.Row="4" Grid.Column="1" Grid.ColumnSpan="3" > <data:DataGrid.Columns> <data:DataGridTemplateColumn Header="Items"> <data:DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Mode=OneWay}"></TextBlock> </DataTemplate> </data:DataGridTemplateColumn.CellTemplate> </data:DataGridTemplateColumn> </data:DataGrid.Columns> </data:DataGrid>
<TextBlock Text="{Binding Mode=OneWay}"></TextBlock>
MainPage.xaml
and replace it with:
<HyperlinkButton Content="{Binding}" NavigateUri="{Binding}" TargetName="_blank"></HyperlinkButton>
<ScrollViewer x:Name="LayoutRoot" Background="AliceBlue"> <Grid> . . . </Grid> </ScrollViewer>
Testing the application so far has been done using the Visual Studio Development Server. It is possible to host the application using Virtuoso as the web server on non Windows platforms.
vsp
folder of the Virtuoso instance you want to use as the web server; typically, /usr/local/virtuoso-opensource/var/lib/virtuoso/vsp
.
/usr/local/virtuoso-opensource/var/lib/virtuoso/vsp
; so in my example, RDFBrowser
and the Default Page to RDFBrowserTestPage.html
.
http://localhost:8890/RDFBrowser
.
This Silverlight application will only allow you to browse data on SPARQL endpoints that are accessible on your domain.
This is due to the Silverlight security model which is described in the MSDN.
As the Silverlight application is itself making HTTP web requests, these are restricted to only going to the domain that originally served the application.
To allow cross-domain access to the SPARQL service from Silverlight, the SPARQL endpoint would require either a clientaccesspolicy.xml
or crossdomain.xml
file allowing access from the site serving the Silverlight application.