How to convert Linq.IEnumerable(Of String) to formatted Strings - asp.net

I have a List(Of String) that stores validation errors. Should that list contain any items, I'd like to concatenate them into an HTML list to show each error. Currently this is easily done like so:
Dim l As List(Of String) = GetErrors()
If l.Count > 0 Then
Dim sb As New StringBuilder
sb.Append("<div><ul>")
For Each s As String In l
sb.Append(String.Format("<li>{0}</li>", s))
Next
sb.Append("</ul></div>")
ltl_status.Text = sb.ToString()
End If
However, because this is quite lengthy, I wondered whether Linq could provide a shortcut. I tried this (line breaks added for clarity):
If l.Count > 0 Then
ltl_status.Text = String.Format("<div class=""failure""><ul>{0}</ul></div>",
(
From s As String In l Select
String.Format("<li>{0}</li>", s)
)
)
End If
However, given that IEnumerable is a collection, the end result is just this output in the Literal:
System.Linq.Enumerable+WhereSelectListIterator`2[System.String,System.String]
The aim here is to build the list using the least possible lines of code. I see that String.Join accepts an IEnumerable parameter, but that simply joins the items together, whereas here I need to add additional strings to the beginning and end of each item. Is it possible?
Answer
Based on Jon Skeet's excellent suggestions, the extension method has saved me a ton of time and effort:
Public Module CollectionSignatureMethods
''' <summary>
''' Takes each String value in a String collection, reformats using a format, and then returns all as a single String.
''' </summary>
''' <param name="ie">An IEnumerable(Of String) collection of string values</param>
''' <param name="formatString">The string format (as per String.Format)</param>
''' <returns>All of the Strings from the collection, reformatted and combined to a single String</returns>
''' <remarks>Jon Skeet is the daddy(!)</remarks>
<Extension()> _
Public Function JoinFormat(ie As IEnumerable(Of String), formatString As String) As String
Return String.Join(String.Empty, ie.Select(Function(s As String) String.Format(formatString, s)))
End Function
End Module

It sounds like you need a combination of Join and Select:
String.Join("", _
l.Select(Function(s as String) String.Format("<li>{0}</li>", s))
Of course, you could always write your own JoinFormat extension method - that wouldn't be hard to do, and would be potentially useful all over the place. For example, you might then have:
ltl_status.Text = String.Format("<div class=""failure""><ul>{0}</ul></div>",
l.JoinFormat("<li>{0}</li>"));

Related

How to split a MULTIPLE integers values column in linq

I have to filter the records based on a column(datatype: string) integers values using LINQ for eg., IDs: 3,34,35,36,98,43.
Also i tried with the split in linq, this is my scenario
Banners = Banners.Where(Function(x) Not String.IsNullOrEmpty(x.IDs) AndAlso x.[IDs].Split(New Char() {","c}, StringSplitOptions.RemoveEmptyEntries).Select(Function(a) Convert.ToInt32(a)).Contains(3))
my Scenario when doing the split on the IDs it shows a error
Failure: Execution of 'System.Linq.Enumerable:Contains(IEnumerable`1,Int32)' on the database server side currently not implemented.
How to filter the record based on the column IDs integer value using linq?
Replace the .Contains with .Where(x=>x==3).FirstOrDefault() and check for non null.
Personally if that is your data structure I would consider redesigning the db. If that doesn't happen, don't bother with converting to int. Just search for strings.
You can't use Split in LINQ to SQL but you can use String.Contains so translate the Split/Contains into String.Contains:
Banners = Banners.Where(Function(x) Not String.IsNullOrEmpty(x.IDs) AndAlso (","+x.[IDs]+",").Contains(","+"3"+","))
I was not sure about your Banner Class
but I tried with below logic, and it just worked fine
Module Module1
Sub Main()
Dim Banners = New List(Of Banner)
Banners = Banners.Where(Function(x) Not String.IsNullOrEmpty(x.IDs) AndAlso x.[IDs].Split(",").Select(Function(a) Convert.ToInt32(a)).Contains(3)).ToList()
End Sub
End Module
Class Banner
Private _IDs As String
Public Property IDs() As String
Get
Return "3,34,35,36,98,43"
End Get
Set(ByVal value As String)
_IDs = value
End Set
End Property
End Class
Only Change I have is I have added ToList() at the end of Statement while assigning to Banners object

