Stackoverflow exception - Recursive Treeview ASP.Net - asp.net

I am trying to build a treeview recursively using VB.Net this is my code so far
.... Code to get the data table
For Each row As DataRow In dt.Rows
Dim oName As String = Nothing
Dim pId As String = Nothing
Dim cId As String = Nothing
Dim cmts As String = Nothing
Dim lvl As String = Nothing
oName = row(0)
pId = row(4)
cId = row(5)
If Not String.IsNullOrEmpty(row(3).ToString()) Then
cmts = row(3)
End If
lvl = row(2)
list.Add(New MyObject() With { _
.ObjectName = oName,
.ParentId = pId,
.ChildId = cId,
.Comments = cmts,
.level = lvl
})
Next
BindTree(list, Nothing)
End Sub
Private Sub BindTree(list As IEnumerable(Of MyObject), parentNode As TreeNode)
Dim nodes = list.Where(Function(x) If(parentNode Is Nothing, x.ParentId = "[Transform].[(Root)]", x.ParentId = parentNode.Value))
For Each node As MyObject In nodes
Dim newNode As New TreeNode(node.ObjectName, node.ParentId.ToString())
If parentNode Is Nothing Then
TreeView1.Nodes.Add(newNode)
Else
parentNode.ChildNodes.Add(newNode)
End If
BindTree(list, newNode)
Next
End Sub
also the new class
Public Class MyObject
Public ObjectName As String
Public ParentId As String
Public ChildId As String
Public Comments As String
Public level As Integer
End Class
The issue I am having is that when this goes so far through the recursion I get a System.StackOverFlowException. When looking at the exception snapshot every thing says "unable to evaluate expression" The error is coming from this line
Dim nodes = list.Where(Function(x) If(parentNode Is Nothing, x.ParentId = "[Transform].[(Root)]", x.ParentId = parentNode.Value)) but Ive no idea why or how to resolve it.
Any help would be very much appreciated.
Thanks
Simon

A StackOverflowException isn't thrown by a particular line of code, that's just the first line of code in the method being called which is overflowing the stack. And there isn't any more information that can really be given, because no further code can be executed. (Because the stack is full.)
Your method is recursive:
Private Sub BindTree(list As IEnumerable(Of MyObject), parentNode As TreeNode)
' ...
BindTree(list, newNode)
End Sub
Which is ok, except that you're not modifying the list variable anywhere. So every time you call the method, you call it with the same list. Therefore it will continue to perform the same logic, recursively, indefinitely, with no terminating condition.
Generally, you should think of a recursive method with the following structure (in VB-ish pseudo-code):
Method(ByVal something As SomeType)
' Check if the recursion should end
If SomeTerminatingCondition Then
Return
End If
' Perform the logic for this step of the recursion
DoSomething()
' Recurse again
Method(somethingModified)
End Method
It's not entirely clear to me from the code how list should be modified. But presumably when you call BindTree(list, newNode) at that point list should be some subset of the original list.

Related

update currently message by inline keyboard Telegram VB.NET

hello i try to updating Message by inline keyboard.
telegram OnMessageReceived function:
Dim ID As String = e.Message.From.Id.ToString ' it is your id
Select Case e.Message.Text
Case "/ts"
Await bot.SendTextMessageAsync(ID, "Hello", replyMarkup:=CreateInLineMainMenuMarkup)
Return
End Select
Inline Keyboard function's
Public Shared Function CreateInLineMainMenuMarkup() As IReplyMarkup
Dim buttonsList As Dictionary(Of String, String) = New Dictionary(Of String, String)()
buttonsList.Add("Refresh", "test")
Return CreateInlineKeyboardButton(buttonsList, 1)
End Function
Public Shared Function CreateInlineKeyboardButton(ByVal buttonList As Dictionary(Of String, String), ByVal columns As Integer) As IReplyMarkup
Dim rows As Integer = CInt(Math.Ceiling(CDbl(buttonList.Count) / CDbl(columns)))
Dim buttons As InlineKeyboardButton()() = New InlineKeyboardButton(rows - 1)() {}
For i As Integer = 0 To buttons.Length - 1
buttons(i) = buttonList.Skip(i * columns).Take(columns).[Select](Function(direction) TryCast(New InlineKeyboardCallbackButton(direction.Key, direction.Value), InlineKeyboardCallbackButton)).ToArray()
Next
Return New InlineKeyboardMarkup(buttons)
End Function
Image To explain
Question here,can i update text message from "Hello", To be "Done". using inline keyboard click.
you will need handle OnCallbackQuery event.
first :
AddHandler bot.OnCallbackQuery, AddressOf OnCallbackQuery
then add function OnCallbackQuery (that to handle any action of callback inline keyboard)
Private Async Sub OnCallbackQuery(sender As Object, e As CallbackQueryEventArgs)
Dim call_data As String = e.CallbackQuery().Message.Text
Dim message_id As Long = e.CallbackQuery().Message.MessageId
Dim chat_id As Long = e.CallbackQuery().Message.Chat.Id
Dim callback As Long = e.CallbackQuery.Id
If call_data.Equals("Hello") Then
Dim answer As String = "Done"
Dim r As Telegram.Bot.Types.Message = Await bot.EditMessageTextAsync(chat_id, message_id, (answer))
Dim r2 As Boolean = Await bot.AnswerCallbackQueryAsync(callbackQueryId:=callback, text:=answer)
End If
End Sub
Notice that, it gonna update inline keyboard message to normal text message.
Hope it help.

