Base Classes on Proxy Objects - vb.net

I have a webservice that is wrapped up by a data access object and is accessed by many different UI controls.
The proxy objects look something like this:
Public Class WebProxyObject1
' Common properties, there are about 10 of these
Public Name As String
Public Address As String
' Specialized properties there are about 20 of these
Public count As Integer
End Class
The DAL layers look something like this:
Public Class DataAccessObject
Implements IDataAccessObject
' These are called in MANY, MANY, MANY locations
Public Function GetObject(ByVal name As String) As WebProxyObject1 Implements IDataAccessObject.GetObject
' Makes call to a webservice
Return New WebProxyObject1
End Function
Public Function ListObjects() As System.Collections.Generic.List(Of WebProxyObject1) Implements IDataAccessObject.ListObjects
' Makes call to a webservice
Dim list As New List(Of WebProxyObject1)
Return list
End Function
End Class
Now, I need to add a 2nd webservice to the mix. The goal is to reuse the UI controls that are currently coded to use the Proxy Objects that come from the first webservice. There are about 10 common properties and about 20 that are different. To add the 2nd webservice, I'd create a 2nd DAL object that implements the same interface. The problem is that it currently returns the proxies from the first webservice.
My thought on how to solve this is to extract an interface from each of the proxy objects and mash them together. Then implement the new interface on both proxy objects. That will create a huge class/interface where some properties aren't used. Then have the DAL return the interface.
The problem that I'm facing isn't a real bug or an issue, but extracting the 2 interfaces and smashing them together just feels kind of wrong. I think it would technically work, but it kind of smells. Is there a better idea?
The resulting interface would look like this:
Public Interface IProxyObject
' Common
Property Name() As String
Property Address() As String
' Specialized
Property Count() As Integer
Property Foo() As Integer
End Interface

Create a base class for your WebProxyObjects to inherit from.
Public MustInherit Class WebProxyObjectBase
' Common properties
Public Property Name As String
Public Property Address As String
End Class
Next create your two WebProxyObjects:
Public Class WebProxyObject1
Inherits From WebProxyObjectBase
' Specialized properties
Public Property count As Integer
End Class
Public Class WebProxyObject2
Inherits From WebProxyObjectBase
' Specialized properties
Public Property foo As Integer
End Class
Next have your DAL return the Base Class:
Public Class DataAccessObject
Implements IDataAccessObject
' These are called in MANY, MANY, MANY locations
Public Function GetObject(ByVal name As String) As WebProxyObjectBase Implements IDataAccessObject.GetObject
' Makes call to a webservice
Return New WebProxyObjectBase
End Function
Public Function ListObjects() As System.Collections.Generic.List(Of WebProxyObjectBase) Implements IDataAccessObject.ListObjects
' Makes call to a webservice
Dim list As New List(Of WebProxyObjectBase)
Return list
End Function
End Class
Then when calling your DataAccessObject you'll be able to ctype the return to the proper class:
Dim DAO as New DataAccessObject
Dim Pxy1 as WebProxyObject1 = TryCast(DAO.GetObject("BOB"), WebProxyObject1)
If Pxy1 IsNot Nothing Then
'Do stuff with proxy
End If

Related

Method doesnt want to move to Generic Class

I am wrapping a COM API.
In general, I have had good luck designing some generic classes and shoving the tested parts down into those classes.
Here is one that is giving me a problem.
There are classes that represent result sets. They do not inherit, they do implement a common interface, but it is a very simple interface. It does not expose the ResultSet functionality, specifically .COUNT or .GetAt(i)
My workaround is to make this a MustInherit and use CodeSmith to do the work for me. Not the end of the world. 13 more lines of generated code per entity.
I have played around with a class that might bridge this, and an interface that might bridge this, but I keep coming back to the fact that there is no common 'thing' in the API that represents a result set.
I may be missing something, I certainly am not seeing the solution.
The code for one instance of the work around is listed below
I would like to move this function to the Generic. It currently sits in each instance of class that uses the generic.
ICustomerRetList inherits from IBase. IBase has neither .Count or .GetAt() as mentioned above.
To be clear- My question is this : Can you suggest a vb construct that will allow me to move this function from my concrete class, down to my generic class
Public Overrides Function RetListToList(RetList As ICustomerRetList) As List(Of Customer)
Dim oItem As ICustomerRet
Dim oItem As Customer
Dim l As New List(Of Customer)
For idx = 0 To RetList.**Count** - 1 '.Count is not a member of IBase
oqbItem = RetList.**GetAt**(idx) '.GetAt() is not a member of IBase
oItem = New Customer()
'add the Item to the list
Call l.Add(oItem)
Next
Return l
End Function
If all implementations of IBase have these methods, and they all have the same names, you could combine extension methods and reflection to effectively lower the functions.
Public Class CustomerRetListExtensions
<Extension()>
Public Function GetAt(ByVal list As IBase, ByVal idx As Integer) As IBase
Return DirectCast(list.GetType().GetMethod("GetAt").Invoke(list, New Object() { idx }), IBase)
End Function
' If Count is a property, otherwise use the same approach as for GetAt
<Extension()>
Public Function Count(ByVal list As IBase) As Integer
Return DirectCast(list.GetType().GetProperty("Count").GetValue(list), Integer)
End Function
End Class

