RIA Not Loading Child Entities With RIA & Silverlight - silverlight

I am having difficulty loading some child attributes of a given entity. I have managed to load child entities on other objects, but not this one. To add to the frustration, the child entities I am trying to load are referenced from another Entity, and from this they work fine...
My code is as follows.
ViewWasteApplication Page
Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)
If NavigationContext.QueryString.ContainsKey("ID") Then
' load an existing record - edit mode
Context.Load(Context.GetWasteApplicationsByIDQuery(Int32.Parse(NavigationContext.QueryString("ID").ToString())),
AddressOf wasteApplicationLoaded, Nothing)
Else
MessageBox.Show("Application not found")
End If
End Sub
This calls GetWasteApplicationsByID - which is as follows:
Public Function GetWasteApplicationsByID(ByVal query As Int32) As IQueryable(Of WasteApplication)
Dim result = Me.ObjectContext.WasteApplications.Include("CurrentlyWith") _
.Include("Packaging") _
.Include("WasteStream") _
.Where(Function(f) f.ID = query)
Return result
End Function
The WasteApplication is being returned, but neither of the 3 child entities are appearing.
I have created a MetaData class for this WasteApplication, as follows:
<MetadataTypeAttribute(GetType(WasteApplications.WasteApplicationsMetaData))> _
Partial Public Class WasteApplications
Friend NotInheritable Class WasteApplicationsMetaData
'Metadata classes are not meant to be instantiated.
Private Sub New()
MyBase.New()
End Sub
Public Property ID As Integer
Public Property RequestedByName As String
Public Property RequestedByExtension As String
Public Property CARQRequired As Boolean
Public Property OriginOfMaterialID As Integer
Public Property Comments As String
Public Property MaterialName As String
Public Property PackagingID As Integer
Public Property FacilityPath As String
Public Property ProcessStatus As String
Public Property DateSubmitted As DateTime
Public Property RequestedByUsername As String
Public Property CurrentlyWithID As Integer
Public Property WasteStreamID As Integer
<Include()>
Public Property WasteStream As WasteStreams
<Include()>
Public Property Packaging As Packaging
End Class
End Class
Can anyone see anything wrong with this? I load the same two child objects on another page, and these seem to load just fine. The code for this is as follows:
View Chemical Application (This works)
Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)
Context.Load(Context.GetChemicalApplicationsByIDQuery(Int32.Parse(NavigationContext.QueryString("ID"))),
AddressOf wasteApplicationLoaded, Nothing)
End Sub
With the RIA function as follows:
Public Function GetChemicalApplicationsByID(ByVal query As Int32) As IQueryable(Of ChemicalApplication)
Return Me.ObjectContext.ChemicalApplications.Include("Chemical") _
.Include("ProcessStatus") _
.Include("PlanningApprover") _
.Include("FacilitiesApprover") _
.Include("MaintenanceApprover") _
.Include("PPCPermit") _
.Include("DischargeConsent") _
.Include("Facility") _
.Include("Packaging") _
.Include("WasteStream") _
.Where(Function(f) f.ID = query)
End Function
Any suggestions?
NOTE: I have not posted any of the XAML bindings, as I have confirmed via debugging that the source entities do not contain the child data, so therefore binding will not be an issue.
I am using Silverlight 4 with Entity Framework 4.

You will need to create metadata classes for the entities you wish to include and mark the fields with the [Include] attribute.
[MetadataTypeAttribute(typeof(Client.ClientMetadata))]
public partial class Client
{
internal sealed class ClientMetadata
{
private ClientMetadata()
{
}
[Required(ErrorMessage="You must enter a client name")]
public string Name;
[Include]
public EntityCollection<Contact> Contacts;
[Include]
public Employee Employee;
[Include]
public BillTo BillTo;
}
}
See RIA Services and relational data for more.

You need to do the 2 following things in order for this to work:
a) Add the include parameter in a metadata class (as DaveB proposed)
b) on the domain service queries (server side) add the include directive - Me.ObjectContext.WasteApplications.Include("CurrentlyWith")

I managed to solve the issue, you need to load the respective entities into the datacontext as well as loading the linked entity. i.e in my case I need to add:
Context.Load(Context.GetWasteStreamsQuery())
Context.Load(Context.GetPackagingQuery())
so that the linked child entities exist. My full onNavigate therefore looks like this:
Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)
If NavigationContext.QueryString.ContainsKey("ID") Then
Context.Load(Context.GetWasteApplicationsByIDQuery(Int32.Parse(NavigationContext.QueryString("ID").ToString())),
AddressOf wasteApplicationLoaded, Nothing)
Context.Load(Context.GetWasteStreamsQuery())
Context.Load(Context.GetPackagingQuery())
Else
MessageBox.Show("Application not found")
End If
End Sub