Access VB property based on name as string - Fastest Option

I'm developing an ASP.NET MVC web app in VB and I am required to output a set of data to a table format, and to allow the user to configure the order and presence of columns from an available set. The data set is stored as a list of the object type representing the row model.
Currently, I implement this using CallByName. Iterating over an ordered list of property names and outputting the value from the instance of the row model. However, based on testing this seems to be a major bottleneck in the process.
I've seen a recommendation to store delegates to get the property, against the string representation of the property's name. So, I can presumably do something like this:
Public Delegate Function GetColumn(ByRef RowObj As RowModel) As String
Dim GetPropOne As GetColumn = Function(ByRef RowObj As RowModel) RowObj.Prop1.ToString()
Dim accessors As New Hashtable()
accessors.Add("Prop1", GetPropOne)
Then, loop through and do something like this:
Dim acc As GetColumn = accessors(ColumnName)
Dim val As String = acc.Invoke(currentRow)
It looks faster, but it also looks like more maintenance. If this is indeed faster, is there a way I can dynamically build something like this? I'm thinking:
Public Delegate Function GetObjectProperty(Instance As Object) As Object
For Each prop In GetType(RowModel).GetProperties()
Dim acc As GetObjectProperty = AddressOf prop.GetValue
columns.Add(prop.Name, acc)
Next
Dim getColVal As GetObjectProperty = columns(ColumnName)
Dim val As String = getColVal.Invoke(currentRow).ToString()
Open to suggestions for different approaches.
I do a similar thing to turn a SOAP response into a Data Table
Public Function ObjectToDataSource(objName) As DataSet
Dim CollName = ""
Dim ds As New DataSet()
For Each m As System.Reflection.PropertyInfo In objName.GetType().GetProperties()
If m.CanRead Then
If InStr(m.PropertyType.ToString, "[]") <> 0 Then
CollName = m.Name
Exit For
End If
End If
Next
Dim CollObj
CollObj = CallByName(objName, CollName, CallType.Get)
If CollObj.length = 0 Then
Call EndTask("No Supply Chains to display", "Royal Mail failed to return Supply Chain information for these credentials", 3)
Else
Dim dt_NewTable As New DataTable(CollName)
ds.Tables.Add(dt_NewTable)
Dim ColumnCount = 0
For Each p As System.Reflection.PropertyInfo In CollObj(0).GetType().GetProperties()
If p.CanRead Then
If p.Name <> "ExtensionData" Then
dt_NewTable.Columns.Add(p.Name, p.PropertyType)
ColumnCount = ColumnCount + 1
End If
End If
Next
Dim rowcount = CollObj.Length - 1
For r = 0 To rowcount
Dim rowdata(ColumnCount - 1) As Object
For c = 0 To ColumnCount - 1
rowdata(c) = CallByName(CollObj(r), dt_NewTable.Columns.Item(c).ToString, CallType.Get)
Next
dt_NewTable.Rows.Add(rowdata)
rowdata = Nothing
Next
End If
Return ds
End Function
This is specific to my needs in terms of getting CollName and not requiring ExtensionData
If ColumnName is the same name as one of the RowModel's properties I don't see why you need the long workaround with delegates...
An extension method which gets only the property you want right now is both faster and consumes less memory.
Imports System.Runtime.CompilerServices
Public Module Extensions
<Extension()> _
Public Function GetProperty(ByVal Instance As Object, ByVal PropertyName As String, Optional ByVal Arguments As Object() = Nothing) As Object
Return Instance.GetType().GetProperty(PropertyName).GetValue(Instance, Arguments)
End Function
End Module
Example usage:
currentRow.GetProperty("Prop1")
'or:
currentRow.GetProperty(ColumnName)

