Ok, I'me getting real dumb with this one....
I have this class:
Public Class whatever
Public id as string
Public name as string
public date as string
end class
Wich I use with this code:
dim personlist as new arraylist
dim person as new whatever
person.id="1"
person.name="bozo"
person.date="6-6-6"
personlist.add(person)
and then i repeat so that i can fill my arraylist with all the information that I want to show in my gridview.
The problem is this:
gridview1.datasource = personlist
gridview1.databind()
When executing, I get an error saying :
The data source for GridView with id 'gdpersonlist' did not have any properties or attributes from which to generate columns. Ensure that your data source has content.
Can anyone help me or perhaps point me in the right direction to do this?!
Try using properties instead of fields. The data binding for the grid view won't work for fields.
Public Class whatever
Public _id as string
Public name as string
public date as string
Public Property Id As String
Get
Return _id
End Get
Set (value as String )
_id = value
End Set
End Property
' repeat for all 3 fields
end class
Related
I have a knockoutjs web page doing a post to a VB .net Web API. for some reason my collection always shows nothing on the controller.
on the web page in the post here is my data.
data: ko.toJSON({
ShpmntCntrlNbr: self.ShpmntCntrlNbr,
MstrBillNbr: self.MstrBillNbr,
accesorialChangesHaveBeenMade: self.accesorialChangesHaveBeenMade,
DB2ACTCollection: actCollection
}),
this shows in the console (chrome developer tools) like this.
{
"ShpmntCntrlNbr":"11019813",
"MstrBillNbr":" ",
"accesorialChangesHaveBeenMade":true,
"DB2ACTCollection":[
{"ShpmntCntrlNbr":"11019813"}
]
}
here is .net VB controller
Public Function Post(data As VMFreightReleaseSaveDB2ChangesInput) As IHttpActionResult
Return Ok()
End Function
unfortunately when I put a stop sign in here and inspect data. I get
DB2ACTCollection Nothing
MstrBillNbr " "
ShpmntCntrlNbr "1101983"
accesorialChangesHaveBeenMade True
no clue why my DBACTCollection is showing as nothing. any thoughts?
and here are the classes.
Public Class VMFreightReleaseSaveDB2ChangesInput
Public Property ShpmntCntrlNbr() As String
Get
Return m_ShpmntCntrlNbr
End Get
Set(value As String)
m_ShpmntCntrlNbr = value
End Set
End Property
Private m_ShpmntCntrlNbr As String
Public Property MstrBillNbr() As String
Get
Return m_MstrBillNbr
End Get
Set(value As String)
m_MstrBillNbr = value
End Set
End Property
Private m_MstrBillNbr As String
Public Property accesorialChangesHaveBeenMade() As Boolean
Get
Return m_accesorialChangesHaveBeenMade
End Get
Set(value As Boolean)
m_accesorialChangesHaveBeenMade = value
End Set
End Property
Private m_accesorialChangesHaveBeenMade
Public Property DB2ACTCollection() As List(Of DB2_ACT2)
Get
Return m_DB2ACTCollection
End Get
Set(value As List(Of DB2_ACT2))
value = m_DB2ACTCollection
End Set
End Property
Private m_DB2ACTCollection As List(Of DB2_ACT2)
and
Public Class DB2_ACT2
Public Property ShpmntCntrlNbr() As String
Get
Return m_ShpmntCntrlNbr
End Get
Set(value As String)
m_ShpmntCntrlNbr = value
End Set
End Property
Private m_ShpmntCntrlNbr As String
End Class
Just a note here is my solution so far, not real happy with it but it seems to be working. instead of data As VMFreightReleaseSaveDB2ChangesInput I have data as JToken. then I just loop through it although I am still at a loss why the native .net web api serializer is not doing this.
Dim inner As JArray = data("DB2ACTCollection")
Dim ACTCollection = New List(Of DB2_ACT)
For Each item As JObject In inner
Dim ACT As New DB2_ACT
ACT.ShpmntCntrlNbr = item("ShpmntCntrlNbr")
ACTCollection.Add(ACT)
Next
Based on the JSON and seeing as the models are POCOs try using auto properties with DB2ACTCollection as an array and see if that deserializes properly
Public Class DB2_ACT2
Public Property ShpmntCntrlNbr As String
End Class
Public Class VMFreightReleaseSaveDB2ChangesInput
Public Property ShpmntCntrlNbr As String
Public Property MstrBillNbr As String
Public Property accesorialChangesHaveBeenMade As Boolean
Public Property DB2ACTCollection As DB2_ACT2()
End Class
I have a WEB Method in my ASPX page that retrieves a List from SQL DB using Entity Framework.
Using rep As New RBZPOSEntities
q = rep.getSalesByLocation(fDate, tDate).ToList
End Using
Thereafter i use javascriptserializer to convert this list into a JSON string
Dim jss As JavaScriptSerializer = New JavaScriptSerializer()
Dim json As String = jss.Serialize(q)
So the above works great and i can use AJAX on the client side to display the results successfully.
The problem i am having now is converting a Flat List into a Nested JSON string.
So consider a list like:
locationName as string
MonthName as string
totalAmount as string
Which needs to be converted into a JSON like this:
[{locationName:'Tokyo',totalAmount:[100,200,300,400]},
{locationName:'New York',totalAmount:[500,600,700,800]}]
So the totalAmount values in the above case correspond to totalAmounts for a Location for a specific month. E.g. Tokyo total amount in January is 100, in February is 200 and etc.
What i can do:
I can create a nested list and populate it with results from EF and then serialize to JSON.
What i am asking:
Is there any other cleaner way to do it.
Thank you
As Todd has already said you need to convert your flat list into a list of another intermediate type. Let's call your existing Type the "InputType" and the Type you want the "OutputType"
Public Class InputClass
Public locationName as String
Public MonthName as String
Public totalAmount as String
End Class
Public Class OutputClass
Public LocationName As String
Public TotalAmount As List(Of String)
Public Sub New(groupedInputClass As IGrouping(Of String, InputClass))
LocationName = groupedInputClass.Key
TotalAmount = groupedInputClass.Select(Function(x) x.totalAmount).ToList()
End Sub
End Class
Then all you need to do is transform a List of InputType into a List of OutputType which is really easy with LINQ.
Public Class MappingHelper
Public Function Map (inputList as List(Of InputClass)) As List(Of OutputClass)
Dim groupedByLocation = inputList.GroupBy(Function(k) k.LocationName)
Dim nestedLocations = groupedByLocation.Select(Function(x) new OutputClass(x)).ToList()
Return nestedLocations
End Function
End Class
If you use an intermediate type, it may be helpful. Something like:
class RbzposInfo {
public string Location {get;set;}
public List<MonthlyRevenue> Monthlies {get;set;}
}
class MonthlyRevenue {
public string Month {get;set;}
public int Revenue {get;set;}
}
var result = new List<RbzposInfo>();
foreach (var r in q) { // q from your code
var info = new RbzposInfo();
// TODO: put r values into info.
result.Add(info);
}
Dim json As String = jss.Serialize(result);
(PS, sorry I mixed .NET languages on you. My code is C#.)
I am currently working on the return class. The problem is I want to show the certain member only when some of the condition meet. Below is my code. I only want to show ResponseMsg member when the ResponseCode is 99 otherwise it will be hidden.
Public Class LoginResponse
Public Property TerminalID As String
Public Property ReaderID As String
Public Property TransRef As String
Public Property TransDateTime As String
Public Property Timeout As Integer
Public Property ResponseCode As String
Public Property ResponseMsg As String
Public Property Cryptogram As String
End Class
You can't that I know of. But you can do something like this:
Public Property ResponseMsg
Get
If ResponseCode <> SomeCodeValue
Return _responseCode
Else
Return Nothing
End if
End Get
End Property
You might want to think about making a specialized class.
Let's say you have your basic LoginResponse
Public Class LoginResponse
Public Property TerminalID As String
Public Property ReaderID As String
Public Property TransRef As String
Public Property TransDateTime As String
Public Property Timeout As Integer
Public Property ResponseCode As String
' Note: no ResponseMsg here
Public Property Cryptogram As String
End Class
Then you'd have an extended response class inheriting your basic LoginResponse:
Public Class LoginResponseEx : Inherits LoginResponse
Public Property ResponseMsg As String
End Class
Then where ever you create those LoginResponse objects, you just create one of the apropriate.
Let's say you have a GetResponse() procedure like:
Public Function GetResponse() As LoginResponse
Dim result As LoginResponse = Nothing
Dim code As Integer = GetSomeCode()
' ... get the other properties
' Say you have a const or something with the appropriate code: SPECIAL_CODE
If code = SPECIAL_CODE Then
Dim msg As String = GetSomeMessage()
result = New LoginResponseEx(..., code, msg, ...) ' have a special Response
Else
result = New LoginResponse(..., code, ...) ' have a normal Response
End If
Return result
End Function
Finally when checking the response you just check whether you have a special value in ResponseCode and cast the object respectivly.
'...
Dim resp as LoginResponse = GetResponse()
If resp.ResponseCode = SPECIAL_CODE Then
Dim respx as LoginResponseEx = CType(resp, LoginResponseEx)
Console.WriteLine("ResponseMessage was: " & respx.ResponseMsg
Else
Console.WriteLine("No ResponseMessage")
End If
'...
This way you have your basic LoginResponse with the ResponseMsg hidden in the special class ResponseLoginEx
Note when you do this you should think about how you implement virtual classes. e.g. the fields might have to be declared as Protected instead of Private, though i'm sure you'll do fine.
This also works with Serializable classes, of course.
I am consuming an RSS feed to display on my website using a repeater control. I was wondering if it's possible in VB to return an anonymous type from my linq query rather than a collection of strongly typed RSSItems. I know this is possible in C#, however haven't been able to work out a VB equivalent.
Public Class RSSItem
Public Property Title As String
Public Property Link As String
Public Property Content As String
Public Property Description As String
Public Property pubDate As String
Public Property category As String
End Class
Dim feedXML As XDocument = XDocument.Load("http://myrssfeed.com/rss.xml")
Dim xns As XNamespace = "http://purl.org/rss/1.0/modules/content/"
Dim feeds = From feed In feedXML.Descendants("item") _
Select New RSSItem With _
{.Title = feed.Element("title"),
.Link = feed.Element("link"),
.Content = feed.Element(xns.GetName("encoded")).Value,
.Description = feed.Element("description"),
.pubDate = feed.Element("pubDate"),
.category = GetCategories(feed.Elements("category"))}
I believe you can change New RSSItem With to New With. More details can be found in the VB Anonymous Types MSDN page.
As the title states I'm wondering if it's a good idea for my validation class to have access to all properties from my model. Ideally, I would like to do that because some fields require 10+ other fields to verify whether it is valid or not. I could but would rather not have functions with 10+ parameters. Or would that make the model and validator too coupled with one another? Here is a little example of what I mean. This code however does not work because it give an infinite loop!
Class User
Private m_UserID
Private m_Validator
Public Sub Class_Initialize()
End Sub
Public Property Let Validator(value)
Set m_Validator = value
m_Validator.Initialize(Me)
End Property
Public Property Get Validator()
Validator = m_Validator
End Property
Public Property Let UserID(value)
m_UserID = value
End property
Public Property Get UserID()
UserID = m_Validator.IsUserIDValid()
End property End Class
Class Validator
Private m_User
Public Sub Class_Initialize()
End Sub
Public Sub Initialize(value)
Set m_User = value
End Sub
Public Function IsUserIDValid()
IsUserIDValid = m_User.UserID > 13
End Function End Class
Dim mike : Set mike = New User
mike.UserID = 123456 mike.Validator = New Validator
Response.Write mike.UserID
If I'm right and it is a good idea, how can I go a head and fix the infinite loop with the get property UserID?
Thank you.
Solution
<!-- #include file = "../lib/Collection.asp" -->
<style type="text/css">
td { padding: 4px; }
td.error
{
background: #F00F00;
}
td.warning
{
background: #FC0;
}
</style>
<%
Class UserModel
Private m_Name
Private m_Age
Private m_Height
Public Property Let Name(value)
m_Name = value
End Property
Public Property Get Name()
Name = m_Name
End Property
Public Property Let Age(value)
m_Age = value
End Property
Public Property Get Age()
Age = m_Age
End Property
Public Property Let Height(value)
m_Height = value
End Property
Public Property Get Height()
Height = m_Height
End Property
End Class
Class NameValidation
Private m_Name
Public Function Init(name)
m_Name = name
End Function
Public Function Validate()
Dim validationObject
If Len(m_Name) < 5 Then
Set validationObject = New ValidationError
Else
Set validationObject = New ValidationSuccess
End If
validationObject.CellValue = m_Name
Set Validate = validationObject
End Function
End Class
Class AgeValidation
Private m_Age
Public Function Init(age)
m_Age = age
End Function
Public Function Validate()
Dim validationObject
If m_Age < 18 Then
Set validationObject = New ValidationError
ElseIf m_Age = 18 Then
Set validationObject = New ValidationWarning
Else
Set validationObject = New ValidationSuccess
End If
validationObject.CellValue = m_Age
Set Validate = validationObject
End Function
End Class
Class HeightValidation
Private m_Height
Public Function Init(height)
m_Height = height
End Function
Public Function Validate()
Dim validationObject
If m_Height > 400 Then
Set validationObject = New ValidationError
ElseIf m_Height = 324 Then
Set validationObject = New ValidationWarning
Else
Set validationObject = New ValidationSuccess
End If
validationObject.CellValue = m_Height
Set Validate = validationObject
End Function
End Class
Class ValidationError
Private m_CSSClass
Private m_CellValue
Public Property Get CSSClass()
CSSClass = "error"
End Property
Public Property Let CellValue(value)
m_CellValue = value
End Property
Public Property Get CellValue()
CellValue = m_CellValue
End Property
End Class
Class ValidationWarning
Private m_CSSClass
Private m_CellValue
Public Property Get CSSClass()
CSSClass = "warning"
End Property
Public Property Let CellValue(value)
m_CellValue = value
End Property
Public Property Get CellValue()
CellValue = m_CellValue
End Property
End Class
Class ValidationSuccess
Private m_CSSClass
Private m_CellValue
Public Property Get CSSClass()
CSSClass = ""
End Property
Public Property Let CellValue(value)
m_CellValue = value
End Property
Public Property Get CellValue()
CellValue = m_CellValue
End Property
End Class
Class ModelValidator
Public Function ValidateModel(model)
Dim modelValidation : Set modelValidation = New CollectionClass
' Validate name
Dim name : Set name = New NameValidation
name.Init model.Name
modelValidation.Add name
' Validate age
Dim age : Set age = New AgeValidation
age.Init model.Age
modelValidation.Add age
' Validate height
Dim height : Set height = New HeightValidation
height.Init model.Height
modelValidation.Add height
Dim validatedProperties : Set validatedProperties = New CollectionClass
Dim modelVal
For Each modelVal In modelValidation.Items()
validatedProperties.Add modelVal.Validate()
Next
Set ValidateModel = validatedProperties
End Function
End Class
Dim modelCollection : Set modelCollection = New CollectionClass
Dim user1 : Set user1 = New UserModel
user1.Name = "Mike"
user1.Age = 12
user1.Height = 32
modelCollection.Add user1
Dim user2 : Set user2 = New UserModel
user2.Name = "Phil"
user2.Age = 18
user2.Height = 432
modelCollection.Add user2
Dim user3 : Set user3 = New UserModel
user3.Name = "Michele"
user3.Age = 32
user3.Height = 324
modelCollection.Add user3
' Validate all models in the collection
Dim modelValue
Dim validatedModels : Set validatedModels = New CollectionClass
For Each modelValue In modelCollection.Items()
Dim objModelValidator : Set objModelValidator = New ModelValidator
validatedModels.Add objModelValidator.ValidateModel(modelValue)
Next
%>
<table>
<tr>
<td>Name</td>
<td>Age</td>
<td>Height</td>
</tr>
<%
Dim r, c
For Each r In validatedModels.Items()
%><tr><%
For Each c In r.Items()
%><td class="<%= c.CSSClass %>"><%= c.CellValue %></td><%
Next
%></tr><%
Next
%>
</table>
Which produces
While not perfect, it's way better than what I started with. Basically, I decided to use the decorator pattern. My next step is to most likely remove the Init() function from each validate and replace it with a SetModel() function or something. That way each validate can have access to every property in my model.
Thanks all.
I usually define a validator that validates an entire model; In this case, I would have a UserValidator class that has a method that accepts a User and returns a ValidationResult, which includes a list of validation errors.
This allows you to change the User class' implementation without affecting validation (eg, you don't have to add a new method to the Validator class every time you add a new property, or change a method signature if you want to change how the UserID is validated, etc).
I think you are right in making the validator validate the entire model. To break the infinite loop, you can pass the value to the validator
Public Property Get UserID()
UserID = m_Validator.IsUserIDValid(m_userID)
End property
// in Validator
Public Function IsUserIDValid(userID)
IsUserIDValid = userID > 13
End Function
Alternatively, if you prefer encapsulation, you can add Friend functions for accessing the property without validation.
Public Property Get UserID()
UserID = m_Validator.IsUserIDValid()
End property
Friend Function GetUserID()
GetUserID = m_userID
End Function
// in Validator
Public Function IsUserIDValid()
// "private" access - to get the unvalidated property
IsUserIDValid = m_user.GetUserID > 13
End Function
A third way to do this is to separate your object from validation. The base class defines all the properites without validation. Then you define a child class that adds validation:
class User
Private m_userID
Public Property Get UserID()
UserID = m_userID
End property
End Class
class ValidatedUser inherits User
Public Overrides Property Get UserID()
if (m_userID<15)
// handle invalid case, e.g. throw exception with property that is invalid
UserID = m_userID
End Property
Public Function Validate()
' class-level validation
End Function
End Class
A final variation uses delegation to keep the basic user properties separate from the validated ones. We make User an abstract class, since we have to implementations - one with validation, and one without.
Class MustInherit User
Public MustInherit Property Get UserID()
End Class
' A simple implementation of User that provides the properties
Class DefaultUser Inherits User
Private m_UserID
Public Overrides Property Get UserID()
UserID = m_UserID
End Property
End Class
Class ValidatedUser Inherits User
private Validator m_validator
private User m_User
Public Property Let Validator(value)
Set m_Validator = value
m_Validator.Initialize(m_User)
' note that validator uses m_User - this breaks the infinite recursion
End Property
Public Overrides Property Let UserID(value)
m_User.UserID = value;
End Property
Public Overrides Property Get UserID()
UserID = m_validator.IsUserValid();
End Property
End Class
In the last example ValidatedUser looks similar to your original code, but the key difference is that ValidatedUser itself doesn't have any property values - it delegates all property accessors to the m_User object. The Validator uses the m_user object which provides simple properties without validation, so the infinite recursion goes away.
At present, validation is done when the property is retrieved. I imagine this is done because you want to validate the data before it's used, and to avoid transient validation errors as properties are assigned. In addition to property-level validation, you may want to also define a "whole object" validation method that checks all properties on your object, particularly those involved in multi-property constraints. For example, if you have the constraint A+B+C < 50, then checking A B and C as separate properties will lead to that condition (A+B+C<50) being evaluated 3 times, which is unnecessary, and also confusing since the error will appear on one specific property, when it's really a problem with all 3 properties. Your object-level validator can check this condition just once and flag an error that indicates all 3 properties are not valid.
All of the above bind the Validation to the User class, so that clients can use User without concern for validation. There are benefits and drawbacks with this approach. The benefit is transparency - client's can use User objects and get validation behind the scenes without explicitly asking for it. The downside is that it ties validation very tightly in with your model. An alternative is to completely separate validation from the User object. This not only decouples validation, but also provides for "whole-object" validation. E.g.
' User is now a simple class (like DefaultUser above '
' with just properties, no validation '
Class UserValidator
Public Function Validate(user)
' validate the given user object, return a list of
' validation errors, each validation error object
' that describes the property or properties
' that caused the validation error and why it's an error
' E.g. '
Dim ve As ValidationError
ve = new ValidationError
ve.obj = user; ' the object that failed validation
ve.property = "userID"
ve.msg = "userId must be < 15"
' potentially put several of these in a list and return to caller
End
End Class
Any code manipulating User will then have to explicitly call Validate after making changes, but this is usually not a problem, and the level of control is much better than having it done automatically. (In my experience,you almost always have to undo "automatic" actions at some point because they get in the way.)
I wrote more than I intended. I hope this is helpful!
PS: I don't do much VB, so please be lenient of the occasional syntax error. I am an OO programmer, so I know the principles are correct. And I just noticed the "asp-classic" tag - some of the examples use features that may not be available in classic asp, although the separate Validator code - the last example, should be fine on classic asp.