Is it possible to do it in one line.
I believe you can distill your code to less lines by combining the check into the second sales array such as
var products = Model.Products.Where(p => p.Id == product.Id && p.Sales.Any(i => i.Item1 == sale.Id) ); var saleAmount = (products != null && products.Any()) ? products.First().Sales.First().Item2.ToString() : string.Empty;
Using a Default Value
This solution uses the help from a default faux pre-created Product to be used when one is not found. Using it in the extension method DefaultIfEmpty, that method determines if a empty projection has been returned and in that case it will instead return the faux instance. After that we can safely extract a the value which would be string.empty and assign it to the final string productSale.
Below I use a hardcoded 1.5 as the sale price for easier reading of the example.
// Our default will set saleAmount to string.Empty if nothing is found in Products. var defProduct = new Product() { Id = -1, Sales = new List<Tuple<string, double>>() { new Tuple<string,double>(string.Empty, 0.0) }}; var productSale = Products.Where(p => p.Id == product.Id && p.Sales.Any (s => s.Item2 == 1.5 ) ) .DefaultIfEmpty( defProduct ) .First () .Sales.First() .Item1;
productSale is string.Empty if no value found or has the actual value to use.
Whole test project in LinqPad which simulates a fail by using 1.5. Use 1.6 to show success.
void Main() { var targetSalePrice = 1.5; var targetProductId = 2; var Products = new List<Product>() { new Product() { Id = 2, Sales = new List<Tuple<string, double>>() { new Tuple<string,double>("actual", 1.6) } } }; // Our default will set saleAmount to string.Empty if nothing is found in Products. var defProduct = new Product() { Id = -1, Sales = new List<Tuple<string, double>>() { new Tuple<string,double>("faux string.Empty", 0.0) }}; var productSale = Products.Where(p => p.Id == targetProductId && p.Sales.Any (s => s.Item2 == targetSalePrice ) ) .DefaultIfEmpty( defProduct ) .First () .Sales.First () .Item1; productSale.Dump(); // outputs the string "faux string.Empty" from the faux default. } // Define other methods and classes here public class Product { public int Id { get; set; } public List<Tuple<string, double>> Sales { get; set; } }
NullReferenceExceptionsand I doubt your code would be any clearer.SingleOrDefaultwill throw an exception if more than one item in the list has that produce id. Are you sure you don't wantFirstOrDefaulthere?FirstOrDefaultis performance wise faster (because it's doesn't need to check to see if there are duplicates to throw the exception).