I'm learning how to access data on a DB using a WCF web service. My main issue I've run into is that when making a call for the result from the service, the return value is of type UniversityClass but no properties are available and I believe its real type is simply an object, therefore I have no access to any of its 'real' properties.
Heres a snippet of the class in my interface called 'Service1'
<ServiceContract()>
Public Interface IService1
<OperationContract()>
Function GetUniversity(ByVal universityID As Integer) As UniversityClass
End Interface
<DataContract()>
Public Class UniversityClass
Private _universityId As Integer
Public Property UniversityID As Integer
Get
Return _universityId
End Get
Set(value As Integer)
_universityId = value
End Set
End Property
and heres a preview of me making the call to the service to get the data
Dim client As New ServiceReference1.Service1Client
Dim result As New ServiceReference1.UniversityClass
Dim x = client.GetUniversityAsync(Integer.Parse(tbUniversityID.Text))
Dim r As WCFServiceExample.ServiceReference1.UniversityClass = Await x
If x.IsCompleted Then
result = x.Result
End If
tbResult.Text = result. _ _ _ _
'// ^ No properties accessible here even though it recognizes that result is of type UniversityClass
Upon inspecting ServiceReference1.UniversityClass I get taken to Referece.vb and notice that there is Partial Class UniversityClass which inherits object. I think perhaps that may be the reason why I don't have any access to the properties defined in my Service1 class because it thinks UniversityClass is an object with no type.
I've tried rebuilding all projects individually, rebuilding the solution and still nothing.
Would love some assistance with how to actually get an object of type UniversityClass to return from my service.
Recognizes the type: http://i50.tinypic.com/e5mhvk.jpg
No properties available: http://i50.tinypic.com/wmjui9.jpg
Any help would be greatly greatly appreciated!
Your client can't see the UniversityID because you haven't marked it as a data member:
<DataContract()>
Public Class UniversityClass
Private _universityId As Integer
<DataMember()>
Public Property UniversityID As Integer
Get
Return _universityId
End Get
Set(value As Integer)
_universityId = value
End Set
End Property
Related
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")
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.
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
Im just wondering how I go about saving an instance of a class to the silverlight isolated storage. I also need to know if it possible that the class that we save to isolated storage can have a list of instances of another class. Here is an example of the situation:
Public Class MySettingsToStore
private mPropertyA as string
Public Property PropertyA() As string
Get
Return mPropertyA
End Get
Set(ByVal value As string)
mPropertyA = value
End Set
End Property
private mlstOfSubClass as List(Of MySubClass)
Public Property lstOfSubClass() As List(Of MySubClass)
Get
Return mlstOfSubClass
End Get
Set(ByVal value As List(Of MySubClass))
mlstOfSubClass = value
End Set
End Property
End Class
Public Class MySubClass
private mPropertyA as string
Public Property PropertyA() As string
Get
Return mPropertyA
End Get
Set(ByVal value As string)
mPropertyA = value
End Set
End Property
private mPropertyB as string
Public Property PropertyB() As string
Get
Return mPropertyB
End Get
Set(ByVal value As string)
mPropertyB = value
End Set
End Property
End Class
So basically on load of the application I want to check if there is an instance of MySettingsToStore in the isolatedStorage if not I will create one and save it (and update it when needed), so the next time the application is started there will be an instance in the isolatedstorage to load.
Does anyone know how I go about this? Hope someone can help. Thanks in advance
One word: XMLSerializer
To elaborate a bit, you can serialize any class with public properties to a stream (e.g. in Isolated storage) and reverse that process on startup to load an existing file in ISO storage.
If a property is a collection of other classes, they too will be stored and restored by using XMLSerializer.
Ok - I'm pulling my hair out over what I thought was a simple scenario: create a custom Label for bilingual use that contained two additional properties (EnglishText, FrenchText). Currently its structured like this:
Public Class myCustomLabel
Inherits System.Windows.Controls.Label
Public myEnglishTextProperty As DependencyProperty = DependencyProperty.Register("myEnglishText", GetType(String), GetType(myCustomLabel), New PropertyMetadata("English", New PropertyChangedCallback(AddressOf TextChanged)))
Public myFrenchTextProperty As DependencyProperty = DependencyProperty.Register("myFrenchText", GetType(String), GetType(myCustomLabel), New PropertyMetadata("Francais", New PropertyChangedCallback(AddressOf TextChanged)))
Public Sub New()
'This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
'This style is defined in themes\generic.xaml
DefaultStyleKeyProperty.OverrideMetadata(GetType(myCustomLabel), New FrameworkPropertyMetadata(GetType(myCustomLabel)))
End Sub
Public Property myEnglishText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property
Public Property myFrenchText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property
Private Sub TextChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
If DesignerProperties.GetIsInDesignMode(Me) = True Then
Me.Content = myEnglishText
Else
If myUser.Language = "E" Then
Me.Content = myEnglishText
Else
Me.Content = myFrenchText
End If
End If
End Sub
End Class
My test window grid xaml is simple:
<Grid>
<my:myCustomLabel myEnglishText="English Text" myFrenchText="English Text" Height="25" Width="100" Background="Aqua" Foreground="Black"/>
</Grid>
This seems to work in the development environment - changing the English and French texts change the in the design preview and it works when the app runs and the test window is opened. But only the first time - if I open the test window a second time I receive the following message:
'myEnglishText' property was already
registered by 'myCustomLabel'.
I understand now that if I change the dependency property declarations to shared then this problem goes away - but that leads to a host of other problems like the callback function being required to be shared as well - and thus unable to update the Content (which needs to be instantiated with the class). All I really want is the content property to be updated in design time when the english and french labels are changed.
Is there a way around this? Or maybe are dependency properties overkill for what I need?
You are registering your dependency properties as instance variables, and during the instance constructor. So they are getting registered again every time you instantiate the control, which causes an error the second time. As you have found out, dependency properties need to be static (Shared) members:
Public Shared myEnglishTextProperty As DependencyProperty =
DependencyProperty.Register("myEnglishText", GetType(String), GetType(myCustomLabel),
New PropertyMetadata("English", New PropertyChangedCallback(AddressOf TextChanged)))
You probably need to call OverrideMetadata in your shared constructor (type initialiser) rather than your instance constructor as well.
Regarding your issue with the callback needing to be shared: yes, it will be, but one of the arguments to the callback is the label instance. So you can just cast that to label and call an instance method on that:
private static void TextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyLabel)d).TextChanged();
}
private void TextChanged()
{
// your code here
}
(forgive C# syntax)
Is the reason you don't want the callback method to be shared because you're accessing the "me" instance? If that's all it is, make it shared and use the "d" parameter. I don't know VB well enough to show you the code, but just create a variable of type myCustomLabel and assign "d" to it (with a cast). Then use that variable (say "lbl") instead:
If DesignerProperties.GetIsInDesignMode(lbl) = True Then
lbl.Content = myEnglishText
Else
If myUser.Language = "E" Then
lbl.Content = myEnglishText
Else
lbl.Content = myFrenchText
End If
End If
Also, there's a slight bug in your example code. Try using this:
Public Property myEnglishText() As String
Get
Return MyBase.GetValue(myEnglishTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myEnglishTextProperty, value)
End Set
End Property
Instead of this:
Public Property myEnglishText() As String
Get
Return MyBase.GetValue(myFrenchTextProperty)
End Get
Set(ByVal value As String)
MyBase.SetValue(myFrenchTextProperty, value)
End Set
End Property