So a VB interface can't have shared functions. Is there an alternative to creating dummy objects?

To avoid getting into the weeds on my particular program, let me just create a simplified case.
I have a generic class that should work on a variety of objects. Each of those objects must implement a certain interface.
What I WANT to say is something like:
Public Interface GenThing
Shared Function thing_name() As String ' This doesn't work! Can't be shared!
Sub FillOne(row As DataRow)
End Interface
public class Thing1
implements GenThing
public shared function thing_name() as string implements GenThing.thing_name
return "thing number one"
end function
public sub FillOne(row as DataRow) implements GenThing.MakeOne
... bunch of work ...
end sub
end class
public class ThingUtil(of T as {GenThing,New})
public function GetList(id as integer) as List(of T)
dim name=T.thing_name() ' This doesn't work!
dim ds as DataSet=GetData(name,id) ' bunch of work here that's the whole point of the class but not relevant to the question
dim my_list = new List(of T)
for each row as DataRow in ds.tables(0).rows
dim my_t = new T()
my_t.FillOne(row)
my_list.add(my_t)
next
return my_list
end function
end class
Do you get my problem? I need every class that implements the interface to have a function that returns a "name" that is used to get the data that is needed to create an instance of the object. But I need to know this name BEFORE I create the instance, because I need it to be able to create the instance. But VB doesn't allow an interface to have a shared function, so what I want to write doesn't work.
So what I've done is this:
I make thing_name not shared.
Then instead of simply "dim name=T.thing_name()", I write
dim dummy = new T()
dim name = dummy.thing_name()
Okay, it works, but it seems really ugly. I create an instance of the object, with all the overhead that that involves, just to get a piece of constant text.
Is there a better way? Or am I making a big deal out of nothing?
Update
I see that two people voted to close this question on the grounds that it is the same as "Why can't we have shared functions in an interface?"
I am not asking why I can't have a shared. I am saying, GIVEN that I can't, how do I solve this particular problem?
There's no really simple way of fixing this, no.
Depending on what thing_name does, however, you might approach things in a different way. If each implementation just returns a constant value, then it's effectively metadata about the class - and could be described in an attribute instead, which can be fetched at execution time. (See Type.GetCustomAttributes.) Unfortunately you can't then enforce all types implementing the interface to be decorated with the attribute - but you could write a unit test to check this pretty easily.
If thing_name needs to really do work at execution time, that's tougher. You could potentially look for a well-known shared method name instead and execute that via reflection (and again have unit tests to check that it's implemented properly).
I realize this is from a few years ago, but running into a similar problem, I wanted to offer a different solution. Pass a delegate as parameter to the ThingUtil constructor. You avoid having to put a shared method in an interface, and the constructor will force you to include the parameter at compile time.
You can add more delegates if needed, or to make it even simpler in this case, just pass name as a string instead of get_name as a delegate.
Define the delegate in the interface:
Public Interface GenThing
Delegate Function ThingNameDelegate() As String
Sub FillOne(row As DataRow)
End Interface
Public Class Thing1
Implements GenThing
Public Shared Function thing_name() As String 'name this whatever you want
Return "thing number one"
End Function
Public Sub FillOne(row As DataRow) Implements GenThing.FillOne
'do stuff
End Sub
End Class
In ThingUtil, add a member to store the delegate, a constructor parameter to to accept, and call it with .Invoke():
Public Class ThingUtil(Of T As {GenThing, New})
Private m_thing_name As GenThing.ThingNameDelegate
Public Sub New(thing_name As GenThing.ThingNameDelegate)
m_thing_name = thing_name
End Sub
Public Function GetList(id As Integer) As List(Of T)
Dim name = m_thing_name.Invoke()
Dim ds As DataSet = GetData(name, id) ' bunch of work here that's the whole point of the class but not relevant to the question
Dim my_list = New List(Of T)
For Each row As DataRow In ds.Tables(0).Rows
Dim my_t = New T()
my_t.FillOne(row)
my_list.Add(my_t)
Next
Return my_list
End Function
End Class
Finally, use it like this:
Dim tu as new ThingUtil(Of Thing1)(AddressOf Thing1.get_name)
tu.GetList(1)