Related

Winforms Data from database across multiple forms

I have a winforms applications that has an ms sql server backend. In my database i have lookup tables for like status, and other tables where the data rarely changes. In my application, several forms might use the same lookup tables (Some have a lot of data in them). Instead of loading/filling the data each time the form is open, is there a way to cache the data from the database that can be accessed from multiple forms. I did some searching, but couldnt find the best solution. There is caching, dictionaries, etc. What is the best solution and can you point me to the documentation that discusses it and may even have an example.
Edit:
In my original post I failed to mention that I have a strongly typed dataset and use tableadapters. I want to preload my lookup tables when my application starts, and then have these dataset tables be used throughout the application, on multiple forms without having to fill them on every form.
I have tried creating a class:
Public Class dsglobal
Public Shared EML_StaffingDataSet As EML_StaffingDataSet
Public Shared Sub populateDS()
EML_StaffingDataSet = New EML_StaffingDataSet
End Sub
Public Shared Sub loadskills()
Dim ta As New EML_StaffingDataSetTableAdapters.TSTAFFSKILLTableAdapter
ta.Fill(EML_StaffingDataSet.TSTAFFSKILL)
End Sub
End Class
I run this on a background worker when my application is starting up. So it loads the dataset table. On fill, I can see the datatable has data in it. When I open a form, i want to use the dataset table, but it seems to clear the data out. Not sure if my approach is correct or where my error is.
Edit2:
I have also tried this per comments, but not sure I am doing it correctly. If I am doing it correctly, then how do I use that as a datasource at design time, can i only do that programmatically?
Public Module lookupdata
Private EML_StaffingDataSet As EML_StaffingDataSet
Private skillvalues As List(Of skill)
Public ReadOnly Property skill As List(Of skill)
Get
If skillvalues Is Nothing Then
getskillvalues()
End If
Return skillvalues
End Get
End Property
Private Sub getskillvalues()
skillvalues = New List(Of skill)
EML_StaffingDataSet = New EML_StaffingDataSet
Dim ta As New EML_StaffingDataSetTableAdapters.TSTAFFSKILLTableAdapter
ta.Fill(EML_StaffingDataSet.TSTAFFSKILL)
For Each row As DataRow In EML_StaffingDataSet.TSTAFFSKILL
Dim skill As New skill
skill.skill_id = row("skill_id")
skill.skill_desc = row("skill_desc")
skill.skill_open_ind = row("skill_open_ind")
skillvalues.Add(skill)
Next
End Sub
End Module
Public Class skill
Public Property skill_id As Integer
Public Property skill_desc As String
Public Property skill_open_ind As Boolean
End Class
You might want to consider a lazy loading pattern, like this:
Public Module LookupData
Private statusValues As List(Of LookupValue)
Public Readonly Property Statuses As List(Of LookupValue)
Get
If statusValues Is Nothing Then
GetStatusValues()
End If
Return statusValues
End Get
End Property
Private Sub GetStatusValues()
statusValues = New List(Of LookupValue)
Dim sql = "select key, value from StatusTable"
'TODO: Read the items from the database here, adding them to the list.
End Sub
End Module
Public Class LookupValue
Public Property Key As String
Public Property Value As String
End Class
The idea is that you've got a single instance of LookupData (a Module in VB, there can be only one). Lookup data has a series of Properties, each of which returns a list of values from the database. If the data has already been loaded, it just returns what it has cached. If the data has not been loaded, then the first time it is referenced it retrieves it from the database.
You can consume it elsewhere in your code as follows:
Dim myStatuses = LookupData.Statuses

How To MustOverride Shared/Constructor Function Visual Basic

