SQL LINQ Query: Selecting specific column - sql

Today I am needing to write LINQ queries in VB.net to a database table, but am new to SQL/LINQ. This function below is meant to fill a list of strings with all of the possible "Questions" in the database table that match the QuestionType.
However, I only want to select one single column, the QuestionText column, and not all of the data, whenever I have a match.
Public Shared Function RetrieveQuestions(ByVal QuestionType) As List(Of String)
Dim db As New DBDataContext()
db.CommandTimeout = 300
Dim ListOfQuestions As List(Of String) = New List(Of String)
While True
Dim questionList As List(Of Question) = db.Questions.ToList
Dim question As List(Of String) = (From q As Question In questionList Where q.FormType = QuestionType Select q.QuestionText).ToList
Dim i As List(Of String) = question
If (question IsNot Nothing) Then
ListOfQuestions(ListOfQuestions.Count) = i.QuestionText //ERROR
Else
Exit While
End If
End While
Return ListOfQuestions
End Function
In the function above i am encountering an error when trying to update my list with the new QuestionText. "QuestionText is not a member of System.Collections.Generic.List(Of String)". QuestionText is defined as a varchar in my SQL database, so I know that it is definitely a string. I am not trying to set QuestionText to a list of strings, but rather add it to the end of a list of strings.

Direct answer: you'd need to put the whole If (question IsNot Nothing) Then block in a loop like For Each. As the compiler correctly informs - the i variable holds the whole list, not one of its items. Perhaps you forgot you left the LINQ query?
A better solution: I believe you could just use AndAlso q.QuestionText IsNot Nothing - it spares you the need to allocate a new list and to fill it one by one - the following code should do the trick.
Public Shared Function RetrieveQuestions(ByVal QuestionType) As List(Of String)
Dim db As New DBDataContext()
db.CommandTimeout = 300
Dim ListOfQuestions As List(Of String) = (
From q As Question In db.Questions.ToList
Where
q.FormType = QuestionType
AndAlso q.QuestionText IsNot Nothing
Select q.QuestionText
).ToList
Return ListOfQuestions
End Function

Related

VB.NET copy a 2-dimenion List into a one dimension list