How do I write a VB.Net method to filter a URLs?

I am attempting to write a method using VB.NET that will allow me to read in a URL and compare it to a list. If it is one of the URLs on the list then Bing Tracking conversion will be applied.
At the moment I can only think do write it as a comaparative method, comapring the current URL with the ones that require tracking (a list). This, however, sems a little long winded.
Each page may have a different querystring value/page id, there for its fundamental to get exactly the right page for the tracking to be applied to.
Any Ideas?
Sorry I really am a novice when developing functions in VB.Net
If I were to use th Contains() function then I would imagine that it would look a little something like this:
Private sub URL_filter (ByVal thisPage As ContentPage, brandMessage? As Boolean) As String
Dim url_1 As String = "/future-contact thanks.aspx"
Dim url_2 As String = "/find-enquiry thanks.aspx?did=38"
Dim url_3 As String = "/find-enquiry-thanks.aspx?did=90"
Dim url_4 As String = "/find-enquiry-thanks.aspx?did=62"
Dim result as String
result = CStr (url_1.Contains(current_URL))
txtResult.Text = result
End Sub
If I were to use this then what type of loop would I have to run to check all the URLs that are in my list against the current_URL? Also where would I define the current_URL?
You can use the Contains() function to check if the list contains the given value. You could also implement a binary search, but it is probably overkill for your purposes. Here is an example:
Dim UrlList As New List(Of String)
UrlList.Add("www.example2.net") 'Just showing adding urls to the list
UrlList.Add("www.example3.co.uk")
UrlList.Add("www.exampletest.com")
Dim UrlToCheck As String = "www.exampletest.com" 'This is just an example url to check
Dim result As Boolean = UrlList.Contains(UrlToCheck) 'The result of whether it was found
Make sure to add these imports Imports System and Imports System.Collections.Generic
Disclaimer: I have no experience with VB.NET

How to deal with SqlDataReader null values in VB.net

