How can I get a sub-class name in Visual Basic - vb.net

I have a Visual Basic method called LogException which writes information into my Exceptions database in the event of a TRY..CATCH failure. That method has the following parameters:
methodLocation;
methodName;
exception;
When I invoke the method, I would use the following code:
_ex.LogException(
Me.GetType.Name.ToString,
MB.GetCurrentMethod.Name.ToString,
ex.Message.ToString)
Therefore, if I was invoking this code in a method called "Insert_Test" within a class called "Test", I would expect the first parameter to receive "Test", the second to receive "Insert_Test" and the third to receive the exact details from the exception that was thrown.
This all works fine as long as the "Test" class is the base class. If the "Test" class is a sub-class (for example called "BigTest"), the first two parameters would still be passed as "Test" and "Insert_Test". What I need to know is how to get the exact class tree, so that the first parameter in this scenario would come through as "BigTest.Test".
Ideally I'd like to be able to do this without having to hard-code any values into my code, so that the code can be re-used "as-is".

You could use a function like this:
Public Function GetFullType(ByVal type As Type) As String
Dim fullType As String = ""
While type IsNot GetType(Object)
If fullType = "" Then
fullType &= type.Name
Else
fullType = type.Name & "." & fullType
End If
type = type.BaseType
End While
Return fullType
End Function
And call it like this:
GetFullType(Me.GetType)
EDIT: It appears as though the OP is actually using nested classes, not inherited classes. In such case I found this answer which should be able to tweak into the code provided.
Code for nested classes:
Shared Function GetFullType(ByVal type As Type) As String
Dim fullType As String = ""
While type IsNot Nothing
If fullType = "" Then
fullType &= type.Name
Else
fullType = type.Name & "." & fullType
End If
type = type.DeclaringType
End While
Return fullType
End Function

If possible, don't invent it by yourself. For example, I can just guess that MB.GetCurrentMethod() will read the stacktrace to determine the method name (which is slow!).
You should check if the attributes CallerMemberName. CallerFilePath & CallerLineNumber fulfill your needs. They are filled in by the compiler and therefore won't hit any performance issues.
See:
https://blog.codeinside.eu/2013/11/03/caller-information-with-net-4-5-or-who-touched-the-function/

Related

VB SQL CommandText property has not been initialized

