WCF serializing objects with inheritance - wcf

Here's what I am trying to do. I have a WCF restful service, and I need to serialize multiple objects that inherit from the same class.
There is nothing in any of the base classes that needs to be serialized.
Here is a minimal demo that shows what I want to get to work:
<DataContract()>
Public Class BaseObj
<DataMember()>
Public ID As Integer
Public Sub New(ByVal idval As Integer)
ID = idval
End Sub
End Class
<DataContract()>
Public Class TestObj1
Inherits BaseObj
Public Sub New(ByVal id As Integer)
MyBase.New(id)
End Sub
End Class
' Different from TestObj1 in real life
<DataContract()>
Public Class TestObj2
Inherits BaseObj
Public Sub New(ByVal id As Integer)
MyBase.New(id)
End Sub
End Class
And here's the code that uses it:
<ServiceContract()>
<AspNetCompatibilityRequirements(RequirementsMode:=AspNetCompatibilityRequirementsMode.Allowed)>
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.PerCall)>
Public Class Service1
<WebGet(ResponseFormat:=WebMessageFormat.Json, UriTemplate:="Test?reqReportID={reqReportID}")>
Public Function GetCollection(ByVal reqReportID As Integer) As List(Of BaseObj)
Dim myObjs As New List(Of BaseObj)
myObjs.Add(New TestObj1(20))
myObjs.Add(New TestObj2(20))
Return myObjs
End Function
End Class
If I declare the List to be a list of TestObj1 instead, everything works.
Am I missing some crucial concept here?
EDIT:
The problem gains a new level of confusion by looking at this code:
<WebGet(ResponseFormat:=WebMessageFormat.Json, UriTemplate:="Test?reqReportID={reqReportID}")>
Public Function GetCollection(ByVal reqReportID As Integer) As BaseObj()
Dim myObjs As New List(Of BaseObj)
myObjs.Add(New TestObj1(20))
myObjs.Add(New TestObj2(20))
' This guy works. Yields correct result of [{"ID":20},{"ID":20}] )
Dim testStr As String = New JavaScriptSerializer().Serialize(myObjs.ToArray())
' But this guy still has problems...
Return myObjs.ToArray()
End Function

What you are missing is a [KnownType] attribute.
WCF requires a way of knowing all possible types so that it could publish the WSDL.
Have a look here.
UPDATE
The problem is that List<T> is not covariant.
Use IEnumerable<T> instead.

Related

Interface usage within three projects

I will try to simplify as possible. In my solution i got 3 separated projects:
Main - where i use Bal
Bal - business logic, eg. classes:
Artikel
Material
Dal - data layer logic, eg. classes:
DALArtikel
DALMaterial
Now Dal's classes implements interface IDAL which looks like follows:
Public Interface IDAL
Function isExist(name As String) As Boolean
Function GetIdByName(name As String) As Integer
Function GetNameById(pId As Integer) As String
End Interface
Then i can call interface's methods from Bal's project. Every BAL's class has it's DAL class like for Artikel is DALArtikel.
Now every BAL's classes inherits from one Base class which looks like below. This Base class implements interface similar to mentioned above called IGetInformation
Public Class Base
Implements IGetInformation
Property Id As Integer
Property DAL As DataLayer.IDAL
Protected Sub New()
End Sub
Protected Sub New(dal As DataLayer.IDAL)
Me.DAL = dal
End Sub
Protected Sub New(pId As Integer)
_Id = pId
End Sub
Public Overridable Function IsExist(name As String) As Boolean Implements IGetInformation.IsExist
Return DAL.isExist(name)
End Function
Public Overridable Function GetNameById(pId As Integer) As String Implements IGetInformation.GetNameById
Return DAL.GetNameById(pId)
End Function
Public Overridable Function GetIdByName(pName As String) As Integer Implements IGetInformation.GetIdByName
Return DAL.GetIdByName(pName)
End Function
Mentioned interface:
Public Interface IGetInformation
Function isExist(name As String) As Boolean
Function GetIdByName(name As String) As Integer
Function GetNameById(pId As Integer) As String
End Interface
So every Bal's class like Artikel is constructed as following:
Public Class Artikel
Inherits Base
Property Serie As String
Property Nummer As String
Property Name As String
Sub New()
MyBase.New(New DALArtikel)
End Sub
Sub New(pId As Integer)
MyBase.New(New DALArtikel)
Id = pId
End Sub
Sub New(pId As Integer, pSerie As String)
MyBase.New(New DALArtikel)
Id = pId
Serie = pSerie
End Sub
This way i can instatiate artikel class in Main project and call it's e.g isExist method without specyfing specific DAL class associated with it as in Artikel class constructor it was already specified. The problem is now when i want to add new method which will be not in IDAL interface i have to implement like this in Artikel:
Public Function IsExistBarcode(barcode As String) As Boolean
Return New DataLayer.DALArtikel().CheckIfBarcodeExist(barcode)
End Function
so this time i have to specify DALArtikel before i call CheckIfBarcodeExist as my property DAL doesn't know it.
Generally i don't like the way as it is currently, you see that i use two separate exactly the same content interfaces for bal's and dal's projects and the logic behind. Do you know other efficient way which i could change current logic to be let's say 'better'?
Appreciate possible improvment example according to my situation. Sorry for long post but couldn't make it more less. If something unclear let me know.