I have the follwoing code that performs a query and returns a result. However, I looked around and found some examples to take care of null values but I get an error: "Invalid attempt to read when no data is present." I also got the error: "Conversion from type 'DBNull' to type 'Decimal' is not valid."
Can someone help me out with this code to prevent null values from crashing my program?
Private Sub EFFICIENCY_STACKRANK_YTD(ByVal EMPLOYEE As String)
Dim queryString As String = "SELECT " & _
" (SELECT CAST(SUM(TARGET_SECONDS) AS DECIMAL)/ CAST(SUM(ROUTE_SECONDS) AS DECIMAL) FROM dbo.APE_BUSDRIVER_MAIN WITH(NOLOCK) WHERE APE_AREA_OBJID = " & lblAreaOBJID.Text & " AND EMPLOYEE_NAME = '" & EMPLOYEE & "' AND YEAR_TIME = '" & cbYear.Text & "' AND ACTIVE = 1) AS RESULT1" & _
" FROM dbo.APE_BUSDRIVER_MAIN "
Using connection As New SqlConnection(SQLConnectionStr)
Dim command As New SqlCommand(queryString, connection)
connection.Open()
Dim reader As SqlDataReader = command.ExecuteReader()
If reader.Read Then
RESULT1 = reader("RESULT1")
Else
RESULT1 = 0
End If
End Using
End Sub
You have opened the reader, but have not asked it to actually read anything.
After this line:
Dim reader As SqlDataReader = command.ExecuteReader()
add
If reader.Read() Then
and wrap the result reading into this if statement, i.e.
If reader.Read() Then
Dim index As Integer = reader.GetOrdinal("RESULT1")
If reader.IsDBNull(index) Then
RESULT1 = String.Empty
Else
RESULT1 = reader(index)
End If
End If
Note that this works because your SQL should only return a single record. In the event that you were reading multiple records, you would need to call the Read statement in a loop until there were no more records, i.e.
Do While reader.Read()
Loop
I wanted to provide another, more-advanced, answer as an option. Many classes can be extended in .NET like this.
If you are regularly performing "Is NULL" checks like this in your applications, you can choose to extend the DataReader class once to have additional functions available everywhere in your application. Here is an example that creates an extension called "ReadNullAsString()" onto the data reader class. This makes a function that always returns String.Empty when a DbNull is encountered.
Part 1, place this module code in a new class file in App_Code if application is a website, otherwise place where ever you prefer. There are two overloads, one for the field's ordinal position (aka index), and one for the field's ColumnName.
Public Module DataReaderExtensions
''' <summary>
''' Reads fieldName from Data Reader. If fieldName is DbNull, returns String.Empty.
''' </summary>
''' <returns>Safely returns a string. No need to check for DbNull.</returns>
<System.Runtime.CompilerServices.Extension()> _
Public Function ReadNullAsEmptyString(ByVal reader As IDataReader, ByVal fieldName As String) As String
If IsDBNull(reader(fieldName)) Then
Return String.Empty
Else
Return reader(fieldName)
End If
Return False
End Function
''' <summary>
''' Reads fieldOrdinal from Data Reader. If fieldOrdinal is DbNull, returns String.Empty.
''' </summary>
''' <returns>Safely returns a string. No need to check for DbNull.</returns>
<System.Runtime.CompilerServices.Extension()> _
Public Function ReadString(ByVal reader As IDataReader, ByVal fieldOrdinal As Integer) As String
If IsDBNull(reader(fieldOrdinal)) Then
Return ""
Else
Return reader(fieldOrdinal)
End If
Return False
End Function
End Module
Step 2, call the new extension like so:
' no need to check for DbNull now, this functionality is encapsulated in the extension module.
RESULT1 = reader.ReadNullAsEmptyString(index)
'or
RESULT1 = reader.ReadNullAsEmptyString("RESULT1")

Do I Need a Class if I only need 1 Property (at the moment)?

Update: I didn't make it clear but I meant this to be a question about where/how I would use a function to return a list of strings when I'm trying to just work with classes.
I have a class called Account.
I have data access class called AccountDAO.
I have various functions that return lists of objects like GetAllAccounts, GetAccountByID etc.
I want to populate a drop down list with just the account names and nothing else. It's proving rather slow when using lists of objects and databinding them to the dropdownlist.
I feel like I should be using a simple "Select Account_Name From blah" type statement and returning a list of strings but I don't know how to work this into my class and data access class.
How should I handle this predicament?
You can use a list of string,s and bind the list of strings to a dropdownlist no problem... the DDL can support that, just leave out DataTextField and DataValueField props, and it will display the account name as is, which that name would be accessible through the ListItem's Text and Value property.
I like to use objects to be consistent with the rest of the app (which other areas might need a class), and if for some reason you want to add AccountKey later, if you use an object, all you need to do is add a property. Otherwise, if using strings, you'd have to switch up the binding later to point to the object.
HTH.
There is nothing wrong by making a function that only returns a list of strings. YOu could however wonder if it's not better to restrict the number of records you want to put in the list and use some kind of paging.
Assuming that you're using a List<>, you can try something like this:
IEnumerable<string> nameList = accountList.Select(t => t.AccountName);
Or if you need a List:
List<string> nameList = accountList.Select(t => t.AccountName).ToList();
Go with your feelings. Use a datareader to select the list and then load them into an arraylist which can then be bound to the dropdown. Alternately, use something like this method I use to provide both a DisplayMember and a ValueMember which uses a class (with both values) as members of the arraylist. This should give you the general idea. (Note: I normally include this code in a data access class (MyBase) where StartReader, _datRdr, ReadNext and_ReaderValid are a members. But the general idea is intact.)
Public Sub LoadDataSource(ByRef PlantDataSource As PlantSource, Optional ByVal Filter As String = "", Optional ByVal IncludeBlankItem As Boolean = False)
PlantDataSource = New PlantSource
If IncludeBlankItem Then
PlantDataSource.Add(0, "")
End If
If Filter = String.Empty Then
Call StartReader(" Order by PlantName")
Else
Call StartReader(String.Concat(" Where ", Filter, " Order by PlantName"))
End If
If _DatRdr.HasRows Then
While MyBase._ReaderValid
PlantDataSource.Add(PlantId, PlantName)
ReadNext()
End While
End If
Call CloseReader()
End Sub
Private Class PlantListing
Private _PlantList As New ArrayList
Public Sub Add(ByVal PlantId As Integer, ByVal PlantName As String)
_PlantList.Add(New PlantDataItem(PlantId, PlantName))
End Sub
Public ReadOnly Property List() As ArrayList
Get
Return _PlantList
End Get
End Property
End Class
Private Class PlantDataItem
Private _PlantId As Integer
Private _PlantName As String
Public Sub New(ByVal pPlantId As Integer, ByVal pPlantName As String)
Me._PlantId = pPlantId
Me._PlantName = pPlantName
End Sub
Public ReadOnly Property PlantName() As String
Get
Return _PlantName
End Get
End Property
Public ReadOnly Property PlantId() As Integer
Get
Return _PlantId
End Get
End Property
Public ReadOnly Property DisplayValue() As String
Get
Return CStr(Me._PlantId).Trim & " - " & _PlantName.Trim
End Get
End Property
Public Overrides Function ToString() As String
Return CStr(Me._PlantId).Trim & " - " & _PlantName.Trim
End Function
End Class