Unable to cast object of type 'VB$StateMachine_4_GetAllFrontPageBanners' to type IEnumerable(Of FrontPageBanner)

I'm not really used to working with VB.NET, but i've come across an error where googling did not suffice. I've created this data access class, it has a method that makes use of Yield and vb.net's object initializer shortcut
Public Class FMACMarketingRepository
Private cvConnection As SqlConnection
Private cvCommand As SqlCommand
Private cvReader As SqlDataReader
Public Sub New(Role As InfotelLibrary.Data.DatabaseRole)
cvConnection = New SqlConnection(InfotelLibrary.Data.ConnectionString(Role, InfotelLibrary.Data.DatabaseName.Mongoose))
End Sub
Public Iterator Function GetAllFrontPageBanners() As IEnumerable(Of FrontPageBanner)
Using dbConnection As IDbConnection = cvConnection
Using cmd As IDbCommand = dbConnection.CreateCommand()
cmd.CommandType = CommandType.StoredProcedure
cmd.CommandText = "sel_AllFmacFrontPageBanners"
Using reader As IDataReader = cmd.ExecuteReader()
If reader Is Nothing Then
Yield Nothing
End If
While reader.Read()
Yield New FrontPageBanner() With
{
.Banner_Id = CType(reader("Banner_ID"), Integer),
.Geo_Id = CType(reader("Geo_ID"), Long),
.Title = CType(reader("Title"), String),
.Description = CType(reader("Description"), String),
.Link = CType(reader("Link"), String),
.Image = CType(reader("Image"), Byte()),
.SpecialOffer = CType(reader("Special_Offer"), Boolean)
}
End While
End Using
End Using
End Using
End Function
End Class
There are 0 errors in Intellisense, it builds but when i run the webpage it get the error
System.InvalidCastException: Unable to cast object of type 'VB$StateMachine_4_GetAllFrontPageBanners' to type 'System.Collections.Generic.List`1[InfotelData.Mongoose.Data.FrontPageBanner]'.
Line 7:
Line 8: Protected Sub Page_Load(ByVal sender As Object, e As EventArgs) Handles Me.Load
Line 9: Dim banners As List(Of FrontPageBanner) = cvRepo.GetAllFrontPageBanners()
Line 10: If banners.Count() > 0 Then
Line 11: rptUploadedBanners.DataSource = banners
Debugging just gives the same error when it hits page_load.
I've the distinct feeling that user error is to blame.
GetAllFrontPageBanners returns an IEnumerable(Of FrontPageBanner)(*) and you're trying to store it inside a List(Of FrontPageBanner). I'm surprised it doesn't give a compile time error (you're probably in Option Strict Off mode)
you need to make a List from the enumerable using .ToList for example :
Dim banners As List(Of FrontBanner) = cvRepo.GetAllFrontPageBanners.ToList
(*) Internally an Iterator block transform the function into a generated "class-state-machine" (which implement IEnumerable(Of FrontBanner) in your case).
That's the odd name you got but you can (and should) consider it like the return type given in your source code.
More information about that here

Converting an iteration to Async

