Convert IEnumerable(Of Object) to class that Implements IEnumerable(Of Object)? - asp.net

Using VB.NET, I have ths class
Public Class MyCollectionClass
Implements IEnumerable(Of MyClass)
Public Property MadeThisClassCuzINeedToSetThis() As String
' code here
End Class
I want to do this, but get an exception saying I can't do this cast.
Dim objColl As MyCollectionClass
objColl = CType(IEnumerable(Of MyClass), MyCollectionClass)
Can anyone tell me how to get this to work. Thanks.

See this VB.NET/C# casting cheat sheet or the documentation on CType. The major problem is that the first parameter should be the instance to convert, not its type. This should work:
Dim myEnumerable As IEnumerable(Of MyObjectClass) = New MyCollectionClass()
Dim objColl = CType(myEnumerable, MyCollectionClass)
' objColl's type is inferred As MyCollectionClass
(note that as MyClass is a keyword, and I assume you actually have a different class name there, I changed it to MyObjectClass in my example)

Related

Connection String in Constructor

Right now I'm able to establish a connection within my class by calling it in each method by doing the following.
Dim sConnectionString As String = ConfigurationManager.AppSettings("Blah")
'Establish connection with db
Dim cnSqlConnection1 As New SqlConnection(sConnectionString)
Only problem is that I have to call it in each method. I was told that it was better to create a constructor for the class nad have the connection string it uses passed into the constructor.
Here's my attempt but can't seem to figure out since I'm still unable to reach it in the method.
Public Sub New(ByVal sConnectionString As String)
sConnectionString = ConfigurationManager.AppSettings("Blah")
End Sub
What is the best way to do it? Thanks in advance.
You should store the passed connectionstring in a global variable available in all of your class methods
Public Clas MyClass
Private String gs_conString
Public Sub New(ByVal sConnectionString As String)
gs_conString = sConnectionString
End Sub
Public Sub AMethod()
'Establish connection with db
Dim cnSqlConnection1 As New SqlConnection(gs_conString)
.....
End Sub
.....
End Class
Of course this means that every time you create an instance of this class you need to pass the connection string to your constructor
Dim cl As MyClass = new MyClass(ConfigurationManager.AppSettings("Blah"))
So it is probably better to use the constructor to extract the connection string automatically everytime you create an instance
Private String gs_conString
Public Sub New()
gs_conString = ConfigurationManager.AppSettings("Blah")
End Sub
Go with the first option, putting the connection string in the constructor. You don't want your class to depend directly on <appSettings>.
Your class's interface should indicate what dependencies it has. When you put the connection string in the constructor, the class says, "Hey, I need a connection string!"
If the class calls <appSettings> then a user of the class has no way of knowing that the class expects to find a connection string there unless they open your code and read it. If they don't know that the connection string belongs there then they'll get a null reference exception with no explanation.
That raises the question - whatever class creates your class, where does it get the connection string so it can pass it to the constructor? Dependency injection is the answer. It enables you to write classes that way then "wire it up" so that the correct arguments get passed to your constructors.

Dim variable as datatype stored in db field

I have database table which has a field called "datatype". It will hold a particular datatype, such as string, integer, classname, etc....
How can I dim a variable using whatever is stored in this field? For example, if the field says "string" I want to effectively say "Dim MyVar as string", but fill in the "string" portion with what's stored in the table. Hope that makes sense. Thanks!
You can create instances from the name of the class. Note, you can't ask for "MyClass1" without the namespace before it. There are some options for getting from "MyClass1" to "Namespace.MyClass1" such as a Dictionary or even putting the full type name in your database.
Module Module1
Sub Main()
' compiler knows mc1 is a IMyClasses
Dim mc1 = CType(getInstanceFromTypeName("ConsoleApplication1.MyClass1"), IMyClasses)
' compiler doesn't know, mc2 is an object
Dim mc2 = getInstanceFromTypeName("ConsoleApplication1.MyClass2")
mc1.Foo()
mc2.foo()
End Sub
Private Function getInstanceFromTypeName(typeName As String) As Object
Dim o As Object
Try
o = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(typeName)
Catch ex As Exception
o = Nothing
End Try
Return o
End Function
End Module
Public Class MyClass1
Implements IMyClasses
Public Sub Foo() Implements IMyClasses.Foo
Debug.Print("MyClass1")
End Sub
End Class
Public Class MyClass2
Implements IMyClasses
Public Sub Foo() Implements IMyClasses.Foo
Debug.Print("MyClass2")
End Sub
End Class
Public Interface IMyClasses
Sub Foo()
End Interface
mc1.Foo() works because mc1 is declared as an IMyClasses, and IMyClasses defines this subroutine. The compiler knows that IMyClasses defines Foo.
mc2.foo() doesn't work with Option Strict On because Foo() is not a member of Object. With O.S.On, the compiler must be able to resolve all function calls at compile time. It works with Option Strict Off however, as O.S.Off allows function calls on Object, but can potentially be dangerous because O.S.Off also allows mc2.asdf(), for example.
Other resources:
Using System.Reflection
Using System.Activator