Concept needed for building consistent urls (routes)

My project has the need to build consistent urls similar to the ones here on stackoverflow. I know how I "can" do it by running the string through multiple filters, but I'm wondering if I can do it all with a single method.
Basically I want to remove all special characters, and replace them with dashes BUT if there are multiple dashes in a row, I need them to be a single dash. How can I implement this as clean as possible?
Example: If I were to use the following string.
My #1 Event
My regex would create the following string
my--1-event
notice how there are two dashes (one for the space and one for the "#" symbol). What I need is
my-1-event
Here's how I'm implementing it currently
''# <System.Runtime.CompilerServices.Extension()>
Public Function ToUrlFriendlyString(ByVal input As String) As String
Dim reg As New Regex("[^A-Za-z0-9]")
''# I could run a loop filter here to match "--" and replace it with "-"
''# but that seems like more overhead than necessary.
Return (reg.Replace(Trim(input), "-"))
End Function
And then all I do is call the extension method
Dim UrlFriendlyString = MyTile.ToUrlFriendlyString
Thanks in advance.
Add a + to the end of the regex.
This will tell it to match one or more characters that match the character class that precedes the +.
Also, you should create your Regex instance in a Shared field outside the method so that .Net won't need to parse the regex again every time you call the method.
[edited by rockinthesixstring]: here's the final result
Private UrlRegex As Regex = New Regex("[^a-z0-9]+", RegexOptions.IgnoreCase)
<System.Runtime.CompilerServices.Extension()>
Public Function ToUrlFriendlyString(ByVal input As String) As String
Return (UrlRegex.Replace(Trim(input), "-"))
End Function
Another way I do this without using a regex and also is a little simpler to understand is the following:
Excuse me on my vb as I am mainly C# guy.
''# <System.Runtime.CompilerServices.Extension()>
Public Function ToUrlFriendlyString(ByVal input As String) As String
If [String].IsNullOrEmpty(s) = True Then
Return [String].Empty
End If
Dim builder As New StringBuilder()
Dim slug = input.Trim().ToLowerInvariant()
For Each c As Char in slug
Select Case c
Case ' '
builder.Append("-")
Case '&'
builder.Append("and")
Case Else
If (c >= '0' And c <= '9') OrElse (c >= 'a' And c <= 'z') And c != '-')
builder.Append(c)
End If
End Select
Next
Return builder.ToString()
End Function

Resources