Convert a standard EF list to a nested JSON - asp.net

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#.)

Related

VB.NET hide a property member

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.

Bloated Session Struct:

I understand the basic differences between structs and class types. But, in an ASP.NET WebForms application...what is the ideal container type to hold a relatively large data structure in Session in an ASP.NET WebForms application? Should it be a struct or class? For example...
Public Structure SessionData
Public ShipVias As List(Of XXXXX999.sycdefil_sql_VW)
Public ShipTos As List(Of XXXXX999.ABC_Web_ShipTo_VW)
Public Dests As List(Of XXXXX999.ABC_Web_CofA_VW)
Public Owners As List(Of XXXXX999.ABC_Web_CofA_VW)
Public Carts As List(Of XXXXX999.ABC_Web_Cart_VW)
Public Carriers As List(Of XXXXX999.ABC_Carrier_Accounts_VW)
Public Countries As List(Of XXXXX999.usp_get_CountryListResult)
Public States As List(Of XXXXX999.usp_get_StatesListResult)
Public POItems As List(Of XXXXX999.usp_get_cart_infoResult)
Public SearchResults As List(Of SearchResult)
Public Addresses As List(Of AddressResult)
Public CartItems As List(Of itemsdetail)
Public POs As List(Of POResult)
Public SomeCollection As Collection
Public CmpCode As String
Public CmpStatus As String
Public CmpSIPCust As String
Public CurrentSession As String
Public AllowAccess As Boolean
Public QuickAddItems As List(Of QuickAddItem)
End Structure
As you can probably tell, XXXXX999 contains a LINQTOSQL DataContext dbml containing sprocs, functions, views and tables.
Edit point: I just realized I can make a struct(C#) to be nullable...In vb.net Nullable(Of SessionData)...also question mark does the trick like so...
Dim sd As SessionData?
Should I keep this as a Structure or convert it to a Class?
Convert it to a class. Based on this MSDN link a struct should only be used if:
Consider defining a structure instead of a class if instances of the
type are small and commonly short-lived or are commonly embedded in
other objects.
Do not define a structure unless the type has all of the following characteristics:
It logically represents a single value, similar to primitive types (integer, double, and so on).
It has an instance size smaller than 16 bytes.
It is immutable.
It will not have to be boxed frequently.
if you are dealing with distinct values I would use a dictionary that hold dictionaries for every type of list

How to throw an exception in BLL when no data found in DAL

I am trying to throw an exception in my BLL when there is no corresponding carID for the license plate number I've entered in a text box.
My DAL looks like this:
Public Class DALCar
Private dc As New cars_modelDataContext
Public Function getCarIdByLicenePlate(ByVal licensePlate_input As String) As String
Dim result = (From car In dc.Cars
Where car.License_Plate = licensePlate_input
Select car.License_Plate).Single
Return result
End Function
End Class
And this is my BLL:
Public Class BLLCar
Private DALcar As New DALCar
Public Function getCarIdByLicenePlate(ByVal licensePlate_input As String) As String
Dim carID As String = DALcar.getCarIdByLicensePlate(chassisNo_input)
End Function
End Class
So when there is no carID with this specific license plate an exception is throwed in my DAL, but how can I throw this exception in my BLL instead of in my DAL?
Use FirstOrDefault instead of Single
Public Function getCarIdByLicenePlate(ByVal licensePlate_input As String) As String
Dim result = (From car In dc.Cars
Where car.License_Plate = licensePlate_input
Select car.License_Plate).FirstOrDefault
Return result
Public Function getCarIdByLicenePlate(ByVal licensePlate_input As String) As String
Dim carID As String = DALcar.getCarIdByLicensePlate(chassisNo_input)
If carID = Nothing Then
Throw New Exception(String.Format("Can't find car id for chassisNo : {0}", chassisNo_input))
End If
End Function
Because you're using Enumerable.Single in your LINQ expression. It throws an exception if there is more than one element in the sequence or if the sequence is empty.
If you can assume the sequence will always contains 0 or 1 element then you can replace Single with FirstOrDefault (see later for more on this). It'll return the first element in the sequence or Nothing if sequence is empty.
In this case you can check for Nothing in your BLL and throw the appropriate exception there.
Like this in your DAL:
Public Class DALCar
Private dc As New cars_modelDataContext
Public Function getCarIdByLicenePlate(ByVal licensePlate_input As String) As String
Dim result = (From car In dc.Cars
Where car.License_Plate = licensePlate_input
Select car.License_Plate).FirstOrDefault
Return result
End Function
End Class
And this in your BLL:
Public Class BLLCar
Private DALcar As New DALCar
Public Function getCarIdByLicenePlate(ByVal licensePlate_input As String) As String
Dim carID As String = DALcar.getCarIdByLicensePlate(chassisNo_input)
If carId = Nothing Then
Throw New ArgumentException("There is no match.")
End If
End Function
End Class
If your query may returns more than one element than you have to consider if this is an error or not. If it's allowed and you want to process (return) the first one then go on with FirstOrDefault. If it's an error then you should return an enumeration from your DAL and to check the number of items in your BLL (otherwise, using Single, you'll still throw inside DAL).

Return Anonymous type from LINQ query in VB.NET

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.

Binding arraylist to grid view (Part whatever....)

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

Resources