What is the difference between Single and FirstOrDefault? [duplicate] - vb.net

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
LINQ Single vs First
Im new to Linq and want to learn it the best way, I have here 2 working update events for linq, thay do the same, but what way is the best and do i need to add something to make it better !?
Solution 1
Protected Sub Button2_Click(sender As Object, e As System.EventArgs) Handles Button2.Click
Using db As New ThedatabaseconnectionDataContext()
Try
Dim TheUpdateID = DirectCast(FindControl("Textbox5"), TextBox).Text
Dim getEditing As testtable = (From c In db.testtables Where c.test_id = TheUpdateID Select c).FirstOrDefault()
If getEditing IsNot Nothing Then
getEditing.test_cat = DirectCast(FindControl("Textbox1"), TextBox).Text
getEditing.test_info = DirectCast(FindControl("Textbox2"), TextBox).Text
getEditing.test_number = DirectCast(FindControl("Textbox3"), TextBox).Text
getEditing.test_datetime = DirectCast(FindControl("Textbox4"), TextBox).Text
db.SubmitChanges()
'textBox1.Text = "Contact updated."
End If
Catch ex As Exception
'Me.lblMsg.Text = ex.Message
End Try
End Using
End Sub
Solution 2
Protected Sub Button2_Click(sender As Object, e As System.EventArgs) Handles Button2.Click
Using db As New ThedatabaseconnectionDataContext()
Try
Dim tbltest As Table(Of testtable) = db.GetTable(Of testtable)()
Dim TheUpdateID = DirectCast(FindControl("Textbox5"), TextBox).Text
Dim getEditing As testtable = tbltest.Single(Function(c) c.test_id = TheUpdateID)
If getEditing IsNot Nothing Then
getEditing.test_cat = DirectCast(FindControl("Textbox1"), TextBox).Text
getEditing.test_info = DirectCast(FindControl("Textbox2"), TextBox).Text
getEditing.test_number = DirectCast(FindControl("Textbox3"), TextBox).Text
getEditing.test_datetime = DirectCast(FindControl("Textbox4"), TextBox).Text
db.SubmitChanges()
'textBox1.Text = "Contact updated."
End If
Catch ex As Exception
'Me.lblMsg.Text = ex.Message
End Try
End Using
End Sub