I am returning a large number of DataTable rows, iterating them and pushing the values of each row to a webservice, which then returns a response code (string). Should any errors occur in the webservice, the whole process is stopped and an error shown:
Protected Sub DoStuff(sender As Object, e As EventArgs) Handles lnk_orderCards.Click
Dim dt as DataTable = GetDataBaseData()
For each dr as DataRow in dt.Rows()
Dim f as String = dr.Item("firstname").ToString()
Dim m as String = dr.Item("middleName").ToString()
Dim s as String = dr.Item("surname").ToString()
Dim err as String = String.Empty
Dim result as String = XYZService.DoIt(f, m, s)
Select Case result
Case "ok"
' OK - allow For Loop Next '
Case "e"
err = "Some error"
Case "e2"
err = "Another error"
End Select
If Not String.IsNullOrWhiteSpace(err) Then
ShowError(err)
Exit Sub
End If
Next
XYZService.Complete()
ltl_status.Text = "Success!"
End Sub
I assumed that the above would be a good candidate for an Async approach, especially if the datatable has 1000 rows, as each webservice request could be sent in parallel. However, from MSDN I can't find enough examples on how best to implement Async.
Can anyone recommend a more thorough approach? I have read on MSDN about Task.WaitAll and Task.Factory.StartNew but the examples aren't straightforward. If Task.WaitAll was utilised like this, how do you stop the flow if one (or more) tasks fail?
It's important that all tasks much return success before XYZService.Complete() is called.
Final code based on Stephen's input
Protected Async Sub DoStuff(sender As Object, e As EventArgs) Handles lnk_orderCards.Click
Dim cts As New CancellationTokenSource
Dim dt As DataTable = GetDataBaseData()
Dim rows As IEnumerable(Of Task(Of String)) = (From dr As DataRow In dt.Rows Select DoServiceCall(cts, dr))
Dim results() As String = Await Task.WhenAll(rows)
Dim errors As List(Of String) = (From s As String In results Where s <> String.Empty).ToList()
If errors.Count > 0 Then
ShowError(String.Join("<br/>", errors))
Exit Sub
Else
Console.WriteLine("Success")
End If
End Sub
Protected Async Function DoServiceCall(t As CancellationTokenSource, dr As DataRow) As Task(Of String)
If t.IsCancellationRequested Then
t.Token.ThrowIfCancellationRequested()
End If
Dim f As String = dr.Item("firstname").ToString()
Dim m As String = dr.Item("middleName").ToString()
Dim s As String = dr.Item("surname").ToString()
Dim returnResult As XYZService.ServiceReturnResult = Await XYZService.DoItAsync(f, s, s)
Select Case returnResult.return
Case "ok"
' OK - allow For Loop Next '
Case Else
t.Cancel(False)
Throw New Exception("Web service error: " & returnResult.return)
End Select
Return returnResult.return
End Function
The simplest solution is to use Task.WhenAll. Specifically, you can project (LINQ's Select) each item into a Task (of T) and then do an Await Task.WhenAll.
However, that simple approach will execute all requests concurrently, and will not stop other requests if one of them failed. That's a bit more complicated; I recommend using a CancellationTokenSource to represent the "emergency stop", with each request receiving the CancellationToken and cancelling the source if it fails.
Do not use Task.Run or its even-worse cousin StartNew. Since your operation is asynchronous by nature (an I/O-bound network request), you want asynchronous concurrency (Task.WhenAll), not parallel concurrency (StartNew).

Assigning value to item within an arraylist by index

I have this VB.NET ArrayList object which is working quite well. I have built it like this. It's the first one I have used.
Public Class MyObj
Private _str1 As String
Private _str2 As String
Public Property Str1() As String
Get
Return _str1
End Get
Set(ByVal value As String)
_str1 = value
End Set
End Property
Public Property Str2() As String
Get
Return _str2
End Get
Set(ByVal value As String)
_str2 = value
End Set
End Property
Public Sub New(ByRef pStr1 As String)
_str1 = pStr1
End Sub
End Class
Then I initiialise it doing this...
Dim MyObj1 As ArrayList = New ArrayList()
MyObj1.Add(New MyObj("myTestString"))
So this is all working later on. So I pack up the arraylist and store it in a class level variable. Then in a different method I grab my arraylist. I then want to assign a value to _str2. Does anyone have advice on how I would go about this. I keep trying to know avail. This is the sort of thing I mean.
For i = 0 To MyObj1.Items.Count - 1
MyObj1.Item(i)("Str2") = "tesstring2"
Next
As Tim wrote, you will be better off using a List instead of the old ArrayList.
To set it up and then access the property Str2 of the instances of MyObj:
Dim myList As New List(Of MyObj)
myList.Add(New MyObj("Hello"))
For i = 0 To myList.Count - 1
myList(i).Str2 = "World"
Next
Notice how it is .Str2, not ("Str2").

Resources