Incompatible interface

Please see the code below:
Public Class clsCar
Implements IVehicle
Public Function getWheels() As Integer Implements IVehicle.getWheels
Return 4
End Function
End Class
Public Interface IVehicle
Function getWheels() As Integer
End Interface
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim list As List(Of IVehicle) = New List(Of IVehicle)
Dim v1 As IVehicle = New clsCar
Dim v2 As IVehicle = New clsBus
list.Add(v1)
list.Add(v2)
Dim IVehicle As IVehicle = New clsCar
Dim IVehicle2 As IVehicle = New clsBus
For Each IVehicle In list
MsgBox(IVehicle.getWheels())
Next
End Sub
End Class
I want to add a new function to the clsBus class:
Public Function getWheels(ByVal strCarType As String) As Integer
If strCarType = "Robin Ryliant" Then
Return 3
Else
Return 4
End If
End Function
How do I call this from the FOR EACH statement? At the moment it will call getWheels with no arguments.
You will have to add the method overload to the interface in order to be able to call it from a IVehicle variable.
Public Interface IVehicle
Function getWheels() As Integer
Function getWheels(ByVal strCarType As String) As Integer
End Interface
But probably it is better to have different, more specialized car types
Public Class clsCar
Implements IVehicle
Public Overridable Function getWheels() As Integer Implements IVehicle.getWheels
Return 4
End Function
End Class
Public Class clsRobinRyliantCar
Inherits clsCar
Public Overrides Function getWheels() As Integer
Return 3
End Function
End Class
This does not break the inheritance hierarchy and is purely polymorphic.
I think I would go for something more like this, with the number of wheels being an instance property (code in LINQPad format):
Sub Main
Dim list As List(Of IVehicle) = New List(Of IVehicle)()
list.Add(New clsCar("Ford Focus", 4))
list.Add(New clsCar("Robin Ryliant", 3))
list.Add(New clsBus())
For Each v In list
Console.WriteLine(String.Format("{0}:{1}", v.GetVehicleType(), v.GetWheels()))
Next
End Sub
' Define other methods and classes here
Public Interface IVehicle
Function GetVehicleType() As String
Function GetWheels() As Integer
End Interface
Public MustInherit Class clsVehicle
Implements IVehicle
Protected Property VehicleType As String
Protected Property Wheels As Integer
Protected Sub New(vehicleType As String, wheels As Integer)
Me.VehicleType = vehicleType
Me.Wheels = wheels
End Sub
Public Function GetVehicleType() As String Implements IVehicle.GetVehicleType
Return Me.VehicleType
End Function
Public Function GetWheels() As Integer Implements IVehicle.GetWheels
Return Me.Wheels
End Function
End Class
Public Class clsCar
Inherits clsVehicle
Public Sub New(vehicleType As String, wheels As Integer)
MyBase.New(vehicleType, wheels)
End Sub
End Class
Public Class clsBus
Inherits clsVehicle
Public Sub New()
MyBase.New("Bus", 4)
End Sub
End Class
If your clsBus and clsCar are meant to refer to a specific car then the type should be a member of that class already, not something you pass in when you want to get information. To this end I'd suggest that you have "type" as something you can pass in the constructor and then the method on the bus would have no parameters and would just refer to its internal type to determine how many wheels it has.
I'm not too fluent with VB.NET so would probably make mistakes in example code but hopefully you get what I mean. If not I'll knock up some code. :)

