LINQ has transformed C# development, providing a powerful syntax for querying and manipulating data. In this article, we’ll explore LINQ methods that return a single object, ideal for simplifying and improving the readability of queries that require retrieving a specific element from a collection. For methods that return more than one element, we recommend our article on “3 Essential Methods for a .NET Developer.”
Base Code for Examples
To exemplify the application of the methods, we will use a class called “Product” as our data model, whose code is as follows:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
Next, we will create a list called “products,” which will serve as our database, and add five objects:
List products = new List
{
new Product { Id = 1, Name = "Notebook", Price = 1200 },
new Product { Id = 2, Name = "Tablet", Price = 400 },
new Product { Id = 3, Name = "Smartphone", Price = 800 },
new Product { Id = 4, Name = "Monitor", Price = 300 },
new Product { Id = 5, Name = "Keyboard", Price = 50 }
};
First
The LINQ “First” method is used to return the first element of a sequence that satisfies a specified condition. If no condition is passed, it returns the first element of the sequence. Below, we see the two possibilities of use
Product firstElement = products.First();
Product firstMatchingElement = products.First(p => p.Price < 500);
In this example, the variable “firstElement” will receive the “Notebook” product, which is the first element of the “products” list. The variable “firstMatchingElement” will receive the “Tablet” item, which is the first item with a value below $500. Note that the “Monitor” and “Keyboard” items also have a value below $500, but as the “First” method found an item that satisfies the condition, it immediately returns that item and stops searching, without needing to check the others.
It is important to note that if no element in the list satisfies the condition or if the list is empty, the “First” method will throw an “InvalidOperationException” exception. Therefore, it is a good practice to use exception handling to deal with these cases and avoid unexpected program failures. This also applies to the “Single” and “Last” methods, which we will discuss later.
We can implement the following code to handle exceptions:
try
{
Product firstMatchingElement = products.First(p => p.Price > 2000);
}
catch (InvalidOperationException ex)
{
Console.WriteLine("No element found: " + ex.Message);
}
In this example, the list does not contain any items with a price above $2,000. When trying to retrieve the first element that matches the condition with “First,” an “InvalidOperationException” exception is thrown, which is then caught and handled in the “catch” block, displaying an appropriate message.
FirstOrDefault
An alternative to “First” is the “FirstOrDefault” method, which, if the sequence is empty or no element satisfies the condition, returns the default value “null” for reference types like “string” or objects, and for value types like “int,” “bool,” the default value is “0” for “int” and “false” for “bool.” This can be useful to avoid the need for exception handling in some cases:
Product firstMatchingElement = products.FirstOrDefault(p => p.Price > 2000);
In this example, instead of returning an exception, a “null” value will be returned.
From .NET 6 onwards, it is possible to define a default value directly in the “FirstOrDefault” method, which facilitates return handling:
Product firstMatchingElement = products.FirstOrDefault(p => p.Price > 2000, new Product { Id = 0, Name = "Product not found", Price = 0 });
In this example, if an item with a value above $2,000 is not found, the “firstMatchingElement” variable, instead of receiving a “null” value, will receive an item with “Id” and “Price” equal to “0” and “Name” equal to “Product not found.”
Single
The “Single” method is quite similar to “First.” It also returns a single element from a sequence that satisfies a specified condition. If no element is found that satisfies the condition, an “InvalidOperationException” exception will be thrown, with the message “Sequence contains no matching element.” However, unlike “First,” which returns only the first element if there are multiple elements that satisfy the condition, “Single” will also return an exception of the type “InvalidOperationException,” but with the message “Sequence contains more than one matching element.” That is, this method should be used when it is expected that there is only one element that satisfies the specified condition.
Product productId1 = products.Single(p => p.Id == 1);
Product firstMatchingElement = products.Single(p => p.Price > 500);
In the example above, the variable “productId1” will receive the “Notebook” product, which is the only one that matches the condition Id == 1. In the second case, the “Single” method will search for an element with a value above $500 in the “products” list and will find the “Notebook” and “Smartphone” items. Because of this, an exception will be thrown.
SingleOrDefault
The “SingleOrDefault” method works similarly to “Single,” but instead of throwing an exception when no element satisfies the condition, it returns the default value for the specified type.
Product firstMatchingElement = products.SingleOrDefault(p => p.Price > 2000);
In the example above, “firstMatchingElement” will be “null” because there is no element with a value above $2,000 in the list. The “SingleOrDefault” method also allows adding a default value if the element does not exist in the list
Product firstMatchingElement = products.SingleOrDefault(p => p.Price > 2000, new Product { Id = 0, Name = "Product not found", Price = 0 });
This is useful for returning a default value to indicate the absence of results. This avoids exceptions or unexpected behavior in the application.
Last
The LINQ “Last” method is used to return the last element of a sequence that satisfies a specified condition. If no condition is passed, it returns the last element of the sequence. If no element meets the condition, an “InvalidOperationException” exception will be thrown.
This method is useful when you need to get the final element of a collection, whether it is ordered or not.
Product lastElement = products.Last(p => p.Price > 500);
In this example, the variable “lastElement” will receive the value “Smartphone,” which is the last element of the list that has a value above $500.
LastOrDefault
An alternative to “Last” is the “LastOrDefault” method, which returns the default value of the element type of the sequence if the sequence is empty or if no element satisfies the specified condition.
Product lastMatchingElement = products.LastOrDefault(p => p.Price > 2000);
In this example, “lastMatchingElement” will be “null” because there is no product in the list that has a value above $2,000.
It is also possible to add a default value to be returned if the condition is not met:
Product firstMatchingElement = products.LastOrDefault(p => p.Price > 2000, new Product { Id = 0, Name = "Product not found", Price = 0 });
In this example, if an item with a value above $2,000 is not found, “lastMatchingElement” will be a product with the “Name” “Product not found.”
Conclusion
In summary, LINQ methods for retrieving single records offer flexibility and robustness for C# developers. The use of methods like “First,” “FirstOrDefault,” “Single,” “SingleOrDefault,” “Last,” and “LastOrDefault” simplifies the precise retrieval of elements from collections, promoting readability and efficiency in the code. However, it is crucial to understand their differences and specific applicability to avoid exceptions and ensure data integrity.