|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThis article provides an introduction to employing LINQ to SQL within a Windows Forms application; the article will address the incorporation of LINQ to SQL into a win forms project, how to use LINQ to SQL to select, insert, update, and delete data, and how to use LINQ to SQL to execute stored procedures. Select query examples will demonstrate ordering, filtering, aggregation, returning typed lists, returning single objects and values, and how to query across entity sets (which are essentially related tables associated by foreign keys).
Figure 1: Application Main Form
The demonstration project included with the article is a simple win forms application; this example contains a datagridview control and a menu; the menu is used to execute each example query contained in the demonstration. The application provides the following functionality:
There is a great deal more that one can do with LINQ to SQL that is not contained in this demonstration however, the demonstration was geared towards the mechanics of performing the most typical types of queries that might be required within a data driven application. LINQ to SQL StatementsThis section will discuss some of the common techniques used in LINQ to SQL statement construction. In a nutshell, LINQ to SQL provides the developer with the means to conduct queries against a relational database through a LINQ to SQL database model and related data context. Data ContextThe data context provides the mapping of all entities (essentially tables) to the database. It is through the data context that the application can query the database and it is through the data context that changes to the database can be executed. Anatomy of LINQ to SQL StatementsExample 1 – A Simple SelectThis is an example of a very simple LINQ to SQL statement: Public Sub SampleQ1()
Dim dc As New NwindDataClassesDataContext()
Dim q = _
From a In dc.GetTable(Of Order)() _
Select a
DataGridView1.DataSource = q
End Sub
In the example, an instance of the data context is created and then a query is formed to get all of the values in the table; once the query runs, the result is used as the data source of a datagridview control and the results are displayed in the grid: Dim q = _
From a In dc.GetTable(Of Order)() _
Select a
Since the Public Sub SampleQ2()
Dim dc As New NwindDataClassesDataContext()
DataGridView1.DataSource = dc.GetTable(Of Order)()
End Sub
If you were to create a project, add either bit of code to a method and run it, the results would look like this:
Figure 2: Query Results
Example 2 – Select with a Where ClauseThe next example shows a LINQ to SQL query that incorporates a where clause. In this example, we get a data context to work with first, and then query the Orders table to find a customer with the customer ID of starts with the letter “A”, the results are then bound to a datagridview control. Public Sub SimpleQ3()
Dim dc As New NwindDataClassesDataContext()
Dim q = _
From a In dc.GetTable(Of Order)() _
Where a.CustomerID.StartsWith("A") _
Select a
DataGridView1.DataSource = q
End Sub
If you were to run the query, the results would appear as follows:
Figure 3: Query Results
Example 3 – Select with a Where ClauseIn a slight variation to the previous query, this example looks for an exact match in its where clause: Public Sub SimpleQ4()
Dim dc As New NwindDataClassesDataContext()
Dim q = _
From a In dc.GetTable(Of Order)() _
Where a.CustomerID = "VINET" _
Select a
DataGridView1.DataSource = q
End Sub
Running this code will display this result:
Figure 4: Query Results
Example 4 – Generating an Ordered ListIn this query, the list of orders is ordered (using “orderby a.OrderDate ascending”): Public Sub SimpleQ5()
Dim dc As New NwindDataClassesDataContext()
Dim q = _
From a In dc.GetTable(Of Order)() _
Where a.CustomerID.StartsWith("A") _
Order By a.OrderDate Ascending _
Select a
DataGridView1.DataSource = q
End Sub
Figure 5: Query Results
Example 5 – Working with a Custom TypeIn this example a query is built to return a list of a custom type (CustomerOrderResult). Public Sub GetCustomerOrder()
Dim dc As New NwindDataClassesDataContext()
Dim q = (From orders In dc.Orders _
From orderDetails In orders.Order_Details _
From prods In dc.Products _
Where ((orderDetails.OrderID = orders.OrderID) And _
(prods.ProductID = orderDetails.ProductID) And _
(orders.EmployeeID = 1)) _
Order By (orders.ShipCountry) _
Select New CustomerOrderResult With { _
.CustomerID = orders.CustomerID, _
.CustomerContactName = orders.Customer.ContactName, _
.CustomerCountry = orders.Customer.Country, _
.OrderDate = orders.OrderDate, _
.EmployeeID = orders.Employee.EmployeeID, _
.EmployeeFirstName = orders.Employee.FirstName, _
.EmployeeLastName = orders.Employee.LastName, _
.ProductName = prods.ProductName _
}).ToList()
dataGridView1.DataSource = q
End Sub
The “select new” in the query defines the result type and then sets each of the properties in the type to a value returned by the query. At the end of the query, the output is converted to a List of the The displayed results of running the query are:
Figure 6: Query Results
The Public Class CustomerOrderResult
Private mCustomerID As String
Private mCustomerContactName As String
Private mCustomerCountry As String
Private mOrderDate As Nullable(Of DateTime)
Private mEmployeeID As Int32
Private mEmployeeFirstName As String
Private mEmployeeLastName As String
Private mProductName As String
Public Property CustomerID() As String
Get
Return mCustomerID
End Get
Set(ByVal value As String)
mCustomerID = value
End Set
End Property
Public Property CustomerContactName() As String
Get
Return mCustomerContactName
End Get
Set(ByVal value As String)
mCustomerContactName = value
End Set
End Property
Public Property CustomerCountry() As String
Get
Return mCustomerCountry
End Get
Set(ByVal value As String)
mCustomerCountry = value
End Set
End Property
Public Property OrderDate() As Nullable(Of DateTime)
Get
Return mOrderDate
End Get
Set(ByVal value As Nullable(Of DateTime))
mOrderDate = value
End Set
End Property
Public Property EmployeeID() As Int32
Get
Return mEmployeeID
End Get
Set(ByVal value As Int32)
mEmployeeID = value
End Set
End Property
Public Property EmployeeFirstName() As String
Get
Return mEmployeeFirstName
End Get
Set(ByVal value As String)
mEmployeeFirstName = value
End Set
End Property
Public Property EmployeeLastName() As String
Get
Return mEmployeeLastName
End Get
Set(ByVal value As String)
mEmployeeLastName = value
End Set
End Property
Public Property ProductName() As String
Get
Return mProductName
End Get
Set(ByVal value As String)
mProductName = value
End Set
End Property
End Class
Example 6 – Searching an Existing List (Of Type) Using LINQ to ObjectsIn this example, a typed list is created (as in the previous example) using LINQ to SQL, populated, and then the returned typed list is queried using LINQ to Objects. In this case, the query includes a where clause that only returns matches were the customer ID begins is equal to “RICAR”: Public Sub GetCustomerOrder2()
Dim dc As New NwindDataClassesDataContext()
Dim q = (From orders In dc.Orders _
From orderDetails In orders.Order_Details _
From prods In dc.Products _
Where ((orderDetails.OrderID = orders.OrderID) And _
(prods.ProductID = orderDetails.ProductID) And _
(orders.EmployeeID = 1)) _
Order By (orders.ShipCountry) _
Select New CustomerOrderResult With { _
.CustomerID = orders.CustomerID, _
.CustomerContactName = orders.Customer.ContactName, _
.CustomerCountry = orders.Customer.Country, _
.OrderDate = orders.OrderDate, _
.EmployeeID = orders.Employee.EmployeeID, _
.EmployeeFirstName = orders.Employee.FirstName, _
.EmployeeLastName = orders.Employee.LastName, _
.ProductName = prods.ProductName _
}).ToList()
Dim matches = (From c In q _
Where c.CustomerID = "RICAR" _
Select c).ToList()
DataGridView1.DataSource = matches
End Sub
Figure 7: Query Results
Example 7 – Searching an Existing List (Of Type) Using LINQ to Objects and Returning a Single ResultIn this example, a typed list is created (as in the previous example), populated, and then queried using LINQ to Objects. In this case, returns a single result of type “Parts”: Public Sub GetEmployeeLastName()
Dim dc As New NwindDataClassesDataContext()
Dim query = (From orders In dc.GetTable(Of Order)() _
Select orders)
Dim matches = (From c In query _
Where c.OrderID = 10248 _
Select c.Employee.LastName).SingleOrDefault()
MessageBox.Show(matches, "Employee 10248 - Last Name")
End Sub
The results are displayed as:
Figure 8: Returning a Single Result
The preceding examples were intended to provide a simple overview as to how to conduct some basic queries against collections using LINQ to SQL and LINQ to Objects; there are certainly a great number of more complex operations that can be executed using similar procedures (groups and aggregation, joins, etc.) however, the examples provided are representative of some of the more common types of queries. Getting StartedThere is a single solution included with this download, the solution contains a Win Forms project called “L2S_Northwind_VB”; this project contains one form (the main form used to display the results of the demonstration queries (frmMain) , and LINQ to SQL database model (NorthwindDataClasses.dbml) along with the models designer code and layout file, and a class entitled, “Accessor” which contains code used to perform the LINQ to SQL queries used in the demonstration. If you open the attached project into Visual Studio 2008, you should see the following in the solution explorer:
Figure 9: Solution Explorer
The demonstration relies upon an instance of the Northwind database running in SQL Server 2005. The database can be downloaded from here; the database was created for SQL Server 2000 but you can install the database and attach to it using a local instance of SQL Server 2005. Once the database is installed, you will want to update the connection string found in the “MyProject” settings. Open the settings and click on the button (showing an ellipsis) to set the connection string.
Figure 10: Settings and the Connection String
Figure 11: Adding LINQ to SQL Classes to a Project
When starting from scratch, in order to add LINQ to SQL to a project, open the “Add New Item” dialog and select the LINQ to SQL Classes item (Figure 11); name the data classes and then select the “Add” button to add the item to the project. Once set, set the connection string for the data classes, then open the server explorer to drag tables and stored procedures onto the designer (dragging the tables into the left hand side of the workspace and stored procedures into the right hand side of the workspace (Figure 12)). Once that is done, build the project to generate the LINQ to SQL code.
Figure 12: Model of Northwind Data Class (tables on left, stored procedures on right)
This project is intended for Visual Studio 2008 with the .NET framework version 3.5. Code: Accessor.vbThe The class begins with the normal and default imports: Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Data.Linq
Imports System.Text
The next section contains the class declaration. ''' <summary>
''' This class defines functions used to
''' select, insert, update, and delete data
''' using LINQ to SQL and the defined
''' data context
''' </summary>
''' <remarks></remarks>
Public Class Accessor
Next up is a region containing all of the functions used to return full tables from the database through the data context. All of the functions work essentially the same way; the data context includes a function called #Region "Full Table Queries"
' This section contains examples of
' pulling back entire tables from
' the database
''' <summary>
''' Returns the Full Employee Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetEmployeeTable() As _
System.Data.Linq.Table(Of Employee)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of Employee)()
End Function
''' <summary>
''' Returns the Full Shipper Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetShipperTable() As _
System.Data.Linq.Table(Of Shipper)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of Shipper)()
End Function
''' <summary>
''' Returns the Full Order Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetOrderTable() As _
System.Data.Linq.Table(Of Order)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of Order)()
End Function
''' <summary>
''' Returns the Full Employee Territory Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetEmployeeTerritoryTable() As _
System.Data.Linq.Table(Of EmployeeTerritory)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of EmployeeTerritory)()
End Function
''' <summary>
''' Returns the Full Territory Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetTerritoryTable() As _
System.Data.Linq.Table(Of Territory)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of Territory)()
End Function
''' <summary>
''' Returns the Full Region Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetRegionTable() As _
System.Data.Linq.Table(Of Region)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of Region)()
End Function
''' <summary>
''' Returns the Full Customer Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetCustomerTable() As _
System.Data.Linq.Table(Of Customer)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of Customer)()
End Function
''' <summary>
''' Returns the Full CustomerCustomerDemo Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetCustomerCustomerDemoTable() As _
System.Data.Linq.Table(Of CustomerCustomerDemo)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of CustomerCustomerDemo)()
End Function
''' <summary>
''' Returns the Full Customer Demographic Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetCustomerDemographicTable() As _
System.Data.Linq.Table(Of CustomerDemographic)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of CustomerDemographic)()
End Function
''' <summary>
''' Returns the Full Order_Detail Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetOrderDetailTable() As _
System.Data.Linq.Table(Of Order_Detail)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of Order_Detail)()
End Function
''' <summary>
''' Returns the Full Product Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetProductTable() As _
System.Data.Linq.Table(Of Product)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of Product)()
End Function
''' <summary>
''' Returns the Full Supplier Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetSupplierTable() As _
System.Data.Linq.Table(Of Supplier)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of Supplier)()
End Function
''' <summary>
''' Returns the Full Category Table
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetCategoryTable() As _
System.Data.Linq.Table(Of Category)
Dim dc As New NorthwindDataClassesDataContext()
Return dc.GetTable(Of Category)()
End Function
#End Region
That next region contained in the #Region "Queries"
' This region contains examples of some
' of the sorts of queries that can be
' executed using LINQ to SQL
''' <summary>
''' Example: Where Clause
''' Returns an employee where the
''' employee ID matches the value
''' passed in as empID
''' </summary>
''' <param name="empId"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetEmployeeById(ByVal empId As Integer) As Employee
' get the data context
Dim dc As New NorthwindDataClassesDataContext()
' get the first Employee with and employee ID
' matching the employee ID passed in as an
' argument to this function
Dim retVal = (From e In dc.GetTable(Of Employee)() _
Where (e.EmployeeID = empId) _
Select e).FirstOrDefault()
Return retVal
End Function
''' <summary>
''' Example: Select to a single returned object
''' using a Where Clause
'''
''' Returns the first matching order
''' </summary>
''' <param name="ordId"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetOrderById(ByVal ordId As Integer) As Order
' get the data context
Dim dc As New NorthwindDataClassesDataContext()
' return a single value from the orders table
' where the order Id match the ordId argument
' passed to this function
Dim retVal = (From ord In dc.GetTable(Of Order)() _
Where (ord.OrderID = ordId) _
Select ord).FirstOrDefault()
Return retVal
End Function
''' <summary>
''' Example: Select to a typed List
''' using a Where Clause
''' </summary>
''' <param name="ordId"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetOrdersById(ByVal ordId As Integer) As List(Of Order)
' get the context
Dim dc As New NorthwindDataClassesDataContext()
' get a list of Orders where the Order ID matches
' the ordId argument and return the collection as
' a list of type Order
Dim retVal = (From ord In dc.GetTable(Of Order)() _
Where (ord.OrderID = ordId) _
Select ord).ToList()
Return retVal
End Function
''' <summary>
''' Example: Return an ordered list
'''
''' Converts the returned value to a List
''' of type Employee; the list is ordered
''' by hire date
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function GetEmployeesByHireDate() As List(Of Employee)
' get the context
Dim dc As New NorthwindDataClassesDataContext()
' get the Employee table, order it by HireDate
' and return the result as a list of type Employee
Dim retVal = (From emp In dc.GetTable(Of Employee)() _
Order By emp.HireDate Ascending _
Select emp).ToList()
Return retVal
End Function
''' <summary>
''' This class is used to define the return type
''' for the next function - OrdersAndDetails
'''
''' When results are extracted from multiple tables
''' you can either return the results as anonymous
''' or as a type; this class defines the return
''' type used by OrdersAndDetails
''' </summary>
''' <remarks></remarks>
Public Class OrdersAndDetailsResult
Private mCustomerID As String
Private mOrderDate As Nullable(Of DateTime)
Private mRequiredDate As Nullable(Of DateTime)
Private mShipAddress As String
Private mShipCity As String
Private mShipCountry As String
Private mShipZip As String
Private mShippedTo As String
Private mOrderID As Integer
Private mNameOfProduct As String
Private mQtyPerUnit As String
Private mPrice As Nullable(Of Decimal)
Private mQtyOrdered As Int16
Private mDiscount As Single
Public Property CustomerID() As String
Get
Return mCustomerID
End Get
Set(ByVal value As String)
mCustomerID = value
End Set
End Property
Public Property OrderDate() As Nullable(Of DateTime)
Get
Return mOrderDate
End Get
Set(ByVal value As Nullable(Of DateTime))
mOrderDate = value
End Set
End Property
Public Property RequiredDate() As Nullable(Of DateTime)
Get
Return mRequiredDate
End Get
Set(ByVal value As Nullable(Of DateTime))
mRequiredDate = value
End Set
End Property
Public Property ShipAddress() As String
Get
Return mShipAddress
End Get
Set(ByVal value As String)
mShipAddress = value
End Set
End Property
Public Property ShipCity() As String
Get
Return mShipCity
End Get
Set(ByVal value As String)
mShipCity = value
End Set
End Property
Public Property ShipCountry() As String
Get
Return mShipCountry
End Get
Set(ByVal value As String)
mShipCountry = value
End Set
End Property
Public Property ShipZip() As String
Get
Return mShipZip
End Get
Set(ByVal value As String)
mShipZip = value
End Set
End Property
Public Property ShippedTo() As String
Get
Return mShippedTo
End Get
Set(ByVal value As String)
mShippedTo = value
End Set
End Property
Public Property OrderID() As Integer
Get
Return mOrderID
End Get
Set(ByVal value As Integer)
mOrderID = value
End Set
End Property
Public Property NameOfProduct() As String
Get
Return mNameOfProduct
End Get
Set(ByVal value As String)
mNameOfProduct = value
End Set
End Property
Public Property QtyPerUnit() As String
Get
Return mQtyPerUnit
End Get
Set(ByVal value As String)
mQtyPerUnit = value
End Set
End Property
Public Property Price() As Nullable(Of Decimal)
Get
Return mPrice
End Get
Set(ByVal value As Nullable(Of Decimal))
mPrice = value
End Set
End Property
Public Property QtyOrdered() As Int16
Get
Return mQtyOrdered
End Get
Set(ByVal value As Int16)
mQtyOrdered = value
End Set
End Property
Public Property Discount() As Single
Get
Return mDiscount
End Get
Set(ByVal value As Single)
mDiscount = value
End Set
End Property
End Class
''' <summary>
''' Example: Joins
''' Joining using the join keyword
'''
''' The values are set to each of the
''' properties contained in the
''' OrdersAndDetailsResult class
'''
''' The value returned is converted
''' to a list of the specified type
''' </summary>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function OrdersAndDetails() As List(Of OrdersAndDetailsResult)
' get the data context
Dim dc As New NorthwindDataClassesDataContext()
' join Orders on Order_Details, order the list
' by CustomerID and select the results into a new
' instance of OrdersAndDetailsResults, return the
' collection as a list of that type
Dim rtnVal = (From ords In dc.GetTable(Of Order)() _
Join dets In dc.GetTable(Of Order_Detail)() _
On ords.OrderID Equals dets.OrderID _
Order By ords.CustomerID Ascending _
Select New OrdersAndDetailsResult With { _
.CustomerID = ords.CustomerID, _
.OrderDate = ords.OrderDate, _
.RequiredDate = ords.RequiredDate, _
.ShipAddress = ords.ShipAddress, _
.ShipCity = ords.ShipCity, _
.ShipCountry = ords.ShipCountry, _
.ShipZip = ords.ShipPostalCode, _
.ShippedTo = ords.ShipName, _
.OrderID = ords.OrderID, _
.NameOfProduct = dets.Product.ProductName, _
.QtyPerUnit = dets.Product.QuantityPerUnit, _
.Price = dets.UnitPrice, _
.QtyOrdered = dets.Quantity, _
.Discount = dets.Discount}).ToList()
Return rtnVal
End Function
''' <summary>
''' Defined to support following function:
''' GetOrderAndPricingInformation - this class
''' supplies the return type for that function
''' </summary>
''' <remarks></remarks>
Public Class OrderAndPricingResult
Private mOrderID As Int32
Private mCompany As String
Private mOrderCountry As String
Private mProductName As String
Private mUnitPrice As Nullable(Of Decimal)
Private mUnitsOrder As Int16
Private mShipperName As String
Private mSalesFirstName As String
Private mSalesLastName As String
Private mSalesTitle As String
Public Property OrderID() As Int32
Get
Return mOrderID
End Get
Set(ByVal value As Int32)
mOrderID = value
End Set
End Property
Public Property Company() As String
Get
Return mCompany
End Get
Set(ByVal value As String)
mCompany = value
End Set
End Property
Public Property OrderCountry() As String
Get
Return mOrderCountry
End Get
Set(ByVal value As String)
mOrderCountry = value
End Set
End Property
Public Property ProductName() As String
Get
Return mProductName
End Get
Set(ByVal value As String)
mProductName = value
End Set
End Property
Public Property UnitPrice() As Nullable(Of Decimal)
Get
Return mUnitPrice
End Get
Set(ByVal value As Nullable(Of Decimal))
mUnitPrice = value
End Set
End Property
Public Property UnitsOrder() As Int16
Get
Return mUnitsOrder
End Get
Set(ByVal value As Int16)
mUnitsOrder = value
End Set
End Property
Public Property ShipperName() As String
Get
Return mShipperName
End Get
Set(ByVal value As String)
mShipperName = value
End Set
End Property
Public Property SalesFirstName() As String
Get
Return mSalesFirstName
End Get
Set(ByVal value As String)
mSalesFirstName = value
| ||||||||||||||||||||