I'm new to working with background workers, and I'm trying to run the following code. But I recieve a run-time error on the m._Value_CreatedDate = m._MyCMD.ExecuteScalar() line. The error is:
Additional information: ExecuteScalar: CommandText property has not
been initialized
Try
Dim m As MyParameters = DirectCast(e.Argument, MyParameters)
m._Con.Open()
m._QueryStr = "SELECT TOP 1 CONVERT(varchar(10),aCreated,103) FROM Account WHERE aMember_ID = " & m._Selected_MemberID & ""
m._MyCMD.CommandType = CommandType.Text
m._Value_CreatedDate = m._MyCMD.ExecuteScalar()
Catch ex As Exception
m._Value_CreatedDate = "N/A"
End Try
Here's the Parameter's I'm using:
Class MyParameters
Friend _QueryStr As String
Friend _Value_CreatedDate As Object
Friend _AccountID As Object
Friend _Selected_MemberID As String = Committee_Database.GridView1.GetFocusedRowCellValue("Unique ID").ToString
Friend _Con As New SqlConnection('Connection string ommitted)
Friend _MyCMD As New SqlCommand(_QueryStr, _Con)
End Class
Please forgive me if I'm doing something extremely wrong, I'm self taught and experimenting with the backgroundworker. It's worth noting that _QueryStr will change multiple times as the background worker runs multiple queries against the same database and (as I understand it) stores each of the returned values from the queries into variables - _Value_CreatedDate is the variable I am using in this code. I've included an example of how I'm recycling the _QueryStr variable below and storing the returned value into a diffent Variable each time.
Try
m._QueryStr = "SELECT TOP 1 aAccount_ID FROM Account WHERE aUser_Name='" & _Selected_AccountID & "'"
m._MyCMD.CommandType = CommandType.Text
m._AccountID = m._MyCMD.ExecuteScalar()
Catch ex As Exception
End Try
Am I doing something wrong?
In the implementation of your class MyParameters you initialize the SqlCommand directly with the declaration using the value of the variable _QueryStr. At that point in time the variable _QueryStr is not yet initialized, so it is an empty string.
After the initialization of an instance of MyParameters, you change the value of _QueryStr (many times according to you) but these changes are not automatically passed to the CommandText of your SqlCommand. It still contains the empty initial value.
You could fix this problem building appropriate getter and setter for a new QueryStr property. When someone tries to set the property the code below change the value of the internal field _QueryStr (now private) and reinitializes the CommandText property of your SqlCommand.
Class MyParameters
Private _QueryStr As String
Public Property QueryStr() As String
Get
Return _QueryStr
End Get
Set(ByVal value As String)
_QueryStr = value
_MyCMD.CommandText = _QueryStr
End Set
End Property
..... other properties
Friend _MyCMD As New SqlCommand(_QueryStr, _Con)
End Class
Now when you write
Try
m.QueryStr = "SELECT ...."
....
the new command text is correctly assigned to your command.
As a side note: I suggest to use plain ADO.NET objects (or learn how to use an ORM tool). Do not try to encapsulate them in custom classes unless you have a very deep understanding on how these ADO.NET objects works. You gain nothing and expose your code to many problems. For example your code is very easy to exploit with Sql Injection because the MyParameters class has no provision to use a parameterized query. Your code has no method to close and dispose the SqlConnection embedded in your class thus leading to resource leaks.

Alternatives to using a Collection class

I have been looking through old code to get familiar with the system I use and found a piece of code that I feel can be used better.
What goes on here is some data gets added to the collection(around 150 string variables, some with two variables(variableName/VariableValue), most with only one(VariableName)). It will try to set a module level string variable to the item of the collection passing it the index(variableName) then if there's a value setting the VariableVAlue to the module level variable.
What I feel needs work is that if the collection is passed a variable and the variable doesn't have a value it will return a "" which would cause a runtime error hence there's a On Error GoTo Handler code to manually add a "" to the collection. I feel there's a better way to do this rather than knowing there will be a runtime issue then solving it after catching it. Would there be a way to have a return "" not throw an exception or would the use of an Array also work here since it's a "collection" as well?
Here's an example to try to help visualize:
Public Function GetCollectionVariable(ByVal varName as string) as String
If collection1 Is Nothing Then
m_collection1 = New Collection
End If
On Error GoTo Handler
GetCollectionVariable = collection1.Item(VarName)
exit function
Handler:
collection1.add("", VarName)
GetCollectionVariable = ""
End FUnction
Thanks for your time!!
If Collection1 is a dictionary, you can use TryGetValue.

Write a variable to a file that has a different type than the function assigned to the variable

I have the following code that I am using to parse out a test file. I am getting variable conversion error in Sub Main() when I assign file = Read(). The return value of Read() is a TextFieldParser type. How do I assign the proper variable type to "file" so I can write the output to a text file?
Thanks!
Module Module1
Function Read()
Using MyReader As New FileIO.TextFieldParser("C:\Users\Colin\Desktop\Parse_Me.txt")
Dim currentRow As String
While Not MyReader.EndOfData
Try
currentRow = MyReader.ReadLine()
Console.WriteLine(Parse_me(currentRow))
Catch ex As FileIO.MalformedLineException
MsgBox("Line " & ex.Message &
" is invalid. Skipping")
End Try
End While
Return MyReader
MyReader.Close()
End Using
End Function
Function Parse_me(ByVal test As String)
Dim Set_1, Set_2, Set_3, Set_4, Set_5 As String
Dim new_string As String
Set_1 = test.Substring(0, 4)
Set_2 = test.Substring(7, 2)
Set_3 = test.Substring(11, 1)
Set_4 = test.Substring(14, 4)
Set_5 = test.Substring(20, 4)
new_string = Set_1 & " " & Set_2 & " " & Set_3 & " " & Set_4 & " " & Set_5
Return new_string
End Function
Sub Main()
Dim file As Object
file = Read()
FilePutObject("C:\Users\Colin\Desktop\Parse_Meoutput.txt", file)
End Sub
End Module
Here's how FilePutObject is supposed to work (example taken from MSDN documentation for FilePutObject):
Sub WriteData()
Dim text As String = "test"
FileOpen(1, "test.bin", OpenMode.Binary)
FilePutObject(1, text)
FileClose(1)
End Sub
The 1 act as an identifier for the file. Note also that the file name is passed to FileOpen before calling FilePutObject, and that FileClose is called afterwards. Also note that a string is being written to the file. I don't know which types of data are valid for being passed to FilePutObject, but FileIO.TextFieldParser is definitely not one of them (I just tried it).
Correct me if I'm wrong, but I'm pretty sure that FilePutObject is one of those carry-overs from VB6. If you're writing new code, I would rather use a Stream object for my I/O. For one, it's a lot more .Net-ish (i.e., type-safe, object-oriented, etc). And as far as usability goes, it's a lot clearer how a Stream works, not to mention it doesn't involve passing arbitrary integers as handles to functions in order to identify which file you'd like to work with. And to top it all off, a Stream works whether you want to write to a file, to the console, or send the data to another machine. To sum up, I would definitely look up the Stream class, some of its child classes (like FileStream, and whatever else appeals to you), and some associated types (such as the TextWriter class for conveniently writing text).
Change the definition of the function "read" to:
Function Read() as FileIO.TextFieldParser
and change the declaration of "file" in sub main to:
Dim file as FileIO.TextFieldParser
That way the data type of the function and assignment match.

Easier way to define properties in classic ASP?

For every property in my class in ASP code I have to use this:
Public Property Get ItemsOnPage()
ItemsOnPage = m_ItemsOnPage
end Property
Public Property Let ItemsOnPage(inp)
m_ItemsOnPage = inp
End Property
This example is for the ItemsOnPage property. Is there any other way that I could use a subroutine somehow? I tried using:
sub subClassProperty(varProperty)
execute("Public Property Get " & varProperty & "()")
execute(varProperty & " = m_" & varProperty)
execute("end Property")
execute("Public Property Let " & varProperty & "(inp)")
execute("m_" & varProperty & " = inp")
execute("End Property")
end sub
but this sub I can not call from Class :-((
I believe you could use a public statement, which is a bit simpler syntax.
http://msdn.microsoft.com/en-us/library/72bd95z8%28v=VS.85%29.aspx
This illustrates a simple example
https://web.archive.org/web/20210506183450/http://www.4guysfromrolla.com/webtech/092399-1.2.shtml
VBScript is not a dynamic language in that sense. You can not modify the class at runtime.
I would suggest that instead of using properties, you use some common methods like SetProperty("propertyName", value) and GetProperty("propertyName) in the script that uses this class.
The internal mapping between the "propertyName" and value is up to you. If it's relatively small number of properties, you can use just a 2 dimensional array, or 2 arrays.

XmlTextWriter responds with System.InvalidOperationException during creation of second element

I am having trouble with XMLTextWriter.WriteStartElement throwing an exception:
System.InvalidOperationException
when trying to write the second element in my XML document. This error comes back as "The Writer is closed". I have not closed it, so I am guessing it has fallen out of scope?? I have created a class to write an XML file using XMLTextWriter as an object within my class. Below is the relevant code. I have found one other post on codeguru that was never answered with the exact same issue. Any ideas for workarounds or otherwise would be appreciated.
Function CreateXML()...
Try
_listDocument = New XmlTextWriter(_xmlDI.FullName & "\\" & currentFilename, Nothing)
CreateHeader()
AddTimeDateNode()
CreateXML = True
Catch xmlErr As XmlException
MsgBox("Unable to create temporary file(" & currentFilename & ") that is used to change your whitelist or blacklist. " & _
"More technical information: " & xmlErr.Message, MsgBoxStyle.Critical, "Can't Continue")
End Try
End Function
Function AddListMember(ByVal listType As String, ByVal listItem As String, ByVal action As String) As Boolean
_listDocument.WriteStartElement(listItem) <-- CODE THROWS EXCEPTION HERE!
_listDocument.WriteAttributeString("listType", listType)
_listDocument.WriteAttributeString("action", action)
_listDocument.WriteString(listItem)
_listDocument.WriteEndElement()
_listDocument.WriteWhitespace(Chr(13) & Chr(10) & "\t")
Return True
End Function
'Sets the XML header
Private Function CreateHeader() As Boolean
_listDocument.WriteStartDocument(False)
_listDocument.WriteWhitespace(Chr(13) & Chr(10))
Return True
End Function
'Add Time Date node
Private Function AddTimeDateNode() As Boolean
_listDocument.WriteStartElement("DateTimeAdded")
_listDocument.WriteString(DateTime.Now.ToString)
_listDocument.WriteEndElement()
_listDocument.WriteWhitespace(Chr(13) & Chr(10))
Return True
End Function
I am calling these functions after instantiating a dimension from ListXML (the name of my class) with the following code:
Dim xmloutput As New ListXML
xmloutput.CreateXML()
xmloutput.AddListMember(xmloutput.ReturnWhiteList, currentItem.SenderEmailAddress, xmloutput.ReturnAddAction)
As far as I can tell, it looks like you're trying to create more than one root element - one for DateTimeAdded and one for your list member.
If you call WriteStartElement in CreateXml() you'd end up with valid XML. You'll need to end that element before you end the document of course.
(And yes, the codeguru post looks like it's trying to do the same thing.)
Basically, this is a valid XML document:
<RootElement>
<FirstElement>
Content
</FirstElement>
<SecondElement>
Content
</SecondElement>
</RootElement>
But this is not:
<FirstElement>
Content
</FirstElement>
<SecondElement>
Content
</SecondElement>
You were trying to do the latter, hence the problem.