Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 1
Chapter 23
How to use LINQ
(Language-Integrated
Query)
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 2
Objectives
Applied
1. Use any of the LINQ features presented in this chapter to query
an in-memory data structure such as an array, sorted list, or
generic list.
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 3
Objectives (continued)
Knowledge
1. In general terms, explain how LINQ is implemented.
2. Name the interface that a data source must implement to use
LINQ with that data source.
3. Describe the three stages of a query operation.
4. Describe how the type of a query variable is determined.
5. Describe how deferred execution works.
6. Describe when a query operation results in a projection.
7. Describe when a query operation results in an anonymous type.
8. Describe the purpose of each of the following LINQ clauses:
from, where, orderby, select, join.
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 4
C# keywords for working with LINQ
• from
• where
• orderby
• select
• join
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 5
LINQ features
• Query language is integrated with C#
• Provides IntelliSense, compile-time syntax checking, and debugging
support
• Same basic syntax for each type of query
• Provides designer tools for object-relational mappings
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 6
The three stages of a query operation
• Get the data source.
• Define the query expression.
• Execute the query to return the results.
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 7
A LINQ query that retrieves data from an array
Code that defines the array
int[] numbers = new int[6];
for (int i = 0; i < numbers.Length; i++)
numbers[i] = i;
A statement that defines the query expression
var numberList = from number in numbers
where number % 2 == 0
orderby number descending
select number;
Code that executes the query
string numberDisplay = "";
foreach (var number in numberList)
numberDisplay += "tt" + number + "n";
MessageBox.Show(numberDisplay, "Sorted Even Numbers");
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 8
The resulting dialog box
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 9
The syntax of the from clause
from [type] elementName in collectionName
An example that uses an array of decimals
A statement that gets the data source
decimal[] salesTotals =
new decimal[4] {1286.45m, 2433.49m, 2893.85m,
2094.53m};
A statement that defines the query expression
var salesList = from sales in salesTotals
select sales;
Code that executes the query
decimal sum = 0;
foreach (var sales in salesList)
sum += sales;
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 10
An example that uses a generic list of invoices
The Invoice class
public class Invoice
{
public int InvoiceID { get; set; }
public int CustomerID { get; set; }
public DateTime InvoiceDate { get; set; }
public decimal ProductTotal { get; set; }
public decimal SalesTax { get; set; }
public decimal Shipping { get; set; }
public decimal InvoiceTotal { get; set; }
}
A statement that gets the data source
List<Invoice> invoiceList = InvoiceDB.GetInvoices();
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 11
A statement that defines the query expression
var invoices = from invoice in invoiceList
select invoice;
Code that executes the query
decimal sum = 0;
foreach (var invoice in invoices)
sum += invoice.InvoiceTotal;
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 12
The syntax of the where clause
where condition
An example that filters the salesTotals array
A query expression that returns sales greater than $2000
var salesList = from sales in salesTotals
where sales > 2000
select sales;
Code that executes the query
string salesDisplay = "";
foreach (var sales in salesList)
salesDisplay += "t" + sales.ToString("c") + "n";
MessageBox.Show(salesDisplay, "Sales Over $2000");
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 13
The resulting dialog box
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 14
An example that filters the generic list of invoices
A query expression that returns totals over $150
var invoices = from invoice in invoiceList
where invoice.InvoiceTotal > 150
select invoice;
Code that executes the query
string invoiceDisplay = "";
foreach (var invoice in invoices)
invoiceDisplay += "tt" +
invoice.InvoiceTotal.ToString("c") + "n";
MessageBox.Show(invoiceDisplay, "Invoices Over $150");
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 15
The resulting dialog box
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 16
The syntax of the orderby clause
orderby expression1 [ascending|descending]
[, expression2 [ascending|descending]]...
An example that sorts the salesTotals array
A query expression that sorts the sales
var salesList = from sales in salesTotals
where sales > 2000
orderby sales
select sales;
Code that executes the query
string salesDisplay = "";
foreach (var sales in salesList)
salesDisplay += "tt" + sales.ToString("c") + "n";
MessageBox.Show(salesDisplay, "Sorted Sales Over $2000");
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 17
The resulting dialog box
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 18
An example that sorts the generic list of invoices
A query expression that sorts the invoices
var invoices = from invoice in invoiceList
where invoice.InvoiceTotal > 150
orderby invoice.CustomerID,
invoice.InvoiceTotal descending
select invoice;
Code that executes the query
string invoiceDisplay = "Cust IDtInvoice amountn";
foreach (var invoice in invoices)
invoiceDisplay += invoice.CustomerID + "t"
+ invoice.InvoiceTotal.ToString("c")
+ "n";
MessageBox.Show(invoiceDisplay,
"Sorted Invoices Over $150");
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 19
The resulting dialog box
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 20
Two ways to code the select clause
select columnExpression
select new [type] { [PropertyName1 =] columnExpression1
[, [PropertyName2 =] columnExpression2]... }
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 21
An example that selects key values from a list
The employee sales sorted list
SortedList<string, decimal> employeeSales =
new SortedList<string,decimal>();
employeeSales.Add("Anderson", 1286.45m);
employeeSales.Add("Menendez", 2433.49m);
employeeSales.Add("Thompson", 2893.85m);
employeeSales.Add("Wilkinson", 2094.53m);
A query expression that selects employee names
var employeeList = from sales in employeeSales
where sales.Value > 2000
orderby sales.Value descending
select sales.Key;
Code that executes the query
string employeeDisplay = "";
foreach (var employee in employeeList)
employeeDisplay += "ttt" + employee + "n";
MessageBox.Show(employeeDisplay,
"Sorted Employees With Sales Over $2000");
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 22
The resulting dialog box
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 23
A query expression that creates an anonymous
type from the list of invoices
var invoices = from invoice in invoiceList
where invoice.InvoiceTotal > 150
orderby invoice.CustomerID,
invoice.InvoiceTotal descending
select new { invoice.CustomerID,
invoice.InvoiceTotal };
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 24
The basic syntax of the join clause
join elementName in collectionName
on keyName1 equals keyName2
An example that joins data from two generic lists
The Customer class
public class Customer
{
public int CustomerID { get; set; }
public string Name { get; set; }
}
Code that gets the two data sources
List<Invoice> invoiceList = InvoiceDB.GetInvoices();
List<Customer> customerList = CustomerDB.GetCustomers();
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 25
A query expression that joins data
var invoices =
from invoice in invoiceList
join customer in customerList
on invoice.CustomerID equals customer.CustomerID
where invoice.InvoiceTotal > 150
orderby customer.Name,
invoice.InvoiceTotal descending
select new { customer.Name, invoice.InvoiceTotal };
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 26
Code that executes the query
string invoiceDisplay =
"Customer NamettInvoice amountn";
foreach (var invoice in invoices)
{
invoiceDisplay += invoice.Name + "tt";
invoiceDisplay +=
invoice.InvoiceTotal.ToString("c") + "n";
}
MessageBox.Show(invoiceDisplay,
"Joined Customer and Invoice Data");
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 27
The resulting dialog box
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 28
The Customer Invoice form
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 29
Customer Invoice form with generic lists
private void Form1_Load(object sender, EventArgs e)
{
List<Customer> customerList = CustomerDB.GetCustomers();
List<Invoice> invoiceList = InvoiceDB.GetInvoices();
var invoices = from invoice in invoiceList
join customer in customerList
on invoice.CustomerID equals customer.CustomerID
orderby customer.Name, invoice.InvoiceTotal
descending
select new { customer.Name,
invoice.InvoiceID,
invoice.InvoiceDate,
invoice.InvoiceTotal };
string customerName = "";
int i = 0;
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 30
Customer Invoice form with generic lists (cont.)
foreach (var invoice in invoices)
{
if (invoice.Name != customerName)
{
lvInvoices.Items.Add(invoice.Name);
customerName = invoice.Name;
}
else
{
lvInvoices.Items.Add("");
}
lvInvoices.Items[i].SubItems.Add(
invoice.InvoiceID.ToString());
lvInvoices.Items[i].SubItems.Add(
Convert.ToDateTime(
invoice.InvoiceDate).ToShortDateString());
lvInvoices.Items[i].SubItems.Add(
invoice.InvoiceTotal.ToString("c"));
i += 1;
}
}
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 31
The dataset schema
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 32
Customer Invoice form with typed dataset
MMABooksDataSet mmaBooksDataSet = new MMABooksDataSet();
InvoicesTableAdapter invoicesTableAdapter =
new InvoicesTableAdapter();
CustomersTableAdapter customersTableAdapter =
new CustomersTableAdapter();
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 33
Customer Invoice form with typed dataset (cont.)
private void Form1_Load(object sender, EventArgs e)
{
invoicesTableAdapter.Fill(mmaBooksDataSet.Invoices);
customersTableAdapter.Fill(mmaBooksDataSet.Customers);
var invoices = from invoice in mmaBooksDataSet.Invoices
join customer in mmaBooksDataSet.Customers
on invoice.CustomerID equals customer.CustomerID
orderby customer.Name, invoice.InvoiceTotal
descending
select new { customer.Name,
invoice.InvoiceID,
invoice.InvoiceDate,
invoice.InvoiceTotal };
string customerName = "";
int i = 0;
Murach’s C#
2010, C23 © 2010, Mike Murach & Associates, Inc.Slide 34
Customer Invoice form with typed dataset (cont.)
foreach (var invoice in invoices)
{
if (invoice.Name != customerName)
{
lvInvoices.Items.Add(invoice.Name);
customerName = invoice.Name;
}
else
{
lvInvoices.Items.Add("");
}
lvInvoices.Items[i].SubItems.Add(
invoice.InvoiceID.ToString());
lvInvoices.Items[i].SubItems.Add(
Convert.ToDateTime(
invoice.InvoiceDate).ToShortDateString());
lvInvoices.Items[i].SubItems.Add(
invoice.InvoiceTotal.ToString("c"));
i += 1;
}
}