Problems implementing a recursive find extension method

I am attempting to implement a recursive extension method in VB.net that will find all objects with a certain property set so I can call it like so...
Dim camp As New CampaignStructure 'Populated with a full structure of course
Dim lstFoundItems As List(Of CategoryStructure) = camp.Categories.FindRecursive((Function(c) c.Found = False), 3)
My VB.Net classess and modules currently look like this
Imports System.Runtime.CompilerServices
Namespace MyStructure
Public Class CategoryStructure
Public Property CategoryID As Integer = Nothing
Public Property Name As String
Public Property Rank As Integer
Public Property Found As Boolean = False
Public Property Children As New List(Of CategoryStructure)
End Class
Public Class CampaignStructure
Public Property CampaignID As String = Nothing
Public Property Categories As List(Of CategoryStructure)
End Class
Public Module ControlExtensions
<Extension()> _
Public Function FindRecursive(cs As List(Of CategoryStructure), predicate As Func(Of CategoryStructure, Boolean), depthLimit As Integer) As List(Of CategoryStructure)
If cs Is Nothing OrElse cs.Count = 0 Then
Return New List(Of CategoryStructure)()
End If
If depthLimit = 0 Then
Return cs.OfType(Of CategoryStructure)().Where(predicate).ToList()
Else
'**ERROR IS THROWN HERE**
Return cs.OfType(Of CategoryStructure)().Where(predicate).ToList().Union(cs.Cast(Of CategoryStructure).Select(Of List(Of CategoryStructure))(Function(c) c.Children.FindRecursive(predicate, depthLimit - 1)).ToList())
End If
End Function
End Module
End Namespace
However I'm having casting problems when I'm unioning the recursive result with the current list at the point in the code marked. I can see why that's happening, just have no idea how to resolve it. Please do not send me C# examples as there is no 'yield' alternative in VB.net
That's because both sides of UNION have different signatures.
cs.OfType(Of CategoryStructure)().Where(predicate).ToList()
This returns List(Of CategoryStructure).
cs.Cast(Of CategoryStructure).Select(Of List(Of CategoryStructure))(Function(c) c.Children.FindRecursive(predicate, depthLimit - 1)).ToList()
This one returns List(Of List(Of CategoryStructure))
I think what you're looking for is:
Return cs.OfType(Of CategoryStructure)().Where(predicate).Union(cs.Cast(Of CategoryStructure).SelectMany(Function(c) c.Children.FindRecursive(predicate, depthLimit - 1))).ToList()
SelectMany returns flattened collection typed as IEnumerable(Of CategoryStructure).

Do I Need a Class if I only need 1 Property (at the moment)?

