I have searched on the internet for the answer to this question with no luck. I have a model that exists in the database created with entity framework and is accessed via db (an instance of my DBContext class).
Function Details(id As Integer) As ViewResult
Dim bill As Bill = db.Bills.Find(id)
Return View(bill)
End Function
The model for the bill class is defined as:
Public Class Bill
Public Property BillId() As Integer
Public Property CustomerId() As String
Public Property BillStatus() As String
End Class
Assuming then that in the function mentioned first I was passed the CustomerId rather than the primary key of this model (BillId), how would I use this to create a 'Dim bill As Bill' that included only bills with that customerId.
As in the example above where Bills was filtered with .Find(id) - which only looks at the primary key - I want to be able to use a similar method to .Find() but on a non key field: CustomerId in this case.
Or if you are sure there will always only be one entity matching that CustomerId you can use Single.
VB .NET
Dim myBill = db.Bills.Single(Function(x) x.CustomerId = cId);
C# .NET
var myBill = db.Bills.Single(x => x.CustomerId == cId);
Note, this will throw an exception if exactly 1 is not found.
Use .NET's version of filter (Where)
Dim bills = db.Bills.Where(Function(x) x.CustomerId = cId)
or
Dim bills = From i in db.Bills
Where i.CustomerId = cId
Select i
EDIT: Per Scott's comment call FirstOrDefault() to return only one result. If none exist, null will be returned.
To do that.
Dim bill = (From i in db.Bills
Where i.CustomerId = cId
Select i).FirstOrDefault()
If Not IsDBNull(bill) Then
/*Do Stuff*/
End If
Related
I am using Linq statement as per below to find student name by its ID. It works fine. However there are cases where there is no student with given ID. In those cases an error is thrown "Object reference not set to an instance of an object."
How to efficiently deal with this problem?
Dim Name As String = Students.FirstOrDefault(Function(Student) Student.ID = "NO00007").Name
If you are satisfied with Name being null if there is no matching student, you can use the null conditional operator for member access:
Dim Name As String = Students.FirstOrDefault(Function(Student) Student.ID = "NO00007")?.Name
As usually answer is "it depend" - it depend on how you will use result you will get
If you want get some "default"/empty string instead of name when collection doesn't contain item
Dim result = Students.Where(Function(student) student.ID = "NO00007").
Select(Function(student) student.Name).
DefaultIfEmpty(String.Empty).
First()
Almost same approach if you want to get some "empty" object instead of null
Dim resultStudent = Students.Where(Function(student) student.ID = "NO00007").
DefaultIfEmpty(New Student With { .Name = "empty" }).
First()
From performance point of view approach above are same as FirstOrDefault - but provide little bid better readability(subjective of course)
So I have an application in VB.net that is pulling data from a table and inserting it into an arraylist to be used later. What I want to do is before adding the object to the arraylist, I want to check that arraylist to see if the object exists, but I want to be able to check based off a particular property of that object.
Here is an example of what I am talking about:
Lets say Im pulling info from a table with the following columns:
InvoiceNo|DateCharged|Quantity|TotalCharge
I have a SQL statement that pulls info from a table and then I use a data reader to go through the info. My Code looks somewhat like this:
Dim dbobjM As New clsDbobjManual()
If dbobjM.Exec_SQL_DR("SELECT InvoiceNo, DateCharged, Quantity, TotalCharges From Invoices") = 0 Then
If dbobjM.DataReader.HasRows Then
Dim invoicelist As New ArrayList(5000)
Dim invoiceno As String = String.Empty
Do While dbobjM.DataReader.Read()
invoicelist.Add(New Invoice(dbobjM.DataReader.GetInt32(0), dbobjM.DataReader.Value(1), dbobjM.DataReader.GetInt32(2), dbobjM.DataReader.GetFloat(3)))
Loop
End If
End if
(Exec_SQL_DR is a function in the clsDbobjManual class that check to make sure the SQL is in the proper syntax first and checks that records are returned otherwise it returns an error)
Basically what I want to do is before I add a new object to the arraylist I want to check if an object already exists in the list where the InvoiceNo is a particular value, or the value pulled from the table each time to make sure there is no duplicates. I want one object in the list for each InvoiceNo.
Im looking for something like:
If Not invoicelist.Contains(Object where InvoiceNo = dbobjM.DataReader.GetInt32(0)) Then
invoicelist.Add
End If
But I cant seem to find what I need, any help is greatly appreciated
There is no need to use the outdated ArrayList: a List will serve you better. Please see ArrayList vs List<> in C# if you need reasons - the advantages for a list apply to VB.NET too.
Without seeing your clsDbobjManual or Invoice classes, I ended up writing the minimal code to do what you're after, which is basically the check for invoices.Any(Function(i) i.InvoiceNo = inv.InvoiceNo), which you can do if you have the data in a List(Of Invoice).
Please note that I assumed that the appropriate data types have been used in the database - you should use the Decimal type for money as otherwise you can end up with significant rounding errors, and a date should be stored as DateTime, not as a string.
Imports System.Data.SqlClient
Module Module1
Class Invoice
Property InvoiceNo As Integer
Property DateCharged As DateTime
Property Quantity As Integer
Property TotalCharges As Decimal
Sub New()
' empty constructor
End Sub
Sub New(invoiceNo As Integer, dateCharged As DateTime, quantity As Integer, totalCharges As Decimal)
Me.InvoiceNo = invoiceNo
Me.DateCharged = dateCharged
Me.Quantity = quantity
Me.TotalCharges = totalCharges
End Sub
End Class
Function LoadData() As List(Of Invoice)
Dim invoices As New List(Of Invoice)
Dim connStr As String = "your connection string"
Dim sql = "SELECT InvoiceNo, DateCharged, Quantity, TotalCharges From Invoices"
Using sqlConn As New SqlConnection(connStr)
Using sqlCmd As New SqlCommand(sql, sqlConn)
Dim reader As SqlDataReader = sqlCmd.ExecuteReader()
While reader.Read()
Dim inv As New Invoice(reader.GetInt32(0), reader.GetDateTime(1), reader.GetInt32(2), reader.GetDecimal(3))
If Not (invoices.Any(Function(i) i.InvoiceNo = inv.InvoiceNo)) Then
invoices.Add(inv)
Else
' there is a duplicate invoice number
End If
End While
End Using
End Using
Return invoices
End Function
Sub Main()
Dim uniqueInvoices As List(Of Invoice) = LoadData()
' uniqueInvoices now contains the data
End Sub
End Module
If you had a lot of invoice entries to go through, you would likely be better off writing an SQL query to do that.
If you actually just want to find duplicate invoice numbers, you could use the SQL
SELECT [InvoiceNo]
FROM testTable
GROUP BY [InvoiceNo]
HAVING COUNT([InvoiceNo]) > 1
Finally, please ensure that you are using Option Strict On so that you don't make accidental data type errors - they can drastically slow down your program and lead to erroneous results.
You can use linq to select the objects that matches your condition.
Dim result = (From invoiceitem As Invoice
In invoicelist
Where invoiceitem.InvoiceNo = dbobjM.DataReader.GetInt32(0)
Select invoiceitem).ToList()
If Not result.Count > 0 Then
invoicelist.Add(New Invoice(dbobjM.DataReader.GetInt32(0), dbobjM.DataReader.Value(1), dbobjM.DataReader.GetInt32(2), dbobjM.DataReader.GetFloat(3)))
End If
I have a list of objects, with a property customerId which is a nullable System.Guid property.
I also have a list of id's of type System.Guid, I also added a Guid.Empty value to this list.
I try to do a join on both, but the objects with empty guids aren't returned.
Dim dos = (From d In documents Join c In allowedCustomers On c Equals If(d.CustomerGuid = Nothing, System.Guid.Empty, d.CustomerGuid) Select d).Skip(10 * (pageNr - 1)).Take(10).ToList
What is wrong? Is there another way to do this in a better way?
You are using d.CustomerGuid = Nothing but you have to use d.CustomerGuid Is Nothing.
Try this approach which uses the VB.NET's null-coalescing operator.
Dim query = From doc In documents
Join custID In allowedCustomers
On If(doc.CustomerGuid, Guid.Empty) Equals custID
Skip 10 * (pageNr - 1)
Take 10
Select doc
Dim docList = query.ToList()
Note that you can increase readability with multiple lines, also, VB.NET's query syntax is powerful than C#, so you can use Skip and Take in the query.
I have got a bit of slack project time so I decided to star in a production of micro optimisation theatre. The app I’m working on is going to be run on some quite low performance tablets so I was looking for ways to speed things up. Given that I’m using LINQ to Entities I looked into precompiled queries to boast performance and so came up with this simple one to return a list of contacts for a given company
Public ReadOnly pContacts_list_query As Func(Of SpiraxDDWEntities, Integer, IQueryable(Of tblContacts)) = _
CompiledQuery.Compile(Of SpiraxDDWEntities, Integer, IQueryable(Of tblContacts))(Function(ctx As SpiraxDDWEntities, pCompany_ABN As Integer) _
From Contact_data In ctx.tblContacts Where Contact_data.AccountNumber = pCompany_ABN
)
Now that’s fine as its just one table so the IQueryable type can be the table name. My question is what if I wanted to precompile a query with joins? For example this one
Dim Quote_QRY = From Quote_data In linEntities.tblQuote
Join Quote_value_data In linEntities.tblQuoteValue On Quote_data.ID Equals Quote_value_data.QuoteID
Join Quote_status_data In linEntities.tblQuoteStatus On Quote_data.Status Equals Quote_status_data.Abbreviation
Where Quote_data.AccountNo = Me.txtCompany_ABN.Text
Select Quote_data.ID, Status = Quote_status_data.Description, Quote_data.Contact, Quote_data.Project, Quote_value_data.QuoteValue
How would I go about that?
Thanks
The join here isn't the issue. The issue is projecting into an anonymous type. In this case, you need to create a named class and project into it in your select clause:
Public Class Quote
Public Property ID As String
Public Property Status As String
Public Property Contact As String
Public Property Project As String
Public Property QuoteValue As String
End Class
Public ReadOnly Quote_query As Func(Of SpiraxDDWEntities, Integer, IQueryable(Of Quotes)) = _
CompiledQuery.Compile(Of SpiraxDDWEntities, Integer, IQueryable(Of Quotes))(Function(ctx As SpiraxDDWEntities, pCompany_ABN As Integer)
From Quote_data In linEntities.tblQuote
Join Quote_value_data In linEntities.tblQuoteValue On Quote_data.ID Equals Quote_value_data.QuoteID
Join Quote_status_data In linEntities.tblQuoteStatus On Quote_data.Status Equals Quote_status_data.Abbreviation
Where Quote_data.AccountNo = Me.txtCompany_ABN.Text
Select New Quote With {
.ID = Quote_data.ID,
.Status = Quote_status_data.Description,
.Contact = Quote_data.Contact,
.Project = Quote_data.Project,
.QuoteValue = Quote_value_data.QuoteValue
}
One additional caveat: I'm not sure off the top of my head if Compiled Query requires that the resulting type be a mapped entity type or not. If so, you may need to alter the EDMX to make EF think that the type is valid. Alternatively, you may want to consider using a Defining query in the CSDL.
I'm trying to do the following LINQ grouping, which works in the debugger (the results are populated in the GroupedOrders object. But VS 2008 gives me the following error at design time...
Name 'x' is not declared
Dim GroupedOrders = (From m In thisConsultant.orders _
Group m By Key = m.commCode Into Group _
Select commCode = Key, orders = Group)
For Each x In GroupedOrders
Next
Public Structure consultantDetail
Public orders As List(Of orderDetail)
End Structure
Public Structure orderDetail
Public transactionID As Integer
Public qualifyingVolume As Decimal
Public commissionableVolume As Decimal
Public sponsorID As Integer
Public orderDate As DateTime
Public commCode As String
Public commPercentage As Decimal
Public discountPercent As Decimal
End Structure
Do you have Option Infer On?
My guess is that you have Option Strict On and Option Infer Off. To check these settings:
Right-click on your project in the solution explorer
Select Properties
Select the Compile tab on the left
try to surround the Linq query with try-catch. Sometimes, there are error that are not catch directly by VS2008.