In my original code I have a list object containing 2 columns, Word and Percent. I sort the list but only want to return the list containing just the Word
Here is some example code broken down into something simple:
Public Function SortMyWords() as list(of string)
Dim Words As WordsToSort
Dim ListofWords As New List(Of WordsToSort)
Words.Word = "John"
Words.Percent = "10"
ListofWords.Add(Words)
Words.Word = "Robert"
Words.Percent = "1"
ListofWords.Add(Words)
ListofWords = ListofWords.OrderBy(Function(x) x.Percent).ToList()
End Sub
Public Structure WordsToSort
Public Word As String
Public Percent As String
Public Sub New(ByVal _word As String, ByVal _percent As String)
Word = _word
Percent = _percent
End Sub
End Structure
At the end of the SortMyWords function, I want to return just the Word column back as a list, I'm not sure if I can do this direct - i.e.
Return Listofwords(column Word) or whether I need to copy my ListofWords into a new list, just containing the Column Word - something like this (which doesn't work)
Dim Newlist As New List(Of String)
Newlist.AddRange(ListofWords(Words.Word))
Return NewList
Any suggestions on whether I should do this completely differently (and better) would be really appreciated as I am trying to get my head around objects and although I use them all the time, I'm new to structures and list objects.
Thanks for any help, this has been driving me crazy for an hour now.
I think you're close. Try:
ListOfWords
.OrderBy(Function(x) x.Percent)
.Select(Function(x) x.Word)
.ToList()
If you prefer, you can also use the LINQ syntax:
(from w in ListOfWords
orderby w.Percent ascending
select w.Word).ToList()
Note that the return type is a List(Of String) and not a List(Of WordsToSort) anymore. So you cannot assign it back to the variable ListOfWords again like you do in your sample code.

GetType of Values in DataTable

I've been working on a project which has a Function of reading Data from a DataTable and returning it as a list. I made specific statements over some rows to get the data I want.
Little problem is, my entity "Number" is declared As an Integer and in the DataTable I do have some String values which leads the function to crash. Do any of you guys has an Idea about how I can check which Data Type the row "Number" has and if it's not an Integer to just leave it out?
My function looks like this:
Public Function LiefereAlleRechte(ByVal dt As DataTable) As ICollection(Of Recht) Implements IBenutzerInfoServiceUtil.LiefereAlleRechte
If dt Is Nothing Then
Throw New ArgumentNullException("DataTable can't be empty", "dt")
End If
Dim query = From dr As DataRow In dt.DefaultView.ToTable(True, "Name", "Number")
Dim coll As ICollection(Of Recht) = New List(Of Recht)
For Each row In query
Dim recht As Recht = New Recht()
With recht
.Name = row.Item("Name")
.Nummer = row.Item("Number")
coll.Add(recht)
End With
Next
Return coll
End Function
Thanks for any help!

How to make a generic database record to object conversion function?

I am using the following function to retrieve records from a database and convert the records to a collection of strongly typed objects.
Private Function GetPlantSettingsFiltered(parameters As Dictionary(Of String, Object), queryCondition As String) As PlantSettings
Dim query As String
query = " SELECT * FROM Plant_Settings " _
+ queryCondition
Dim settings As New PlantSettings
Dim table As DataTable = GetQueryResults(parameters, query, GetConnectionString("WeighScaleDB"))
If table Is Nothing Then
Return settings
End If
For Each row As DataRow In table.Rows
settings.Add(New PlantSetting With {
.Setting_ID = ConvertByteArrayToString(TryCast(row("Setting_ID"), Byte())),
.Plant_ID = ConvertByteArrayToString(TryCast(row("Plant_ID"), Byte())),
.Value = row("Setting_Value").ToString(),
.Comments = row("Setting_Comments").ToString()
})
Next
Return settings
End Function
I would like to create a generic version of this function that would work for any of my objects without me creating this function for each object.
For example, if the caller could specify the type, then some other details, the function would return a collection of that type.
Private Function GetObjects(Of T)(parameters As Dictionary(Of String, Object), query As String) As WSAEntityCollection(Of T)
Dim objectCollection As New WSAEntityCollection(Of T)
Dim table As DataTable = GetQueryResults(parameters, query, GetConnectionString("WeighScaleDB"))
If table Is Nothing Then
Return objectCollection
End If
For Each row As DataRow In table.Rows
' Here is my problem
objectCollection.Add(New T With {})
Next
Return objectCollection
End Function
My current problem with this new function is that I do not know how to dynamically match the column names with the parameters of the generic object. Any ideas on how this could be done?

How to query an arraylist using Linq that stores dictionaries

The following code reads query results from oracle data reader and stores each record in a dictionary and appends the dictionaries to an array list :
Dim dr As OracleDataReader = cmd.ExecuteReader()
'loop oracle data records and store them to dictionaries
'append dictionaries to an array list
Dim arr As New ArrayList
While dr.Read
Dim dict As New Dictionary(Of String, Object)
For count As Integer = 0 To (dr.FieldCount - 1)
dict.Add(dr.GetName(count), dr(count))
Next
arr.Add(dict)
End While
How do I write a LINQ query that can be used to retrieve values from the dictionaries stored in the array list? Please help. I've been searching and have not got any good answers
First of all, don't use ArrayList, ever. It is there for backwards compatibility but has no usage. I can make answer short - there is no use of LINQ with ArrayList. Use generic List(Of T) and LINQ to search values in it. No need for Dictionary either. This is the old style. We used Dictionary because it has key
I see, you trying to create your table structure but no need for this. First of all, there is System.Data.DataTable, which can be queried on client.
Or use this technique
Public Class User
Public Property Id As Integer
Public Property Name As String
Public Property Email As String
Public Property Country As String
End Class
Private Function LoadUsers() As List(Of User)
Dim uList As New List(Of User)()
' Some Code goes here
While dr.Read()
Dim u As New User()
u.Id = dr("Id")
u.Name = dr("Name")
u.Email = dr("Email")
u.Country = dr("Country")
uList.Add(u)
End While
. . . . . . .
Return uList
End While
' somewhere in class set member variable
_users = LoadUsers()
' And then you can search for info using LINQ
Public Function FindByCountry(ByVal country As String) As List(Of User)
Return _users.Where(Function(u) u.Country.Equals(country, StringComparison.OrdinalIgnoreCase))
End
The downside of this approach - you need Find function for each field. But what if you can pass a function itself. See- you have Name, email, Country - all strings. Here what you can do
Class Client
Sub SearchStrings(ByVal searchOption String, Byval searchValue As String)
Dim f As Func(Of User, boolean)
If searchOption = "Name" Then
f = Function(u as User)(u.Name.Equals(searchValue , Stringcomparison.OrdinalIgnoreCase))
ElseIf searchOption = "Country" Then
f = Function(u as User)(u.Country.Equals(searchValue , Stringcomparison.OrdinalIgnoreCase))
ElseIf searchOption = "Email" Then
f = Function(u as User)(u.Email.Equals(searchValue , Stringcomparison.OrdinalIgnoreCase))
Else
. . . .
End If
dataGrd.DataSource = myRepository.FindByString(f)
End Sub
End Class
' In your repository class
public sub FindByString(ByVal f as Func(Of String, Boolean)) As List(Of User)
_users.Where(f).ToList()
End sub
' use this to search single user
public sub FindByInteger(ByVal f as Func(Of Integer, Boolean)) As User
_users.SingleOrDefault(f)
End sub
The bottom line - drop what you do and use modern and efficient techniques. And above are just couple of them

How to merge two list to have a distinct list without duplicate values in vb.net

I have this problem in vb.net. Lets say I got 2 Lists ListA and ListB both holds objects of same type.
Eg., one of the property of the object is ID. (ID is written in brackets)
ListA ListB
---------------------------
A(3818) A(3818)
B(3819) B(3819)
C(3820) C(3820)
D(3821) D(3821)
E(3823) F(0)
H(3824) G(0)
I(3825)
How do I merge these two Lists to have a new distinct list which holds objects only once whose ID matches and all other objects(whose ID dont match) are simply added to the new list.
Sample output be,
New List
--------
A(3818)
B(3819)
C(3820)
D(3821)
E(3823)
F(0)
G(0)
H(3824)
I(3825)
When I searched I found that AddRange() and Union are some of the methods to do the merge. But i am not able to find if this works for non standard objects(apart from Integer, String)
Use addRange() and then linq with distinct to filter out the duplicates.
Dim b = YourCollection.Distinct().ToList()
Could use a collection bucket
Dim oCol As New Collection
AddTitems(oCol, oListA)
AddTitems(oCol, olistB)
Public Function AddTitems(oSummaryList As Collection, oList As List(Of thing)) As Collection
For Each oThing As thing In oList
If Not oSummaryList.Contains(CStr(oThing.ID)) Then oSummaryList.Add(oList, CStr(oThing.ID))
Next
Return oSummaryList
End Function
Here are a couple simple functions that should do that for you. I'm not sure how efficient they are though. I don't think there is anything built in.
Private Function nameOfFunction(list1 as list(of type), list2 as list(of type)) as list(of type)
Dim result as new list(of type)
for a as integer = 0 to math.max(list1.count, list2.count) - 1 step 1
If a < list1.count AndAlso resultHasID(result, list1(a).ID) = False Then
result.add(list1(a))
end if
If a < list2.count AndAlso resultHasID(result, list2(a).ID) = False Then
result.add(list2(a))
end if
next
End Function
Private Function resultHasID(testList as list(of type), s as string) as boolean
Dim result as Boolean = False
for a as integer = 0 to testlist.count - 1 step 1
if(testlist(a).ID = s) then
result = true
exit for
End if
Next
Return result
End function
For each item as String in ListA
If Not ListB.Contains(item) Then
ListB.Add(item)
End If
Next