Update: I didn't make it clear but I meant this to be a question about where/how I would use a function to return a list of strings when I'm trying to just work with classes.
I have a class called Account.
I have data access class called AccountDAO.
I have various functions that return lists of objects like GetAllAccounts, GetAccountByID etc.
I want to populate a drop down list with just the account names and nothing else. It's proving rather slow when using lists of objects and databinding them to the dropdownlist.
I feel like I should be using a simple "Select Account_Name From blah" type statement and returning a list of strings but I don't know how to work this into my class and data access class.
How should I handle this predicament?
You can use a list of string,s and bind the list of strings to a dropdownlist no problem... the DDL can support that, just leave out DataTextField and DataValueField props, and it will display the account name as is, which that name would be accessible through the ListItem's Text and Value property.
I like to use objects to be consistent with the rest of the app (which other areas might need a class), and if for some reason you want to add AccountKey later, if you use an object, all you need to do is add a property. Otherwise, if using strings, you'd have to switch up the binding later to point to the object.
HTH.
There is nothing wrong by making a function that only returns a list of strings. YOu could however wonder if it's not better to restrict the number of records you want to put in the list and use some kind of paging.
Assuming that you're using a List<>, you can try something like this:
IEnumerable<string> nameList = accountList.Select(t => t.AccountName);
Or if you need a List:
List<string> nameList = accountList.Select(t => t.AccountName).ToList();
Go with your feelings. Use a datareader to select the list and then load them into an arraylist which can then be bound to the dropdown. Alternately, use something like this method I use to provide both a DisplayMember and a ValueMember which uses a class (with both values) as members of the arraylist. This should give you the general idea. (Note: I normally include this code in a data access class (MyBase) where StartReader, _datRdr, ReadNext and_ReaderValid are a members. But the general idea is intact.)
Public Sub LoadDataSource(ByRef PlantDataSource As PlantSource, Optional ByVal Filter As String = "", Optional ByVal IncludeBlankItem As Boolean = False)
PlantDataSource = New PlantSource
If IncludeBlankItem Then
PlantDataSource.Add(0, "")
End If
If Filter = String.Empty Then
Call StartReader(" Order by PlantName")
Else
Call StartReader(String.Concat(" Where ", Filter, " Order by PlantName"))
End If
If _DatRdr.HasRows Then
While MyBase._ReaderValid
PlantDataSource.Add(PlantId, PlantName)
ReadNext()
End While
End If
Call CloseReader()
End Sub
Private Class PlantListing
Private _PlantList As New ArrayList
Public Sub Add(ByVal PlantId As Integer, ByVal PlantName As String)
_PlantList.Add(New PlantDataItem(PlantId, PlantName))
End Sub
Public ReadOnly Property List() As ArrayList
Get
Return _PlantList
End Get
End Property
End Class
Private Class PlantDataItem
Private _PlantId As Integer
Private _PlantName As String
Public Sub New(ByVal pPlantId As Integer, ByVal pPlantName As String)
Me._PlantId = pPlantId
Me._PlantName = pPlantName
End Sub
Public ReadOnly Property PlantName() As String
Get
Return _PlantName
End Get
End Property
Public ReadOnly Property PlantId() As Integer
Get
Return _PlantId
End Get
End Property
Public ReadOnly Property DisplayValue() As String
Get
Return CStr(Me._PlantId).Trim & " - " & _PlantName.Trim
End Get
End Property
Public Overrides Function ToString() As String
Return CStr(Me._PlantId).Trim & " - " & _PlantName.Trim
End Function
End Class

updating properties of a class with dynamic references

Sorry for what is probably a very basic question. I have a vb.net class with properties defined as follows:
Private m_Property1 As String
Public Property Property1() As String
Get
Return m_Property1
End Get
Set(ByVal value As String)
If IsNothing(value) Then
m_Property1 = String.Empty
Else
m_Property1 = value
End If
End Set
End Property
I can then set the values as follows:
classname.Property1 = "myvalue"
How do I set the value of a property that is defined dynmically eg
Dim strPropertyName As String = "Property1"
Hope that makes sense.
Thanks,
Josh
You would use reflection
Dim strPropertyName as string = "Property1"
Dim pi As PropertyInfo = myClass.GetType().GetProperty(strPropertyName)
pi.SetValue(myClass.GetType(), "some string", Nothing)
You want to use Reflection in order to do this. VB.NET provides a way to do this if you know the value at compile-time, but for run-time operations, you need to use the GetType keyword in order to get the type of your class (or, use the GetType method on an instance of it if you don't know it).
Then, with that Type instance, you would call GetProperty, passing the string with the name of the property. It will return an PropertyInfo instance which you then call GetValue on, passing the instance of the object in, which will return an Object which you have to cast back to a type you wish to use (if you are).
VB.NET makes a lot of this easier with the CallByName function.
Also, if you know at compile-time what the name of the property is, you can always cast to object and use VB.NET's inherent late binding:
Dim o As Object = <your object>
o.Property1 = ...
VB.NET will perform the late-binding for you.

Resources