How to get properties from nested object using reflection and recursion?

I have a set of classes, whose properties have data annotations on them. Some of these class properties are of primitive types (and by primitive, I also mean types such as string, double, datetime etc), while others are properties of a custom type.
I would like to be able to iterate through the properties of a class and the properties of the nested objects and pull out the attributes of each property. I’ve played around with reflection and my code works fine, if the class under consideration has only one property of a custom type.
However when a class has multiple properties of a custom type and each of those properties have other custom types, I am completely lost on how I’d keep track of the objects/properties that have already been visited.
This is where I have got so far. I have seen a lot of examples on the forum, but they all have a simple nested class, where there is a maximum of one custom type per class.
Below is a sample of what I am trying to get done:
Public Class Claim
<Required()>
<StringLength(5)>
Public Property ClaimNumber As String
<Required()>
Public Property Patient As Patient
<Required()>
Public Property Invoice As Invoice
End Class
Public Class Patient
<Required()>
<StringLength(5)>
Public Property MedicareNumber As String
<Required()>
Public Property Name As String
<Required()>
Public Property Address As Address
End Class
Public Class Address
Public Property Suburb As String
Public Property City As String
End Class
Public Class Invoice
<Required()>
Public Property InvoiceNumber As String
<Required()>
Public Property Procedure As String
End Class
Public Shared Function Validate(ByVal ObjectToValidate As Object) As List(Of String)
Dim ErrorList As New List(Of String)
If ObjectToValidate IsNot Nothing Then
Dim Properties() As PropertyInfo = ObjectToValidate.GetType().GetProperties()
For Each ClassProperty As PropertyInfo In Properties
Select Case ClassProperty.PropertyType.FullName.Split(".")(0)
Case "System"
Dim attributes() As ValidationAttribute = ClassProperty.GetCustomAttributes(GetType(ValidationAttribute), False)
For Each Attribute As ValidationAttribute In attributes
If Not Attribute.IsValid(ClassProperty.GetValue(ObjectToValidate, Nothing)) Then
ErrorList.Add("Attribute Error Message")
End If
Next
Case Else
Validate(ClassProperty.GetValue(ObjectToValidate, Nothing))
**** ‘At this point I need a mechanism to keep track of the parent of ClassProperty and also mark ClassProperty as visited, so that I am able to iterate through the other properties of the parent (ObjectToValidate), without revisiting ClassProperty again.**
End Select
Next
End If
Return Nothing
End Function
The most straightforward (and probably easiest) way to approach this is to keep a Dictionary of class property attributes keyed by class name.
If I were approaching this, I would probably create a class to hold the property attributes:
Public Class PropertyAttribute
Public PropertyName As String
Public PropertyTypeName As String
Public Required As Boolean
Public StringLength As Integer
End Class
Then create a class to hold information about each class' properties:
Public Class ClassAttributes
Public ClassName As String
' You could also use a dictionary here to key properties by name
Public PropertyAttributes As New List(Of PropertyAttribute)
End Class
Finally, create a dictionary of ClassAttributes to keep track of which custom classes you have already processed:
Public ProcessedClasses As New Dictonary(Of String, ClassAttributes)
The key for the dictionary is the classname.
When you are processing the attributes through reflection, if the property type is custom, check the dictionary for the existence of the class. If it is there, you don't have to process it.
If it is not there, add a new instance to the dictionary immediately (so that nested objects of the same type are safely handled) and then process the attributes of the class.

Extending a base class to a derived class