C# Tutorial MSM_Murach chapter-23-slides

  • 1.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 1 Chapter 23 How to use LINQ (Language-Integrated Query)
  • 2.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 2 Objectives Applied 1. Use any of the LINQ features presented in this chapter to query an in-memory data structure such as an array, sorted list, or generic list.
  • 3.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 3 Objectives (continued) Knowledge 1. In general terms, explain how LINQ is implemented. 2. Name the interface that a data source must implement to use LINQ with that data source. 3. Describe the three stages of a query operation. 4. Describe how the type of a query variable is determined. 5. Describe how deferred execution works. 6. Describe when a query operation results in a projection. 7. Describe when a query operation results in an anonymous type. 8. Describe the purpose of each of the following LINQ clauses: from, where, orderby, select, join.
  • 4.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 4 C# keywords for working with LINQ • from • where • orderby • select • join
  • 5.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 5 LINQ features • Query language is integrated with C# • Provides IntelliSense, compile-time syntax checking, and debugging support • Same basic syntax for each type of query • Provides designer tools for object-relational mappings
  • 6.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 6 The three stages of a query operation • Get the data source. • Define the query expression. • Execute the query to return the results.
  • 7.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 7 A LINQ query that retrieves data from an array Code that defines the array int[] numbers = new int[6]; for (int i = 0; i < numbers.Length; i++) numbers[i] = i; A statement that defines the query expression var numberList = from number in numbers where number % 2 == 0 orderby number descending select number; Code that executes the query string numberDisplay = ""; foreach (var number in numberList) numberDisplay += "tt" + number + "n"; MessageBox.Show(numberDisplay, "Sorted Even Numbers");
  • 8.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 8 The resulting dialog box
  • 9.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 9 The syntax of the from clause from [type] elementName in collectionName An example that uses an array of decimals A statement that gets the data source decimal[] salesTotals = new decimal[4] {1286.45m, 2433.49m, 2893.85m, 2094.53m}; A statement that defines the query expression var salesList = from sales in salesTotals select sales; Code that executes the query decimal sum = 0; foreach (var sales in salesList) sum += sales;
  • 10.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 10 An example that uses a generic list of invoices The Invoice class public class Invoice { public int InvoiceID { get; set; } public int CustomerID { get; set; } public DateTime InvoiceDate { get; set; } public decimal ProductTotal { get; set; } public decimal SalesTax { get; set; } public decimal Shipping { get; set; } public decimal InvoiceTotal { get; set; } } A statement that gets the data source List<Invoice> invoiceList = InvoiceDB.GetInvoices();
  • 11.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 11 A statement that defines the query expression var invoices = from invoice in invoiceList select invoice; Code that executes the query decimal sum = 0; foreach (var invoice in invoices) sum += invoice.InvoiceTotal;
  • 12.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 12 The syntax of the where clause where condition An example that filters the salesTotals array A query expression that returns sales greater than $2000 var salesList = from sales in salesTotals where sales > 2000 select sales; Code that executes the query string salesDisplay = ""; foreach (var sales in salesList) salesDisplay += "t" + sales.ToString("c") + "n"; MessageBox.Show(salesDisplay, "Sales Over $2000");
  • 13.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 13 The resulting dialog box
  • 14.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 14 An example that filters the generic list of invoices A query expression that returns totals over $150 var invoices = from invoice in invoiceList where invoice.InvoiceTotal > 150 select invoice; Code that executes the query string invoiceDisplay = ""; foreach (var invoice in invoices) invoiceDisplay += "tt" + invoice.InvoiceTotal.ToString("c") + "n"; MessageBox.Show(invoiceDisplay, "Invoices Over $150");
  • 15.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 15 The resulting dialog box
  • 16.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 16 The syntax of the orderby clause orderby expression1 [ascending|descending] [, expression2 [ascending|descending]]... An example that sorts the salesTotals array A query expression that sorts the sales var salesList = from sales in salesTotals where sales > 2000 orderby sales select sales; Code that executes the query string salesDisplay = ""; foreach (var sales in salesList) salesDisplay += "tt" + sales.ToString("c") + "n"; MessageBox.Show(salesDisplay, "Sorted Sales Over $2000");
  • 17.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 17 The resulting dialog box
  • 18.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 18 An example that sorts the generic list of invoices A query expression that sorts the invoices var invoices = from invoice in invoiceList where invoice.InvoiceTotal > 150 orderby invoice.CustomerID, invoice.InvoiceTotal descending select invoice; Code that executes the query string invoiceDisplay = "Cust IDtInvoice amountn"; foreach (var invoice in invoices) invoiceDisplay += invoice.CustomerID + "t" + invoice.InvoiceTotal.ToString("c") + "n"; MessageBox.Show(invoiceDisplay, "Sorted Invoices Over $150");
  • 19.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 19 The resulting dialog box
  • 20.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 20 Two ways to code the select clause select columnExpression select new [type] { [PropertyName1 =] columnExpression1 [, [PropertyName2 =] columnExpression2]... }
  • 21.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 21 An example that selects key values from a list The employee sales sorted list SortedList<string, decimal> employeeSales = new SortedList<string,decimal>(); employeeSales.Add("Anderson", 1286.45m); employeeSales.Add("Menendez", 2433.49m); employeeSales.Add("Thompson", 2893.85m); employeeSales.Add("Wilkinson", 2094.53m); A query expression that selects employee names var employeeList = from sales in employeeSales where sales.Value > 2000 orderby sales.Value descending select sales.Key; Code that executes the query string employeeDisplay = ""; foreach (var employee in employeeList) employeeDisplay += "ttt" + employee + "n"; MessageBox.Show(employeeDisplay, "Sorted Employees With Sales Over $2000");
  • 22.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 22 The resulting dialog box
  • 23.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 23 A query expression that creates an anonymous type from the list of invoices var invoices = from invoice in invoiceList where invoice.InvoiceTotal > 150 orderby invoice.CustomerID, invoice.InvoiceTotal descending select new { invoice.CustomerID, invoice.InvoiceTotal };
  • 24.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 24 The basic syntax of the join clause join elementName in collectionName on keyName1 equals keyName2 An example that joins data from two generic lists The Customer class public class Customer { public int CustomerID { get; set; } public string Name { get; set; } } Code that gets the two data sources List<Invoice> invoiceList = InvoiceDB.GetInvoices(); List<Customer> customerList = CustomerDB.GetCustomers();
  • 25.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 25 A query expression that joins data var invoices = from invoice in invoiceList join customer in customerList on invoice.CustomerID equals customer.CustomerID where invoice.InvoiceTotal > 150 orderby customer.Name, invoice.InvoiceTotal descending select new { customer.Name, invoice.InvoiceTotal };
  • 26.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 26 Code that executes the query string invoiceDisplay = "Customer NamettInvoice amountn"; foreach (var invoice in invoices) { invoiceDisplay += invoice.Name + "tt"; invoiceDisplay += invoice.InvoiceTotal.ToString("c") + "n"; } MessageBox.Show(invoiceDisplay, "Joined Customer and Invoice Data");
  • 27.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 27 The resulting dialog box
  • 28.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 28 The Customer Invoice form
  • 29.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 29 Customer Invoice form with generic lists private void Form1_Load(object sender, EventArgs e) { List<Customer> customerList = CustomerDB.GetCustomers(); List<Invoice> invoiceList = InvoiceDB.GetInvoices(); var invoices = from invoice in invoiceList join customer in customerList on invoice.CustomerID equals customer.CustomerID orderby customer.Name, invoice.InvoiceTotal descending select new { customer.Name, invoice.InvoiceID, invoice.InvoiceDate, invoice.InvoiceTotal }; string customerName = ""; int i = 0;
  • 30.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 30 Customer Invoice form with generic lists (cont.) foreach (var invoice in invoices) { if (invoice.Name != customerName) { lvInvoices.Items.Add(invoice.Name); customerName = invoice.Name; } else { lvInvoices.Items.Add(""); } lvInvoices.Items[i].SubItems.Add( invoice.InvoiceID.ToString()); lvInvoices.Items[i].SubItems.Add( Convert.ToDateTime( invoice.InvoiceDate).ToShortDateString()); lvInvoices.Items[i].SubItems.Add( invoice.InvoiceTotal.ToString("c")); i += 1; } }
  • 31.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 31 The dataset schema
  • 32.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 32 Customer Invoice form with typed dataset MMABooksDataSet mmaBooksDataSet = new MMABooksDataSet(); InvoicesTableAdapter invoicesTableAdapter = new InvoicesTableAdapter(); CustomersTableAdapter customersTableAdapter = new CustomersTableAdapter();
  • 33.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 33 Customer Invoice form with typed dataset (cont.) private void Form1_Load(object sender, EventArgs e) { invoicesTableAdapter.Fill(mmaBooksDataSet.Invoices); customersTableAdapter.Fill(mmaBooksDataSet.Customers); var invoices = from invoice in mmaBooksDataSet.Invoices join customer in mmaBooksDataSet.Customers on invoice.CustomerID equals customer.CustomerID orderby customer.Name, invoice.InvoiceTotal descending select new { customer.Name, invoice.InvoiceID, invoice.InvoiceDate, invoice.InvoiceTotal }; string customerName = ""; int i = 0;
  • 34.
    Murach’s C# 2010, C23© 2010, Mike Murach & Associates, Inc.Slide 34 Customer Invoice form with typed dataset (cont.) foreach (var invoice in invoices) { if (invoice.Name != customerName) { lvInvoices.Items.Add(invoice.Name); customerName = invoice.Name; } else { lvInvoices.Items.Add(""); } lvInvoices.Items[i].SubItems.Add( invoice.InvoiceID.ToString()); lvInvoices.Items[i].SubItems.Add( Convert.ToDateTime( invoice.InvoiceDate).ToShortDateString()); lvInvoices.Items[i].SubItems.Add( invoice.InvoiceTotal.ToString("c")); i += 1; } }