First off, you should be able to write your first or default as
Dim getEditing As testtable = tbltest.FirstOrDefault(Function(c) c.test_id = TheUpdateID)
Untested but more to point out that first or default handles lambdas
As for which to use, it depends on your data. To break down what happens
Single - Expects exactly one match, A exception is thrown if no
results are found OR multiple results are found
SingleOrDefault -
Expects 0 or 1 match. A exception is thrown if multiple matches are
found
First - Expects 1 or many match. Exception is thrown if no
matches are found. Any results after the first result are ignored.
FirstOrDefault - handles 0, 1 or multiple matches. Any results after the first result are ignored.
If you are picking based on a ID from a listbox (I.e. it's unique and is definatly in the database) then single is a safe choice.
If the user is entering a ID (again unique) that may or may not be in the DB single or default is safe.
If searching based on a possible duplicate value, like surname, then first or firstordefault is what you should use depending if it is guaranteed to exist in the database or not.
Personally, regardless of the data, I would stick to either first or firstordefault as it handles more scenarios.

Where to even start on this one...
For a start, I know this is likely a test project, so apologies if you're already doing this, but please make sure you're using acceptable data tier hierarchies - your DBML should be in a separate project from your Presentation layer.
But, to the question in hand. My preferred way of doing this is getting the data object and updating it on an object level. Such as (pseudo code / I'm a C# kinda guy!):
private MyObject object;
protected void Page_Load(object sender, EventArgs e)
{
// Select usually be ID
object = DataLayer.GetObject();
if(!IsPostBack)
{
// Load object details for editing into presentation layer
TextboxObjectName.Text = object.Name;
}
}
protected void Button_Click(object sender, EventArgs e)
{
// Button click event - update object and send it to database
object.Name = TextboxObjectName.Text;
DataLayer.UpdateObject(object);
}
This makes use of object tracking, and the Daya Layer can then look like this:
function void UpdateObject(MyObject obj)
{
using (TestDataContext db = new TestDataContext ())
{
db.MyObjects.Attach(obj);
db.Refresh(RefreshMode.KeepCurrentValues, obj);
db.SubmitChanges();
}
}

Related

Filtering binding source

This should be easy but I am having a headbanging of a time trying to get this to work! I have done a search and tried all most EVERY SINGLE ONE. Nothing works. I have a datagrid with a binding source. A user will type text into a textbox and the grid is SUPPOSED to change to only show records that contain what user typed in the name. Simple right? NOPE! Not for me! What am I doing wrong? Code below.
Private Sub SearchButton_Click(sender As Object, e As EventArgs) Handles SearchButton.Click
Dim Found As Boolean = False
Dim StringToSearch As String = ""
Dim ValueToSearchFor As String = "%" & SearchTextBox.Text.Trim.ToLower & "%"
Dim CurrentRowIndex As Integer = 0
Try
If ReferencesGrid.Rows.Count = 0 Then
CurrentRowIndex = 0
Else
CurrentRowIndex = ReferencesGrid.CurrentRow.Index + 1
End If
If CurrentRowIndex > ReferencesGrid.Rows.Count Then
CurrentRowIndex = ReferencesGrid.Rows.Count - 1
End If
If ReferencesGrid.Rows.Count > 0 Then
For Each gRow As DataGridViewRow In ReferencesGrid.Rows
StringToSearch = gRow.Cells(1).Value.ToString.Trim.ToLower
If InStr(1, StringToSearch, LCase(Trim(SearchTextBox.Text)), vbTextCompare) Then
TrainingItemBindingSource.Filter = String.Format("Name LIKE '{0}'", ValueToSearchFor)
Exit For
End If
Next
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
You should get rid of pretty much all that code. If you want to filter the data then just filter the data. There's no conditional statements required and loops required. Just set the Filter property and any records that don't match the filter will be hidden:
Private Sub SearchButton_Click(sender As Object, e As EventArgs) Handles SearchButton.Click
TrainingItemBindingSource.Filter = $"Name LIKE '%{SearchTextBox.Text.Trim()}%'"
End Sub
As you can see, it is simple. I've no real idea what you were actually trying to achieve with the rest of that code. That will exclude any records where the Name column does not contain the search text.
Note that there is no need to try to force case-insensitivity by using ToLower or the like. Just like in real SQL, comparisons done this way in a DataTable are case-insensitive by default. You have to explicitly set the CaseSensitive property of the DataTable or its parent DataSet to True to make such comparisons case-sensitive.
I should also point out that the ability to filter is predicated on the data source implementing certain interfaces. If the data source is a DataTable then you have those interfaces for free. If you have actually bound to something else, e.g. a List(Of T), then you won't be able to filter this way because the required members do not exist.

Pasting new records to a Bound DataGridView

Apologies if this has already been asked. If so, I am unable to find a simple solution. I am trying to allow a user to copy/paste multiple records in a DataGridView (the in memory copy of the data, to be saved later when the user clicks the save button) and cannot find anything that works. It probably is because there is something I do not understand about all of this.
I set up a standard edit form with Visual Studio's drag/table into a form, so it's using a BindingSource control and all the other controls that come with doing that. It works just fine when manually entering something in the new row one by one, so it seems to be set up correctly, but when it comes to adding a record (or multiples) using code, nothing seems to work.
I tried a few things as outline in the code below. Could someone please at least steer me in the right direction? It cannot be that difficult to paste multiple records.
I run this when the user presses Control-V (the clipboard correctly holds the delimited strings):
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item(1) = Items(0)
drv.Item(2) = Items(1)
drv.Item(3) = Items(2)
drv.Item(4) = Items(3)
'Error on next line : Cannot add external objects to this list.
AdjustmentsBindingSource.Add(drv)
Next
End If
End Sub
EDIT
(the bindingsource is bound to a dataadapter, which is bound to a table in an mdb file, if that helps understand)
I adjusted the inner part of the code to this:
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
It kinda works, but it also adds a blank row before the 2 new rows. Not sure where that is coming from, as I have even included your RowHasData() routine...
I would think that “attemp3” SHOULD work, however, it is unclear “what” the AdjustmentsBindingSource’s DataSource is. Is it a List<T> or DataTable?
If I set the BinngSource.DataSource to a DataTable, then attempt 3 appears to work. Below is an example that worked.
Private Sub PasteClipboard2()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
Dim drv As DataRowView = AdjustmentsBindingSource.AddNew()
drv.Item("FontName") = Items(0)
drv.Item("FontSize") = Items(1)
drv.Item("LetterCombo") = Items(2)
drv.Item("Adjustment") = Items(3)
drv.Item("HorV") = Items(4)
End If
Next
End If
End Sub
This appears to work in my tests. I added a small function (RowHasData) to avoid malformed strings causing problems. It simply checks the size (at least 5 items) and also checks to make sure a row actually has “some” data. If a row is just empty strings, then it is ignored.
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
I am guessing it would be just as easy to add the new rows “directly” to the BindingSource’s DataSource. In the example below, the code is adding the row “directly” to the DataTable that is used as a DataSource to the BindingSource. I am confident you could do the same thing with a List<T> by simply adding a new object to the list. Below is a complete example using a BindingSource and a DataTable. This simply adds the rows to the bottom of the table.
Dim gridTable1 As DataTable
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
PasteClipboard()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
gridTable1 = GetTable()
FillTable(gridTable1)
AdjustmentsBindingSource.DataSource = gridTable1
AdjustmentsDataGridView.DataSource = AdjustmentsBindingSource
End Sub
Private Function GetTable() As DataTable
Dim dt = New DataTable()
dt.Columns.Add("FontName", GetType(String))
dt.Columns.Add("FontSize", GetType(String))
dt.Columns.Add("LetterCombo", GetType(String))
dt.Columns.Add("Adjustment", GetType(String))
dt.Columns.Add("HorV", GetType(String))
Return dt
End Function
Private Sub FillTable(dt As DataTable)
For index = 1 To 10
dt.Rows.Add("Name_" + index.ToString(), "Size_" + index.ToString(), "Combo_" + index.ToString(), "Adjust_" + index.ToString(), "HorV_" + index.ToString())
Next
End Sub
Private Sub PasteClipboard()
If Clipboard.ContainsText Then
Dim sLines() As String = Clipboard.GetText.Split(vbCrLf)
Try
Dim dataRow As DataRow
For Each sLine As String In sLines
Dim Items() As String = sLine.Split(vbTab)
If (RowHasData(Items)) Then
dataRow = gridTable1.NewRow()
dataRow("FontName") = Items(0)
dataRow("FontSize") = Items(1)
dataRow("LetterCombo") = Items(2)
dataRow("Adjustment") = Items(3)
dataRow("HorV") = Items(4)
gridTable1.Rows.Add(dataRow)
End If
Next
Catch ex As Exception
MessageBox.Show("Error: " + ex.Message)
End Try
End If
End Sub
Private Function RowHasData(items As String())
If (items.Count >= 5) Then
For Each item In items
If (item <> "") Then Return True
Next
End If
Return False
End Function
Hope the code helps…
Last but important, I am only guessing that you may have not “TESTED” the different ways users can “SELECT” data and “how” other applications “copy” that selected data. My previous tests using the WIN-OS “Clipboard” can sometimes give unexpected results. Example, if the user selects multiple items using the ”Ctrl” key to “ADD” to the selection, extra rows appeared in the Clipboard if the selection was not contiguous. My important point is that using the OS clipboard is quirky IMHO. I recommend LOTS of testing on the “different” ways the user can select the data. If this is not an issue then the code above should work.

calling a VB.net Function for SQL recordset

Beginner here
I have the following code which I would like to call using a button called findCustomerBTN
Public Function Execute(ByVal sqlQuery As String) As ADODB.Recordset
If SecuritySSPIchkbx.Checked Then
chk = "TRUE"
Else chk = "FALSE"
End If
builder.DataSource = ServerBox.Text
builder.InitialCatalog = DatabaseBox.Text
builder.UserID = Username.Text
builder.Password = Password.Text
builder.IntegratedSecurity = chk
MessageBox.Show(builder.ConnectionString)
Using sqlConnection1 As New SqlConnection(builder.ConnectionString)
sqlConnection1.Open()
Try
command = New SqlCommand(sqlQuery, sqlConnection1)
adapter.SelectCommand = command
adapter.Fill(ds, "Create DataView")
adapter.Dispose()
command.Dispose()
sqlConnection1.Close()
dv = ds.Tables(0).DefaultView
DataGridView1.DataSource = dv
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Using
End Function
How do I call this function:
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles
sqlquery = "Select * from customers where name = 'Smith'"
call function?
End Sub
I have googled but I can't wrap my head around how a function works any pointers to help me understand would be great thanks
In VB, a function is a block of code that returns a value. Your code does not return a value, and the type of query execution you're carrying out will never return an ADODB.RecordSet - that's an ancient technology remeniscent of the VB6 era, and you're using a much more modern data access strategy (ADO.NET, DataTables and DataAdapter) though it's not the latest and greatest.
To offer a run through of your code, and the other issues it has:
Execute is a pretty bland name - go for something more specific, just incase you run the wrong execute and some poor prisoner ends up in front of the firing squad
Your function takes an sql string as a parameter to run, but then overwrites it with a fixed string, so there isn't much point offering it as a parameter in the first place. I could call Execute("SELECT * FROM kittens") expecting to get some cute data back, and all I get is the same old customer
Avoid calling MessageBox.Show in any code that shoudl reasonably be expected to run quietly and repeatedly, otherwise the user is going to get hella annoyed. If youre putting this here for debugging purposes, learn how the visual studio debugger works instead
Your code runs an sql query and assigns the resulting data table data to the datasource of a grid, so that the grid will show the data. There's absolutely no need for this code to be a function (and in c# it wouldnt even compile because it doesn't return a value
What are functions? What do they do? They take some inuts and return some outputs:
Public Function AddTheseTwo(a as Integer, b as Integer) As Integer
Return a + b
End Function
They are called like this:
Dim sum = AddTheseTwo(2, 3)
I.e. you give the name of the function and the input values, which can be variables, and store the result (usually, because you want to use it). Here's a code of yours that is a sub - a block of code that doesn't return a value
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles
Execute("Select * from customers where name = 'Smith'")
End Sub
It's not linked to your button click, because there's nothing afte rthe Handles keyword. It should be something like Handles findCustomerBTN.Click. You can mash that button all day and nothing will happen
It called Execute but didn't store the return value (because it didn't need to, because Execute doesn't return anything, so Execute should have been declared as a sub, not a function)
Edit:
You mentioned you want the function to return a datatable:
Public Function GetDataTable(ByVal sqlQuery As String) As DataTable 'need to Imports System.Data if you haven't already
If SecuritySSPIchkbx.Checked Then
chk = "TRUE"
Else chk = "FALSE"
End If
'better to declare builder in this function, not elsewhere
builder.DataSource = ServerBox.Text
builder.InitialCatalog = DatabaseBox.Text
builder.UserID = Username.Text
builder.Password = Password.Text
builder.IntegratedSecurity = chk
MessageBox.Show(builder.ConnectionString)
Using sqlConnection1 As New SqlConnection(builder.ConnectionString)
sqlConnection1.Open()
Try
'note: better to declare adapter and command in this function too
command = New SqlCommand(sqlQuery, sqlConnection1)
adapter.SelectCommand = command
Dim dt as New DataTable
adapter.Fill(dt)
adapter.Dispose()
command.Dispose()
sqlConnection1.Close()
Return dt
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Using
Return Nothing 'a function has to return something from all possible code paths, even if it's Nothing :)
End Function
And then you call it like this, perhaps:
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles whateverbutton.Click
'you can set a datatable as a datasource, doesn't have to be the datatable.defaultview
myDataGRidView.DataSource = GetDataTable("Select * from customers where name = 'Smith'")
End Sub
I recommend you turn on the options for Strict/Explicit etc, to encourage better coding practices. By default VB is quite loose, letting you use variables that havent been declared (autodeclaring variable names that are a typo of another variable name etc), automatically returning Nothing for you from functions etc - it's these little auto's that will later lead to bugs and frsutrations. Computer programming si a precise art; turn on all options to force yourself to be as precise as possible
You can call Execute function using this code:
Private Sub findCustomerBTN_Click(sender As Object, e As EventArgs) Handles
Execute("Select * from customers where name = 'Smith'")
End Sub
You also have to remove this line from Execute function:
sqlQuery = ("select * from ac_billbook where ref = '900123'")
Please follow Steve suggestion to read a good book about programming in VB.NET.

Why is this object function exiting?

A very simple issue im having trying to return a dataset from a VB.NET object function.
The following shows my function that is currently exiting from the function as soon as the SQL query is run and just before the new object connection is created.
The edit form is called here:
edit.Show()
Within the edit form, the following is run to to retrieve the details of the selected data in the database fro a retrieved datatset of the 'editEntry' method based on the ID set at the constructor.
Private Sub edit_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim editDetails As New DBHandler(ID)
Dim returnedDetails As New DataSet
returnedDetails = editDetails.editEntry()
Dim nameReturned As Object = returnedDetails.Tables("editedTable").Rows(0)(1)
Dim firstNameEdit As String = nameReturned.ToString()
TextBox1.Text = firstNameEdit
This is the function where the problem is occuring. Nothing is being returned from the query
Constructor where the ID is set:
Public Sub New(ByVal ID As Integer)
IDofFault = ID
End Sub
The function of the class:
Public Function editEntry() As DataSet
Dim editDataSet As New DataSet
Dim editSql As String = "SELECT * FROM duraGadget WHERE _id = " + IDofFault + ""
'Exiting from the function here
Dim connectionEdit As New OleDbConnection(conString)
Dim editAdapter As New OleDbDataAdapter(editSql, connectionEdit)
connectionEdit.Open()
editAdapter.Fill(editDataSet, "editedTable")
connectionEdit.Close()
Return editDataSet
End Function
There is no error it simply exits from the function and im not sure why.
You could be receiving an exception and your visual studio debug settings are not configured to stop you on those types of exceptions.
Wrap the contents of the EditEntry function in a Try / Catch block, and put a break point inside the catch. See if that triggers and look at the exception details for more info on what occurred.
Very silly error this was guys. I simply stored the ID value as a sting...then tried to pass it as an Integer to the constructor...the result?.....Conversion exception.

datagridview not updating to bound datasource

I am trying to get the datagridview to update when I update the datasource and I'm having no luck whatsoever.
Here is my binding:
Private _dgbNews As SortableBindingList(Of SalesMessageRecord)
Private Sub SalesMessageScreen_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
'_dgbNews.RaiseListChangedEvents = True
_dgbNews = AllNews()
BindingSource1.DataSource = AllNews()
DataGridView1.DataSource = BindingSource1
MassageDemRows()
End Sub
this is AllNews():
Public Function AllNews() As SortableBindingList(Of SalesMessageRecord)
Dim sm = New SortableBindingList(Of SalesMessageRecord)
Dim allnewsitems = News.GetAllNewsItems(Configuration.CompanyID).ToList()
For Each allnewz As News In allnewsitems
Dim smr = New SalesMessageRecord
smr.Body = allnewz.NewsBody
smr.CorporationId = CType(allnewz.CorporationId, Guid)
smr.Expiration = allnewz.Expiration
smr.IsActive = allnewz.IsActive
smr.NewsId = allnewz.NewsId
smr.Title = allnewz.NewsTitle
smr.SortOrder = allnewz.OrderNumber
smr.TokenId = allnewz.TokenId
smr.IsNew = False
sm.Add(smr)
Next
Return sm
End Function
And this is where I'm trying to update it:
Private Sub button_Save_Click(sender As System.Object, e As System.EventArgs) Handles button_Save.Click
If _currentRow < 0 Then
Return
End If
_dgbNews(_currentRow).Expiration = datetimepicker_ExpirationDate.Value
_dgbNews(_currentRow).SortOrder = CInt(numericupdown_SortNumber.Value)
_dgbNews(_currentRow).IsActive = checkbox_Active.Checked
_dgbNews(_currentRow).Body = richtextbox_Body.Text
_dgbNews(_currentRow).Title = textbox_Title.Text
DataGridView1.Refresh()
News.UpdateNewsRecord(_dgbNews(_currentRow).NewsId,
_dgbNews(_currentRow).Expiration,
_dgbNews(_currentRow).SortOrder,
_dgbNews(_currentRow).IsActive,
_dgbNews(_currentRow).Body,
_dgbNews(_currentRow).Title)
End Sub
The database is updating without issue but the datagridview won't update.
Right, I'll take a crack at this. Bindingsources are really useful when used together with a DGV. However, they tend to be aweful in the way that they give no information what so ever why they aren't working.
First of I would say that your binding source has the wrong Datasource.
_dgbNews = AllNews()
BindingSource1.DataSource = AllNews()
DataGridView1.DataSource = BindingSource1
As you can se you have AllNews() as Datasource. But you add stuff to _dgbNews and expect Allnews() to change. Protip, it doesn't. If you were to set the DataSource to this:
BindingSource1.DataSource = _dgbNews
Then atleast you should expect some change when the list is updated. Now this is what you actually do miss. In button save click you add an item to the list, this is now fine if you done the above. But wait, the datasource changed and nothing happened. This is because you don't tell the datagridview to change. Make the Bindingsource tell everything it's connected to to update. With this:
BindingSource1.ResetBindings(True)
This is better than DGV.Refresh (which might work now) since your bindingsource could be attached to other things (I know it isn't, but for future reference).
Try this and well see if it will go better.
Had similiar issue with DataGridView not displaying any data from my datasource, figured out that my binding class properties were regular int or string instead of being declared with get set.
changed from
int RedeemID;
to
public int RedeemID { get; set; }
TRY THIS:
(AFTER EXECUTING THE UPDATE COMMAND FROM THE DATA ADAPTER)
GRIDVIEW_NAME.DATABIND()
Me.TableAdapter1.Fill(Me.DataSet1.Table1)