I'm currently using Visual Basic for a College Project which requires us to make a simple database system. For my system I have a base(abstract) class called Record which is inherited by the different types of records there are in my database e.g. Member, User, Role.
I am saving my data in csv files and have already written a CSVHandler class. However, I want an elegant way of constructing an instance of a class derived from Record with a string from the CSVHandler.
This is where the problem occurs. The only way I can think of doing this is by making a Constrcutor or Shared Function in each class derived from Record. However, Visual Basic does not allow you make Constructors or Shared Functions also MustOverride.
Here is the code I would expect to write:
' Base Class
Public MustInherit Class Record
Public MustOverride Shared Function fromString(ByVal str as String) As Record
End Class
' Example Of Class Derived From Record
Public Class User
Inherits Record
Private _id As String
Private _name As String
Public Sub New(ByVal id As String, ByVal name As String)
_id = id
_name = name
End Sub
Public Overrides Shared Function fromString(ByVal str as String) As Record
Dim strs() As String = str.Split(",")
Return New User(strs(0), strs(1))
End Function
End Class
' Example Of Creating Instacnce Of User
Dim user1 = User.fromString("1671,Kappeh")
Is there a way to achieve this effect?
Have your constructor call a Protected MustOverride method that does the initialisation.
Public MustInherit Class Record
'This is required because each derived constructor must be able to implicitly invoke a parameterless
'base constructor if it doesn't explicitly invoke a base constructor with parameters.
Protected Sub New()
End Sub
Public Sub New(csv As String)
Init(csv)
End Sub
Protected MustOverride Sub Init(csv As String)
End Class
Public Class User
Inherits Record
Private Property Id As String
Private Property Name As String
'This is still required because you can use a base constructor directly to create a derived instance.
Public Sub New(csv As String)
MyBase.New(csv)
End Sub
Public Sub New(id As String, name As String)
Id = id
Name = name
End Sub
Protected Overrides Sub Init(csv As String)
'Add your type-specific implementation here.
End Sub
End Class
This "solution" doesn't actually do what I thought it would because, while it forces you to override Init in a derived class, you still have to provide a derived constructor that invokes the base constructor that calls Init and you still can't enforce that. I think that I'll leave this as an answer though, because, while it doesn't actually provide a solution to your problem, it demonstrates further why (as far as I can tell) there is no such solution.
The following is similar to the answer from #jmcilhinney in that it forces the derived class to implement an initialization method. However it makes use of a generic shared function and uses the little known GetUninitializedObject method to get around using the generic New constraint and it's requirement of an accessible parameter-less constructor.
Public MustInherit Class Record
Public Shared Function fromString(Of T As {Record})(ByVal str As String) As T
' create an unintialized instance of T
Dim ret As T = DirectCast(System.Runtime.Serialization.FormatterServices.GetUninitializedObject(GetType(T)), T)
ret.Initialize(str)
Return ret
End Function
Protected MustOverride Sub Initialize(source As String)
End Class
The User class then would be something like this:
Public Class User : Inherits Record
Private _id As String
Private _name As String
Public Sub New(ByVal id As String, ByVal name As String)
_id = id
_name = name
End Sub
Protected Overrides Sub Initialize(source As String)
Dim strs() As String = source.Split(","c)
_id = strs(0)
_name = strs(1)
End Sub
End Class
Example usage:
Dim userRecord As User = Record.fromString(Of User)("1,2")

SteamAPI JSON.net Error in VB .net

I dont really understand what I do wrong when I try to Deserialize the JSON stuff I get.
You can get the response from http://api.steampowered.com/ISteamApps/GetAppList/v2?format=json
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim SteamGameInfoObject As Object = JsonConvert.DeserializeObject(Of Object)(New System.Net.WebClient().DownloadString("http://api.steampowered.com/ISteamApps/GetAppList/v2?format=json"))
End Sub
Public Class SteamRootObject
Public Class SteamAPIJSONResult
Public Property applist As List(Of applistlist)
End Class
Public Class applistlist
Public Property apps As List(Of appslist)
End Class
Public Class appslist
Public Property appid As Integer()
Public Property name As String()
End Class
End Class
End Class
I dont know how to "To fix this error either change the JSON to a JSON array"
You have a few problems here:
You define appid and name as arrays of integers and strings, respectively. In the JSON these are primitive values not arrays, so the VB.NET properties themselves should be declared to be simple integers and strings.
You define applist as a List(Of applistlist). Despite the name, the property value in the JSON is a single object not an array, so the property should refer to a single object also.
In Form1_Load you need to deserialize to the correct type, SteamRootObject. If you do JsonConvert.DeserializeObject(Of Object)(jsonString) you are not telling Json.NET the type to which to deserialize!
Stylistically there is no need to nest the inner classes inside the root class, it adds to complexity.
There are plenty of on-line tools to auto-generate .Net classes from JSON that can be found quickly using google, for instance http://json2csharp.com/ (for c#) or http://jsonutils.com/ or http://www.httputility.net/json-to-csharp-vb-typescript-class.aspx. In the future, use one of these to check your work.
Thus your fixed classes should look like:
Public Class App
Public Property appid As Integer
Public Property name As String
End Class
Public Class Applist
Public Property apps As New List(of App)
End Class
Public Class SteamRootObject
Public Property applist As Applist
End Class
And, to deserialize:
Dim SteamGameInfoObject = JsonConvert.DeserializeObject(of SteamRootObject)(jsonString)
Prototype fiddle.

WCF serializing struct of Array of Classes