Setting Up These Types While Keeping It Properly Structured

I'm completely stuck in a situation and I have no idea on where to go from here. I'm creating a very large project, so my goal is to keep the code itself as clean as possible and keeping as many hacks as possible out of the mix.
Here is the situation.
I have a class called Woo_Type, it is the parent of my many derived classes.
Public MustInherit Class Woo_Type
Private Shared TypeList As New Dictionary(Of String, Woo_Type)
Public MustOverride Sub SetValue(ByVal val As Object)
Public MustOverride Function GetValue() As Object
Public Shared Function GetTypeFromName(ByVal name As String) As Woo_Type
Return TypeList(name)
End Function
Public Shared Sub AddType(ByVal name As String, ByVal def As Woo_Type)
TypeList.Add(name, def)
End Sub
End Class
I have many classes that Inherit from Woo_Type that have similar structures to this:
Public Class Woo_tpInt
Inherits Woo_Type
Private value As Integer = 0
Public Overrides Function GetValue() As Object
Return value
End Function
Public Overrides Sub SetValue(val As Object)
value = val
End Sub
End Class
I want to be able to do things like:
Woo_Type.GetTypeFromName("int")
And have it return something like the class or something...
At this point I'm really confused as to what I want and I didn't know if anybody had any suggestions. To make sure that GetTypeFromName worked correctly, I had in an Initializer sub the following:
Public Sub InitializeTypes()
Woo_Type.AddType("int", Woo_tpInt)
Woo_Type.AddType("String", Woo_tpInt)
End Sub
But I quickly realized that-that obviously doesn't work either.
So this may seem confusing but I'm basically wondering how to better structure this so that everything works...
What do you want to do with the result? Are you sure you don't simply need generics?
Public Class WooType(Of T)
Public Property Value As T
End Class
Public Class Test
Public Sub Foo()
Dim int As New WooType(Of Integer)
int.Value = 42
Dim str As New WooType(Of String)
str.Value = "Forty-Two"
End Sub
End Class
If what you want to do is get the type itself (as opposed to an object), I would recommend using reflection rather than trying to reinvent the wheel. For instance, to get the Woo_tpInt type, you could do this:
Dim a As Assembly = Assembly.GetExecutingAssembly()
Dim t As Type = a.GetType("WindowsApplication1.Woo_tpInt") ' Change WindowsApplication1 to whatever your namespace is
If you want to use a shorter name like "int" to mean "WindowsApplication1.Woo_tpInt", you could create a dictionary to store the translation table, for instance:
Dim typeNames As New Dictionary(Of String, String)
typeNames.Add("int", GetType(Woo_tpInt).FullName)
Dim a As Assembly = Assembly.GetExecutingAssembly()
Dim t As Type = a.GetType(typeNames("int"))

Generic Collections, Member Classes, Design Pattern question for VB.NET