We have two classes BasicRace & AdvancedRace.
AdvancedRace inherits from BasicRace
I have a BasicRace but i want to 'convert' it to the advanced class.
See code below as a example:
Module Module1
Sub Main()
Dim bRace As New BasicRace With {.CourseID = 1, .MeetingDate = "2013-05-01", .RaceNumber = 1}
Dim aRace As New AdvancedRace
' Upgrade bRace to AdvancedRace???????
End Sub
End Module
Public Class BasicRace
Public Property MeetingDate As Date
Public Property CourseID As Integer
Public Property RaceNumber As Integer
End Class
Public Class AdvancedRace
Inherits BasicRace
Public Property RaceTitle As String
End Class
Any help would be great - I'm starting to think it can not be done unless i write a function to convert a basicRace to AdvancedRace going through each property one by one?
You can't "convert" from a base class to a subclass as such (you can't change the type of an existing object), but you can create new instance of the subclass that copies the properties from your base class.
Typical ways of implementing this for your classes might be:
a constructor in AdvancedRace that takes a BasicRace parameter and copies properties from it
a static method in AdvancedRace that takes a BasicRace parameter, creates the new object with copied properties, and then returns it
It's worth noting that this will result in two completely separate objects (one of each type) that aren't linked at all - changes in your AdvancedRace object won't be reflected in the BasicRace or vice-versa.

Deep Copy of an Object

Can I please have some help to perform a deep copy of an object.
Here is my code:
Option Explicit On
Option Strict On
<Serializable> Public Class [Class]
Private _Name As String
Private _ListOfFields As New List(Of Field)
Public Property Name As String
Get
Return _Name
End Get
Set(value As String)
_Name = value
End Set
End Property
Public Property ListOfFields As List(Of Field)
Get
Return _ListOfFields
End Get
Set(value As List(Of Field))
_ListOfFields = value
End Set
End Property
Public Function Clone() As [Class]
Return DirectCast(Me.MemberwiseClone, [Class])
End Function
End Class
Field is a Class that I have written myself as well.
What do I need to modify for the Clone() Function to return a deep copy?
You can create a clone of any class by calling this helper function:
Function DeepClone(Of T)(ByRef orig As T) As T
' Don't serialize a null object, simply return the default for that object
If (Object.ReferenceEquals(orig, Nothing)) Then Return Nothing
Dim formatter As New BinaryFormatter()
Dim stream As New MemoryStream()
formatter.Serialize(stream, orig)
stream.Seek(0, SeekOrigin.Begin)
Return CType(formatter.Deserialize(stream), T)
End Function
This works by serializing all the information from your class into a portable object and then rewriting it in order to sever any reference pointers.
Note: The passed in class and any other classes it exposes as properties must be marked <Serializable()> in order to use BinaryFormatter.Serialize
If you want to make your own class expose the clonable method itself, you can add the method and implement the ICloneable interface like this:
<Serializable()>
Public Class MyClass : Implements ICloneable
'NOTE - The Account class must also be Serializable
Public Property PersonAccount as Account
Public Property FirstName As String
Function Clone(ByRef orig As MyClass) As MyClass Implements ICloneable.Clone
' Don't serialize a null object, simply return the default for that object
If (Object.ReferenceEquals(orig, Nothing)) Then Return Nothing
Dim formatter As New BinaryFormatter()
Dim stream As New MemoryStream()
formatter.Serialize(stream, orig)
stream.Seek(0, SeekOrigin.Begin)
Return CType(formatter.Deserialize(stream), T)
End Function
End Class
Note: Be aware ICloneable comes with it's share of controversies as it does not indicate to the caller if it is performing a deep or shallow clone. In reality, you don't need the interface to be able to add the method to your class.
(As an aside, I probably would name your class something other than "Class").
If you wanted to do it all by hand you would need to follow steps like:
Ensure that your Field class also implements a deep copy Clone() method. If you haven't done this already, then this would likely involve its Clone() method creating a new object of type Field and then populating each of its properties based on the current object. If your Field class has properties which are other classes/complex types (e.g. classes you have created yourself) then they should also implement Clone() and you should call Clone() on them to create new deep copies
In your Clone() method for the class you would create a new object of type [Class], e.g. by calling its constructor
Set the Name property of the new object to the Name property of your current object
Create a new List(Of Field), let's call it listA for the sake of example
Iterate over your current list and assign a clone of each list item to listA. For example:
For Each item in _ListOfFields
listA.Add(item.Clone())
End
After that you can assign your new list (listA) to the object you have created in the Clone() method
There is an alternative (probably better) by-hand approach that is in VB.NET described here.
If you wanted to cheat a bit then you could just serialize your existing object and then deserialize it into a new object like the technique here
I would say the serialize then deserialize technique is the "easiest" one.