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)
Related
I have this case: I'm using Entity Framework 6 and I'm trying to insert registries into multiple tables like this:
public function method1(model as modelType) as result
dim answer = new result(false)
dim ent = new entitiType
Using context as database1Context
Using dbContextTransaction = context.Database.BeginTransaction()
Try
model.property3 = method2(model.property2, model.property2)
model.property4 = method3()
context.ent.add(model)
contex.savechanges()
answer.ok = true
DbContextTransaction.Commit()
catch ex as Exception
answer.detail = ex.innerException.Message
DbContextTransaction.Rollback()
End Try
End Using
End Using
Return Answer
End Function
This is my method2:
public function method2(idcta as integer) as integer
dim folio As integer
using context as new database1Context
dim values = (From a In context.cuentas
Where a.idcta = idcta Select a).SingleOrDefault()
values.property = values.property +1
context.savechanges()
End Function
(The method3 is identical to method2)
My question is, how can I roll back every insert or update, when any one of these methods fails?
Thank you for your help.
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.
I am using a method of passing Json to and from my markup using jquery and ajax. This can be described in more detail on this page: http://blogs.telerik.com/aspnet-ajax/posts/12-04-27/the-present-and-future-of-using-json-in-webforms.aspx
In this snippet of code, I try to set the object's value dynamically by setting a string variable named "test" to a business object's value:
Dim objOrder As Object = New JsonObject()
For Each Order As BoVendorOrder In Orders
Dim Vendor As New BoVendor(Order.VendorID)
Dim test As String = Order.VendorOrderID
objOrder.test = Vendor.VendorName + " - " + Order.VendorOrderPoNumber
Next
I left out some code for the sake of brevity. The goal is to get the objOrder.test to be equal to the VendorOrderID (a number in our SQL database) so that the JSON looks like this:
{
"123456": "VendorName - PONumber",
"678901": "VendorName - PONumber"
}
Any of you guys out there know how to do this?
Do you really need the order IDs to be properties of the object? It might be easier to just return a serialized Dictionary(Of String, String). You could still look up by order ID and it would be easier to loop over than the props of the Javascript object.
Here's an example of what you'd need to do using the dictionary approach:
Dim OrdersDict as New Dictionary(Of String, String)()
For Each Order as BoVendorOrder In Orders
If Not OrdersDict.ContainsKey(Order.VendorOrderID) Then
OrdersDict.Add(Order.VendorOrderID, Vendor.VendorName + " - " + Order.VendorOrderPoNumber)
End If
Next
' Serialize the dictionary object to JSON
' Using System.Web.Script.Serialization.JavascriptSerializer:
Dim Serializer As New JavaScriptSerializer
If MaxLength Then Serializer.MaxJsonLength = Int32.MaxValue
Dim x as String = Serializer.Serialize(OrdersDict) 'Return or response.write x as needed
'or
'Using JSON.net
Dim x as String = JsonConvert.SerializeObject(OrdersDict) 'Return or response.write x as needed
I know I'm being an idiot here and I just can't work it out. But i'm trying to take some data back from a vb.net database. It's falling over with a Object reference not set to an instance of an object error. And before the code runs it's saying the variable is being used before it's set, but I can't see how. Code:
Private taNotifications As dsDataTableAdapters.NotificationsTableAdapter = New dsDataTableAdapters.NotificationsTableAdapter
Dim notification As dsData.NotificationsDataTable = taNotifications.GetDataByClientID(userrow.UserID)
If notification.Rows.Count > 0 Then
Dim notificationrow As dsData.NotificationsRow
Dim forwardURL As String = notificationrow.ForwardLocation
End If
It falls over on the Dim forwardURL As String = notificationrow.ForwardLocation
The problem is that you have never instantiated the notificationRow inside the if statement. You've declared it, but it doesn't belong to anything. You need to make an assignment or loop through your rows before doing anything with this object:
Dim notificationrow As dsData.NotificationsRow ' this is not instantiated
Dim forwardURL As String = notificationrow.ForwardLocation
What you really want in this case is:
For Each notificationRow As dsData.NotificationRow In notification
Dim forwardURL As String = notificationRow.ForwardLocation
' Do Something
Next
If you only HAVE one row and you know you only have 1 or 0 rows then you could use your if statement by doing:
If notification.Rows.Count > 0 Then
Dim notificationrow As dsData.NotificationsRow = _
CType(notification.Rows(0), dsData.NotificationsRow)
Dim forwardURL As String = notificationrow.ForwardLocation
End If
Edit: In the code above, I originally just had notification.Rows(0). This will produce a DataRow object, but it will not be strongly typed. You need to perform the CType that I added in order to use the custom property ForwardLocation.
You never set notificationrow to anything. Did you mean to set it like this?
Dim notificationrow As dsData.NotificationsRow = CType(notification.Rows(0), dsData.NotificationsRow)
My last question was not clear. Im trying to make a web service in VB.net is their a way that i can return the results that i get from LINQ. ie "return objreturnLINQResults"
I have tryed to set my Public Function GetAlarmsByGUIS(ByVal DeptGUID As String, ByVal IdNumber As String) As Linq.DataContext . i just keep getting errors. help please.
Public Function GetAlarmsByGUIS(ByVal DeptGUID As String, ByVal IdNumber As String) As Linq.DataContext
Dim lqAlarms As New linqAlarmDumpDataContext
Dim Temp As String = ""
Dim n As Integer = 0
Dim GetAlrms = From r In lqAlarms.AlarmDrops _
Where r.DeptGUID = DeptGUID And Not r.AlarmsHandled.Contains(IdNumber) _
Order By r.TimeDate Descending _
Select r
Return GetAlrms
End Function
1) You can't create web service's method which returns DataContext object.Return values and input parameters of Web service methods must be serializable through the XmlSerializer class. DataContext is not serializable
2) The simplest way to avoid errors it is return an array of serializable objects. Like this Return GetAlrms.ToArray();