I have a class called Person:
Public Class Person
Private PersonID as String
Private Name as String
Private Records as GenericCollection(Of PublicRecord)
Public Sub New(ByVal ID as String)
Me.PersonID = ID
Me.Name = getPersonName(ID)
End Sub
'Get/Sets
End Class
getPersonName is simply a function that does exactly as it is described. GenericCollection class is as follows:
Public Class GenericCollection(Of ItemType)
Inherits CollectionBase
' Purpose: Provides a generic collection class from which all other collections
' classes can be inherited if they wish to extend the functionality below.
#Region "Public Methods"
Public Function Add(ByVal NewObject As ItemType) As Integer
Return MyBase.InnerList.Add(NewObject)
End Function
Public Sub New()
MyBase.New()
End Sub
#End Region
#Region "Public Properties"
Default Public Property Item(ByVal Index As Integer) As ItemType
Get
Return CType(MyBase.InnerList(Index), ItemType)
End Get
Set(ByVal value As ItemType)
MyBase.InnerList(Index) = value
End Set
End Property
#End Region
End Class
PublicRecord class is:
Public Class PublicRecord
Private RecordID As String
Private RecordDataOne As String
Private RecordDataTwo As String
Public Sub New()
MyBase.New()
End Sub
'Get/Sets
End Class
One of the requirements I've been told can be done is that I should be able to grab all Persons in a Collection of Persons, then since all of those Persons will have Collectinos of Records within them... grab a specific set of data from the Collection of Records.
We'll say, I want to: getPersonsOverAge21() from the Collection of Records inside each Person inside the Collection of Persons.
Is this even possible? If so, can someone explain how it would work?
There's no need to implement your own generic collection class. .Net has already done this for you in the System.Collections.Generic namespace. Look at a List(Of Person) or even just a simple IEnumerable(Of Person).
Now you haven't explained how your record objects relate to your person type or what data they contain, so I can only speculate on the next part. But it sounds kind of like you want something like this:
Dim people As List(Of Person) = GetPeopleFromDatabase()
Dim peopleOver21 As IEnumerable(Of Person) = people.Where(Function(p) p.Age >= 21)
Dim peopleOver21Query = From p In people _
Where (p.Age >= 21) _
Select p

Similar classes with different signatures

I have two classes:
Public Class Subscribing
Private _subscribingObjects As IList(Of String)
Public Sub Add(ByVal obj As SubscribeObject)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Public Class Providing
Private _providingObjects As IList(Of String)
Public Sub Add(ByVal obj As ProvideObject)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Is there a more elegant way to add do this? One class would suffice, but since the Add methods have different arguments, then one really wouldn't work.
Any help would be appreciated.
this?
Public Class SubscribingProviding(Of t)
Private _subscribingObjects As IList(Of String)
Public Sub Add(ByVal obj As t)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Your add functions should be fine. As long as you have different variable types being passed in you can have the function names be the same. Your remove Subs will not be allowed in the same class because it is using the same parameter Integer.
Eh.. probably not. They are different enough that you cant even Interface them.
I personally wouldn't mix the two responsibilities (of subscribing and providing) in one class. The classes themselves can easily be simplified by just inheriting from List(Of T)
Public Class Subscribing
Inherits List(Of SubscribeObject)
End Class
Public Class Providing
Inherits List(Of ProvideObject)
End Class
If you really want to get down to one class and make sure that it can only accept SubscribeObject and ProvideObject respectively, implement a common interface in both SubscribeObject and ProvideObject. Then create a generic class that accepts the interface:
' Common interface '
Public Interface ISubscribeProvideObject
End Interface
' SubscribeObject and ProvideObject both implementing the common interface '
Public Class SubscribeObject
Implements ISubscribeProvideObject
'...'
End Class
Public Class ProvideObject
Implements ISubscribeProvideObject
'...'
End Class
' Generic class accepting both types '
Public Class SubscribingProviding(Of T As ISubscribeProvideObject)
Inherits List(Of T)
'... Add() and Remove() methods only needed if custom logic applies ...'
End Class