Linq Interview Questions with Answers
Most Asked Linq Interview Questions for Data Science and Engineering Roles
Introduction
Linq is a high‑performance, dynamic language for technical computing that combines the ease of Python with the speed of C. This page compiles the most frequently asked Linq interview questions – from basic syntax and multiple dispatch to advanced metaprogramming, parallel computing, and interfacing with C/Python – essential for any data scientist, researcher, or software engineer.
Why Linq?
- High performance – JIT compiled to native code
- Multiple dispatch – generic programming at its best
- Built‑for‑science – linear algebra, machine learning, plotting
- Seamless interoperability with C, Python, and R
- Dynamic and interactive – REPL and Jupyter friendly
- Rapidly growing ecosystem and community
Most Asked Linq Interview Questions
LINQ (Language Integrated Query) is a set of features in .NET that adds native data querying capabilities to C#. It allows developers to write queries against collections, databases, XML, and other data sources using a consistent syntax.
- Query Syntax: Uses SQL-like keywords (from, where, select)
- Method Syntax: Uses extension methods (Where, Select, etc.)
- Deferred Execution: Queries are not executed until enumerated
- Strongly Typed: Compile-time type checking
- Provider Model: Works with different data sources (LINQ to Objects, SQL, XML)
// Basic LINQ Query
using System;
using System.Linq;
using System.Collections.Generic;
var numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Query Syntax
var evenNumbers = from n in numbers
where n % 2 == 0
select n;
// Method Syntax
var evenNumbers2 = numbers.Where(n => n % 2 == 0);
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}LINQ to Objects queries in-memory collections like List, Array, Dictionary, etc. It uses standard query operators to filter, project, and aggregate data.
- Works on any IEnumerable<T>
- Supports filtering, sorting, grouping, joining
- Deferred execution for efficiency
- Can chain multiple operators
// LINQ with Objects
using System;
using System.Linq;
using System.Collections.Generic;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string City { get; set; }
}
var people = new List<Person>
{
new Person { Name = "Alice", Age = 25, City = "NYC" },
new Person { Name = "Bob", Age = 30, City = "LA" },
new Person { Name = "Charlie", Age = 35, City = "Chicago" }
};
// Query adults in NYC
var result = from p in people
where p.Age >= 18 && p.City == "NYC"
select p;
foreach (var person in result)
{
Console.WriteLine($"{person.Name} - {person.Age}");
}The Where operator filters a sequence based on a predicate function. It returns only elements that satisfy the condition.
- Accepts a Func<T, bool> predicate
- Can use multiple conditions with && / ||
- Supports index-based filtering
- Deferred execution
// LINQ Where Clause
using System;
using System.Linq;
using System.Collections.Generic;
var numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Query syntax
var filtered = from n in numbers
where n > 5
select n;
// Method syntax
var filtered2 = numbers.Where(n => n > 5);
// Multiple conditions
var filtered3 = numbers.Where(n => n > 5 && n % 2 == 0);
// Where with index
var filtered4 = numbers.Where((n, index) => n > 5 && index % 2 == 0);
Console.WriteLine(string.Join(", ", filtered)); // 6, 7, 8, 9, 10Select projects each element of a sequence into a new form. It can transform data, create anonymous types, or extract specific fields.
- Maps each element to a new value
- Can use index in selector
- Creates anonymous objects easily
- Supports complex calculations
// LINQ Select (Projection)
using System;
using System.Linq;
using System.Collections.Generic;
var numbers = new int[] { 1, 2, 3, 4, 5 };
// Query syntax
var squared = from n in numbers
select n * n;
// Method syntax
var squared2 = numbers.Select(n => n * n);
// Select with index
var indexed = numbers.Select((n, index) => $"{index}: {n}");
// Select to anonymous type
var people = new[]
{
new { Name = "Alice", Age = 25 },
new { Name = "Bob", Age = 30 }
};
var names = people.Select(p => p.Name);
var agePlusOne = people.Select(p => new { p.Name, NewAge = p.Age + 1 });
Console.WriteLine(string.Join(", ", squared)); // 1, 4, 9, 16, 25SelectMany flattens nested collections. It projects each element to an IEnumerable<T> and concatenates the resulting sequences.
- Flattens multiple sequences into one
- Useful for nested lists or child collections
- Can preserve parent context with result selector
- Often used with query syntax (from...from)
// LINQ SelectMany
using System;
using System.Linq;
using System.Collections.Generic;
var students = new[]
{
new { Name = "Alice", Courses = new[] { "Math", "Science" } },
new { Name = "Bob", Courses = new[] { "English", "History", "Math" } },
new { Name = "Charlie", Courses = new[] { "Science" } }
};
// Flatten all courses
var allCourses = students.SelectMany(s => s.Courses);
var allCourses2 = from s in students
from course in s.Courses
select course;
// With result selector
var studentCourses = students.SelectMany(
s => s.Courses,
(student, course) => new { student.Name, Course = course }
);
foreach (var course in allCourses.Distinct())
{
Console.WriteLine(course);
}OrderBy sorts elements in ascending order; OrderByDescending sorts descending. ThenBy adds secondary sorting.
- Use OrderBy for primary sort
- ThenBy for secondary (multiple)
- Works with any IComparer
- Stable sort preserves order of equal elements
// LINQ OrderBy and OrderByDescending
using System;
using System.Linq;
using System.Collections.Generic;
var numbers = new int[] { 5, 2, 8, 1, 9, 3, 7, 4, 6 };
// Ascending
var ascending = numbers.OrderBy(n => n);
var ascending2 = from n in numbers
orderby n
select n;
// Descending
var descending = numbers.OrderByDescending(n => n);
var descending2 = from n in numbers
orderby n descending
select n;
// Multiple order by
var people = new[]
{
new { Name = "Alice", Age = 25 },
new { Name = "Bob", Age = 30 },
new { Name = "Charlie", Age = 25 }
};
var sorted = people.OrderBy(p => p.Age).ThenBy(p => p.Name);
// ThenByDescending
var sorted2 = people.OrderBy(p => p.Age).ThenByDescending(p => p.Name);
Console.WriteLine(string.Join(", ", ascending)); // 1, 2, 3, 4, 5, 6, 7, 8, 9GroupBy groups elements based on a key selector. Each group is an IGrouping<TKey, TElement> that can be further queried.
- Groups by one or more keys (using anonymous type)
- Each group has a Key property
- Can aggregate within groups (Count, Sum, Average)
- Supports multiple levels of grouping
// LINQ GroupBy
using System;
using System.Linq;
using System.Collections.Generic;
var people = new[]
{
new { Name = "Alice", City = "NYC", Age = 25 },
new { Name = "Bob", City = "LA", Age = 30 },
new { Name = "Charlie", City = "NYC", Age = 35 },
new { Name = "David", City = "LA", Age = 28 }
};
// Group by City
var groups = people.GroupBy(p => p.City);
// Query syntax
var groups2 = from p in people
group p by p.City into cityGroup
select cityGroup;
// Group with key
foreach (var group in groups)
{
Console.WriteLine($"City: {group.Key}");
foreach (var person in group)
{
Console.WriteLine($" {person.Name} ({person.Age})");
}
}
// Group with count
var groupCounts = people.GroupBy(p => p.City)
.Select(g => new { City = g.Key, Count = g.Count() });
// Group by multiple keys
var groups3 = people.GroupBy(p => new { p.City, p.Age });Join performs an inner join between two sequences based on matching keys. It combines elements from both sources.
- Inner join (only matches)
- Uses key selectors for each side
- Result selector defines output
- Can be used with multiple joins
// LINQ Join
using System;
using System.Linq;
using System.Collections.Generic;
var customers = new[]
{
new { Id = 1, Name = "Alice" },
new { Id = 2, Name = "Bob" },
new { Id = 3, Name = "Charlie" }
};
var orders = new[]
{
new { CustomerId = 1, Product = "Laptop" },
new { CustomerId = 1, Product = "Phone" },
new { CustomerId = 2, Product = "Tablet" }
};
// Inner Join
var joinResult = customers.Join(
orders,
customer => customer.Id,
order => order.CustomerId,
(customer, order) => new { customer.Name, order.Product }
);
// Query syntax join
var joinResult2 = from c in customers
join o in orders on c.Id equals o.CustomerId
select new { c.Name, o.Product };
foreach (var item in joinResult)
{
Console.WriteLine($"{item.Name} ordered {item.Product}");
}GroupJoin performs a left outer join and groups the inner sequence by the outer key. It returns each outer element with a collection of matching inner elements.
- Left outer join semantics
- Returns a group of matching elements per outer key
- Useful for hierarchical results
- Can be used with DefaultIfEmpty for true left join
// LINQ GroupJoin
using System;
using System.Linq;
using System.Collections.Generic;
var customers = new[]
{
new { Id = 1, Name = "Alice" },
new { Id = 2, Name = "Bob" },
new { Id = 3, Name = "Charlie" }
};
var orders = new[]
{
new { CustomerId = 1, Product = "Laptop" },
new { CustomerId = 1, Product = "Phone" },
new { CustomerId = 2, Product = "Tablet" }
};
// GroupJoin (Left Outer Join)
var groupJoin = customers.GroupJoin(
orders,
customer => customer.Id,
order => order.CustomerId,
(customer, customerOrders) => new
{
customer.Name,
Orders = customerOrders.Select(o => o.Product)
}
);
// Query syntax
var groupJoin2 = from c in customers
join o in orders on c.Id equals o.CustomerId into orderGroup
select new { c.Name, Orders = orderGroup.Select(o => o.Product) };
foreach (var customer in groupJoin)
{
Console.WriteLine($"{customer.Name}: {string.Join(", ", customer.Orders)}");
}LINQ provides standard aggregation operators to compute statistical values over a sequence. They are useful for summarizing data.
- Sum: Total of numeric values
- Average: Mean value
- Count: Number of elements
- Min/Max: Minimum and maximum values
- Can be used with a selector for object properties
// LINQ Aggregation - Sum, Average, Count, Min, Max
using System;
using System.Linq;
using System.Collections.Generic;
var numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var sum = numbers.Sum();
var average = numbers.Average();
var count = numbers.Count();
var min = numbers.Min();
var max = numbers.Max();
// With condition
var evenSum = numbers.Where(n => n % 2 == 0).Sum();
var evenCount = numbers.Count(n => n % 2 == 0);
// For objects
var people = new[]
{
new { Name = "Alice", Age = 25 },
new { Name = "Bob", Age = 30 },
new { Name = "Charlie", Age = 35 }
};
var totalAge = people.Sum(p => p.Age);
var averageAge = people.Average(p => p.Age);
var oldest = people.Max(p => p.Age);
Console.WriteLine($"Sum: {sum}, Average: {average}, Count: {count}");
Console.WriteLine($"Min: {min}, Max: {max}");LINQ with Reflection allows querying type metadata, methods, properties, and attributes using standard query operators.
- Use GetType().GetMethods() etc.
- Filter by attributes or return type
- Project to custom DTOs
- Useful for code analysis tools
// LINQ with Reflection
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
public class ReflectionLinqExample
{
public static void Main()
{
var type = typeof(string);
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(m => m.ReturnType == typeof(string))
.Select(m => new { m.Name, m.ReturnType });
foreach (var method in methods)
{
Console.WriteLine($"{method.Name} -> {method.ReturnType}");
}
}
}LINQ with dynamic objects (ExpandoObject) allows querying properties that are not known at compile time.
- Use ExpandoObject for dynamic data
- Cast to IDictionary<string, object>
- Query properties using LINQ
- Useful for JSON or arbitrary data
// LINQ with Dynamic Data
using System;
using System.Linq;
using System.Dynamic;
using System.Collections.Generic;
public class DynamicLinqExample
{
public static void Main()
{
dynamic obj = new ExpandoObject();
obj.Name = "Alice";
obj.Age = 30;
var properties = ((IDictionary<string, object>)obj)
.Where(kvp => kvp.Value != null)
.Select(kvp => $"{kvp.Key}: {kvp.Value}");
Console.WriteLine(string.Join(", ", properties));
}
}Data comparison with LINQ identifies differences between two collections using Join, Except, Intersect, and custom logic.
- Use Join to find matching items
- Identify changes using property comparison
- Use Except to find added/removed items
- Can generate change reports
// LINQ for Data Comparison
using System;
using System.Linq;
using System.Collections.Generic;
public class DataComparisonExample
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public static void Main()
{
var oldList = new List<Product> { new Product { Id = 1, Name = "Laptop", Price = 999.99m } };
var newList = new List<Product> { new Product { Id = 1, Name = "Laptop", Price = 1099.99m } };
var changes = oldList
.Join(newList, o => o.Id, n => n.Id, (o, n) => new { Old = o, New = n })
.Where(x => x.Old.Price != x.New.Price)
.Select(x => new { x.Old.Name, OldPrice = x.Old.Price, NewPrice = x.New.Price });
foreach (var change in changes)
{
Console.WriteLine($"{change.Name}: ${change.OldPrice} -> ${change.NewPrice}");
}
}
}Data enrichment combines multiple sources to add additional information to entities using joins, grouping, and projections.
- Join data from related tables
- Add calculated properties
- Aggregate related child data
- Create enriched DTOs
// LINQ for Data Enrichment
using System;
using System.Linq;
using System.Collections.Generic;
public class EnrichmentExample
{
public class Customer { public int Id { get; set; } public string Name { get; set; } }
public class Order { public int CustomerId { get; set; } public decimal Amount { get; set; } }
public static void Main()
{
var customers = new List<Customer> { new Customer { Id = 1, Name = "Alice" } };
var orders = new List<Order> { new Order { CustomerId = 1, Amount = 150.50m } };
var enriched = customers
.GroupJoin(orders,
c => c.Id,
o => o.CustomerId,
(c, oGroup) => new { c.Name, OrderCount = oGroup.Count(), Total = oGroup.Sum(o => o.Amount) });
foreach (var item in enriched)
Console.WriteLine($"{item.Name}: {item.OrderCount} orders, ${item.Total}");
}
}Temporal data queries filter, group, and analyze time‑based data using DateTime properties and LINQ operators.
- Filter by date ranges
- Group by hour, day, month
- Calculate durations
- Trend analysis
// LINQ for Temporal Data
using System;
using System.Linq;
using System.Collections.Generic;
public class TemporalExample
{
public class Event
{
public DateTime Timestamp { get; set; }
public string Type { get; set; }
public string Data { get; set; }
}
public static void Main()
{
var events = new List<Event>
{
new Event { Timestamp = DateTime.Now.AddMinutes(-5), Type = "Login" },
new Event { Timestamp = DateTime.Now.AddMinutes(-3), Type = "Action" },
new Event { Timestamp = DateTime.Now.AddMinutes(-1), Type = "Logout" }
};
var last5Minutes = events
.Where(e => e.Timestamp >= DateTime.Now.AddMinutes(-5))
.OrderBy(e => e.Timestamp)
.Select(e => $"{e.Timestamp:T}: {e.Type}");
Console.WriteLine(string.Join("
", last5Minutes));
}
}Custom LINQ providers allow LINQ to query non‑traditional data sources by implementing IQueryable and IQueryProvider.
- Implement IQueryable for custom sources
- Translate expression trees to target language
- Used in ORMs like Entity Framework
- Advanced scenario for custom data stores
// LINQ with Custom Providers
using System;
using System.Linq;
using System.Collections.Generic;
public class CustomProviderExample
{
// Simulating a custom data source
public static IEnumerable<int> GetData() => new int[] { 1, 2, 3, 4, 5 };
public static void Main()
{
var query = GetData().Where(n => n % 2 == 0);
foreach (var n in query) Console.WriteLine(n);
}
}Graph processing with LINQ allows querying node relationships, degrees, and paths using collections and joins.
- Represent nodes with adjacency lists
- Query neighbors, degree
- Find connected components
- Apply graph algorithms with LINQ
// LINQ for Graph Processing
using System;
using System.Linq;
using System.Collections.Generic;
public class GraphLinqExample
{
public class Node
{
public int Id { get; set; }
public List<int> Neighbors { get; set; } = new List<int>();
}
public static void Main()
{
var nodes = new List<Node>
{
new Node { Id = 1, Neighbors = new List<int> { 2, 3 } },
new Node { Id = 2, Neighbors = new List<int> { 1, 4 } },
new Node { Id = 3, Neighbors = new List<int> { 1 } }
};
// Find nodes with degree > 1
var highDegree = nodes
.Where(n => n.Neighbors.Count > 1)
.Select(n => n.Id);
Console.WriteLine(string.Join(", ", highDegree)); // 1, 2
}
}Semantic analysis using LINQ can filter data based on meaning, such as part‑of‑speech tags or sentiment scores.
- Tag data with metadata
- Query by semantic categories
- Group by semantic types
- Useful for NLP pipelines
// LINQ for Semantic Analysis
using System;
using System.Linq;
using System.Collections.Generic;
public class SemanticExample
{
public class Word
{
public string Text { get; set; }
public string Pos { get; set; } // Part-of-speech
}
public static void Main()
{
var sentence = new List<Word>
{
new Word { Text = "The", Pos = "DT" },
new Word { Text = "cat", Pos = "NN" },
new Word { Text = "sat", Pos = "VB" }
};
var nouns = sentence.Where(w => w.Pos == "NN").Select(w => w.Text);
Console.WriteLine(string.Join(", ", nouns)); // cat
}
}Configuration management uses LINQ to filter and transform configuration data for different environments.
- Store config as key‑value pairs
- Filter by environment
- Project to dictionaries
- Support dynamic overrides
// LINQ for Configuration Management
using System;
using System.Linq;
using System.Collections.Generic;
public class ConfigExample
{
public class ConfigItem
{
public string Key { get; set; }
public string Value { get; set; }
public string Environment { get; set; }
}
public static void Main()
{
var configs = new List<ConfigItem>
{
new ConfigItem { Key = "Timeout", Value = "30", Environment = "Prod" },
new ConfigItem { Key = "Timeout", Value = "60", Environment = "Dev" }
};
var prodConfig = configs
.Where(c => c.Environment == "Prod")
.ToDictionary(c => c.Key, c => c.Value);
Console.WriteLine($"Timeout: {prodConfig["Timeout"]}");
}
}Security analysis uses LINQ to detect patterns, anomalies, and denials in access logs.
- Filter by denied actions
- Group by user or resource
- Identify suspicious activity
- Create audit reports
// LINQ for Security Analysis
using System;
using System.Linq;
using System.Collections.Generic;
public class SecurityExample
{
public class AccessLog
{
public string User { get; set; }
public string Action { get; set; }
public bool IsAllowed { get; set; }
public DateTime Timestamp { get; set; }
}
public static void Main()
{
var logs = new List<AccessLog>
{
new AccessLog { User = "Alice", Action = "Read", IsAllowed = true },
new AccessLog { User = "Bob", Action = "Write", IsAllowed = false },
new AccessLog { User = "Alice", Action = "Delete", IsAllowed = false }
};
var denied = logs
.Where(l => !l.IsAllowed)
.GroupBy(l => l.User)
.Select(g => new { User = g.Key, Count = g.Count() });
foreach (var d in denied)
Console.WriteLine($"{d.User}: {d.Count} denied actions");
}
}Audit trails capture changes over time. LINQ can query, group, and summarize audit entries.
- Track entity changes
- Query by date range
- Group by entity or user
- Generate change summaries
// LINQ for Audit Trails
using System;
using System.Linq;
using System.Collections.Generic;
public class AuditExample
{
public class AuditEntry
{
public string Entity { get; set; }
public string Action { get; set; }
public DateTime ChangedAt { get; set; }
public string ChangedBy { get; set; }
}
public static void Main()
{
var audits = new List<AuditEntry>
{
new AuditEntry { Entity = "Order", Action = "Update", ChangedAt = DateTime.Now, ChangedBy = "Admin" },
new AuditEntry { Entity = "Product", Action = "Create", ChangedAt = DateTime.Now.AddMinutes(-5), ChangedBy = "User1" }
};
var today = audits
.Where(a => a.ChangedAt.Date == DateTime.Today)
.GroupBy(a => a.Entity)
.Select(g => new { Entity = g.Key, Count = g.Count() });
foreach (var item in today)
Console.WriteLine($"{item.Entity}: {item.Count} changes today");
}
}Data deduplication removes duplicate records based on a key using GroupBy or Distinct with custom comparers.
- Use Distinct for simple duplicates
- GroupBy to keep first/last occurrence
- Custom comparer for complex objects
- Preserve order if needed
// LINQ for Data Deduplication
using System;
using System.Linq;
using System.Collections.Generic;
public class DedupeExample
{
public class Person
{
public int Id { get; set; }
public string Email { get; set; }
}
public static void Main()
{
var people = new List<Person>
{
new Person { Id = 1, Email = "a@x.com" },
new Person { Id = 2, Email = "b@y.com" },
new Person { Id = 3, Email = "a@x.com" }
};
var unique = people
.GroupBy(p => p.Email)
.Select(g => g.First())
.ToList();
Console.WriteLine($"Unique count: {unique.Count}"); // 2
}
}Aggregation patterns include summing, averaging, counting, and grouping with aggregation to produce summaries.
- Sum, Average, Count, Min, Max
- GroupBy with aggregation
- Running totals
- Weighted averages
// LINQ for Data Aggregation Patterns
using System;
using System.Linq;
using System.Collections.Generic;
public class AggregationPatternsExample
{
public class Sale
{
public string Region { get; set; }
public decimal Amount { get; set; }
}
public static void Main()
{
var sales = new List<Sale>
{
new Sale { Region = "North", Amount = 100 },
new Sale { Region = "South", Amount = 200 },
new Sale { Region = "North", Amount = 150 }
};
var totals = sales
.GroupBy(s => s.Region)
.Select(g => new { Region = g.Key, Total = g.Sum(s => s.Amount) });
foreach (var item in totals)
Console.WriteLine($"{item.Region}: ${item.Total}");
}
}Event processing uses LINQ to filter, sequence, and correlate events for analysis.
- Filter by event type
- Order by timestamp
- Calculate durations
- Detect patterns
// LINQ for Event Processing
using System;
using System.Linq;
using System.Collections.Generic;
public class EventProcessingExample
{
public class Event
{
public string Type { get; set; }
public DateTime Time { get; set; }
}
public static void Main()
{
var events = new List<Event>
{
new Event { Type = "Start", Time = DateTime.Now },
new Event { Type = "End", Time = DateTime.Now.AddSeconds(5) }
};
var duration = events
.Where(e => e.Type == "Start" || e.Type == "End")
.OrderBy(e => e.Time)
.Select(e => e.Time)
.ToList();
if (duration.Count == 2)
Console.WriteLine($"Duration: {(duration[1] - duration[0]).TotalSeconds}s");
}
}Stream processing applies transformations to sequences, often using lazy evaluation and pipelining.
- Use Select, Where, etc.
- Chaining operators
- Materialize with ToList() when needed
- Process infinite streams with caution
// LINQ for Stream Processing
using System;
using System.Linq;
using System.Collections.Generic;
public class StreamProcessingExample
{
public static void Main()
{
var data = Enumerable.Range(1, 10);
var processed = data
.Select(x => x * x)
.Where(x => x % 2 == 0)
.ToList();
Console.WriteLine(string.Join(", ", processed)); // 4, 16, 36, 64, 100
}
}Data compression reduces a sequence by grouping and counting occurrences, creating a run‑length encoding.
- Group by value
- Count occurrences
- Create compressed representation
- Useful for data deduplication
// LINQ for Data Compression
using System;
using System.Linq;
using System.Collections.Generic;
public class CompressionExample
{
public static void Main()
{
var numbers = new int[] { 1, 1, 2, 2, 3, 4, 4, 4 };
var compressed = numbers
.GroupBy(n => n)
.Select(g => new { Value = g.Key, Count = g.Count() });
foreach (var item in compressed)
Console.WriteLine($"{item.Value}: {item.Count}");
}
}Caching with LINQ stores results of expensive queries and reuses them, often using a dictionary cache.
- Use a dictionary as cache
- Check cache before executing
- Store computed results
- Consider expiration strategies
// LINQ for Caching
using System;
using System.Linq;
using System.Collections.Generic;
public class CachingExample
{
private static readonly Dictionary<string, object> Cache = new Dictionary<string, object>();
public static T GetOrAdd<T>(string key, Func<T> factory)
{
if (Cache.TryGetValue(key, out var value)) return (T)value;
var result = factory();
Cache[key] = result;
return result;
}
public static void Main()
{
var data = GetOrAdd("numbers", () => Enumerable.Range(1, 10).ToList());
Console.WriteLine($"Cached count: {data.Count}");
// Second call uses cache
var data2 = GetOrAdd("numbers", () => Enumerable.Range(1, 20).ToList());
Console.WriteLine($"Cached count (again): {data2.Count}"); // 10 (cached)
}
}Data synchronization compares two datasets and determines what to add, update, or delete.
- Use Join to find matches
- Identify inserts, updates, deletes
- Generate sync operations
- Work with different data shapes
// LINQ for Data Synchronization
using System;
using System.Linq;
using System.Collections.Generic;
public class SyncExample
{
public class LocalItem { public int Id { get; set; } public string Data { get; set; } }
public class RemoteItem { public int Id { get; set; } public string Data { get; set; } }
public static void Main()
{
var local = new List<LocalItem> { new LocalItem { Id = 1, Data = "A" } };
var remote = new List<RemoteItem> { new RemoteItem { Id = 1, Data = "B" }, new RemoteItem { Id = 2, Data = "C" } };
// Items to add (in remote not in local)
var toAdd = remote
.Where(r => !local.Any(l => l.Id == r.Id))
.Select(r => new LocalItem { Id = r.Id, Data = r.Data });
// Items to update (in both but different data)
var toUpdate = local
.Join(remote, l => l.Id, r => r.Id, (l, r) => new { Local = l, Remote = r })
.Where(x => x.Local.Data != x.Remote.Data)
.Select(x => new LocalItem { Id = x.Local.Id, Data = x.Remote.Data });
Console.WriteLine($"To add: {toAdd.Count()}, To update: {toUpdate.Count()}");
}
}Workflow processing uses LINQ to find the next pending step or to filter completed steps.
- Order steps by sequence
- Find first incomplete step
- Query status of each step
- Aggregate progress
// LINQ for Workflow Processing
using System;
using System.Linq;
using System.Collections.Generic;
public class WorkflowExample
{
public class Step
{
public int Order { get; set; }
public string Name { get; set; }
public bool IsCompleted { get; set; }
}
public static void Main()
{
var steps = new List<Step>
{
new Step { Order = 1, Name = "Init", IsCompleted = true },
new Step { Order = 2, Name = "Process", IsCompleted = false },
new Step { Order = 3, Name = "Finalize", IsCompleted = false }
};
var current = steps
.OrderBy(s => s.Order)
.FirstOrDefault(s => !s.IsCompleted);
Console.WriteLine($"Next step: {current?.Name ?? "All complete"}");
}
}Best practices for LINQ include using appropriate data structures, avoiding multiple enumerations, and preferring method syntax for complex queries.
- Use
Any()instead ofCount() > 0 - Materialize with ToList() when reusing
- Use HashSet for O(1) lookups
- Prefer method syntax for complex queries
- Use query syntax for readability
// LINQ Best Practices
using System;
using System.Linq;
using System.Collections.Generic;
public class BestPracticesExample
{
public static void Main()
{
// 1. Use appropriate data structures
var hashSet = new HashSet<int> { 1, 2, 3 };
var contains = hashSet.Contains(2); // O(1)
// 2. Avoid multiple enumerations
var numbers = Enumerable.Range(1, 1000);
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList(); // Materialize
var count = evenNumbers.Count();
var sum = evenNumbers.Sum();
// 3. Use Any() instead of Count() > 0
var hasAny = numbers.Any(n => n > 100);
// 4. Use FirstOrDefault() with default when appropriate
var first = numbers.FirstOrDefault(n => n > 9999, -1);
// 5. Prefer method syntax for complex queries, query syntax for readability
var query = from n in numbers
where n % 2 == 0
select n * n;
Console.WriteLine("Best practices applied");
}
}