I have something similar to the flowing two simple classes:
<DataContract> _
Public Class C1
Public Sub New(x_ As Int64)
_x= x_
End Sub
<DataMember()> _
Public Property x As Int64
End Class
<DataContract> _
Public Class C2
Public Sub New(y_ As string)
_y= y_
End Sub
<DataMember()> _
Public Property y As string
End Class
and the following structure that made of both:
<DataContract, StructLayout(LayoutKind.Sequential), _
KnownType(GetType(C1)), KnownType(GetType(C2))> _
Public structure S1
<DataMember, MessageHeaderArray> _
Public Second() As C1
<DataMember> _
Public First As C2
End structure
Finally, I'm calling a sub from a server's interface that sends an array of those structures as follows:
<ServiceContract(CallbackContract:=GetType(IClient))> _
Public Interface IServer
<OperationContract> _
Sub SendToServer(ByVal S()As S1)
End Interface
The problem is when I'm trying to pass this using WCF I'm getting a strange exception of the following format:
The use of type 'S1' as a get-only collection is not supported with NetDataContractSerializer. Consider marking the type with the CollectionDataContractAttribute attribute or the SerializableAttribute attribute or adding a setter to the property.
I tried to use the two suggested attributes, but I found that they should be used on declaring new class collection of types public class xx(Of T), not for my case (an exception telling that when used), I really can't understand about which get-only property he speaking about, because I have none, anyway, removing Public Second() As C1 from the structure, or passing one instance of the structure instance of an array, fixes the problem.
So I tried to change S1 to a class instead of structure, and the array into a List with get & set parts, but nothing helps, any clue what is going here?
Is there a reason you are using NetDataContractSerializer? Unless you are trying to support some legacy code, I believe you should use DataContractSerializer.

ListBox ObservableCollection duplicating

I have a WPF application which has a listbox bound to an ObservableCollection which retrieves it's data from a Database. I am attempting to have the ListBox data refreshed every minute through the use of a DispatcherTimer.
Dim dispatcherTimer As DispatcherTimer = New System.Windows.Threading.DispatcherTimer
AddHandler dispatcherTimer.Tick, AddressOf getRoomMeetingDetails
dispatcherTimer.Interval = New TimeSpan(0, 2, 0)
dispatcherTimer.Start()
Which calls the getRoomMeetingDetails method as follows.
Public Sub getRoomMeetingDetails()
If Not My.Settings.rbConn = Nothing And _
Not gl_rmName = Nothing Then
Dim sqlConn As New SqlConnection(My.Settings.rbConn)
Dim sqlquery As String = "SELECT *" & _
"FROM table " & _
Dim sqlCmd As New SqlCommand(sqlquery, sqlConn)
sqlConn.Open()
Dim dr As SqlDataReader
dr = sqlCmd.ExecuteReader
While dr.Read
roomMeetingList.Add(New meetingDetails() With {.eMeetingId = dr.Item("dId")})
End While
End If
End Sub
I then have my two classes for the Collection as follows (I am very new to ObservableCollections and have tried my best to model my code off the MSDN examples, so if this isn't the best method to use to achieve what I am trying to achieve, or can be done easier, please let me know)
Public Class MeetingList
Inherits ObservableCollection(Of meetingDetails)
Private Shared list As New MeetingList
Public Shared Function getList() As MeetingList
Return list
End Function
Private Sub New()
AddItems()
End Sub
Public Shared Sub reset(ByVal rmName As String)
list.ClearItems()
list.AddItems()
End Sub
Private Sub AddItems()
End Sub
End Class
Public Class meetingDetails
Implements INotifyPropertyChanged
Public Sub New()
End Sub
Public Property eID() As String
Get
Return _eID
End Get
Set(ByVal value As String)
_eID = value
OnPropertyChanged("eID")
End Set
End Property
Private _eID As String
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
End Class
What is happening is when the DispatcherTimer is called every minute, the ListBox data is duplicated which I believe is because the getRoomMeetingDetails method is adding all of the SQL results on every tick. How can I refresh the ListBox with only new data or data changes from the table?
I am really struggling to work out where I am going wrong and what needs to be added/removed for this to work.
If there is any details I am missing please let me know.
Matt
Either you clear all the data in the listbox before adding them again or you do a check on the collection. I assume your eID is the primary key? the do something like this:
if ( roomMeetingList.Where ( entry => entry.eID == dbID ).Count () == 0 ) {
// add
}
C# code, but it shows the idea
developerFusion's convert made this VB:
If roomMeetingList.Where(Function(entry) entry.eID = dbID).Count() = 0 Then
' Add
End If

Resources