These questions focus on practical collection operations, LINQ, Dictionary, grouping, sorting, the Repository Pattern, and thread-safe caching in ASP.NET Core.
1. How do you remove duplicate integers from a list?
Explanation
We can remove duplicate integers using:
-
Distinct()— LINQ method that returns unique elements. -
HashSet<T>— Automatically stores unique elements.
Example Program
using System;
using System.Collections.Generic;
using System.Linq;
List<int> numbers = new() { 10, 20, 10, 30, 20, 40 };
List<int> uniqueNumbers = numbers
.Distinct()
.ToList();
Console.WriteLine(string.Join(", ", uniqueNumbers));
Output:
10, 20, 30, 40
Explanation of Code
-
Distinct()eliminates duplicate values using equality comparison. -
ToList()converts the result into a new list. -
The original list remains unchanged.
Alternative: Using HashSet
HashSet<int> uniqueNumbers = new(numbers);
Diagram
Original List
[10, 20, 10, 30, 20, 40]
|
v
Distinct()
|
v
[10, 20, 30, 40]
Key Points
-
Distinct()is the simplest LINQ solution. -
HashSet<T>prevents duplicate values. -
Average time complexity is O(n) with hash-based equality.
-
Distinct()preserves first-occurrence order in current .NET implementations, but its API does not guarantee ordering. Sort explicitly if order matters.
2. How do you find the second-highest number in a list?
Explanation
To find the second-highest distinct number:
-
Remove duplicates.
-
Sort numbers in descending order.
-
Skip the highest number.
-
Return the next number.
Example Program
using System;
using System.Collections.Generic;
using System.Linq;
List<int> numbers = new()
{
10, 50, 30, 50, 40
};
int? secondHighest = numbers
.Distinct()
.OrderByDescending(x => x)
.Skip(1)
.Select(x => (int?)x)
.FirstOrDefault();
Console.WriteLine(secondHighest?.ToString() ?? "Not found");
Output:
40
Diagram
Original
[10, 50, 30, 50, 40]
|
v
Distinct()
[10, 50, 30, 40]
|
v
OrderByDescending()
[50, 40, 30, 10]
|
v
Skip(1)
[40, 30, 10]
|
v
FirstOrDefault()
|
v
40
Key Points
-
Distinct()removes duplicate numbers. -
OrderByDescending()sorts from highest to lowest. -
Skip(1)skips the highest distinct number. -
Sorting takes O(n log n) time.
-
If fewer than two distinct numbers exist, the result is
null. -
An O(n) solution is possible by tracking the highest and second-highest values in one pass.
3. How do you count the frequency of each word using Dictionary?
Explanation
A Dictionary<string, int> stores each word as a key and its occurrence count as the value.
Key Value
----------------
CSharp 3
SQL 2
Azure 1
Example Program
using System;
using System.Collections.Generic;
string[] words =
{
"CSharp", "SQL", "CSharp",
"Azure", "SQL", "CSharp"
};
Dictionary<string, int> frequency = new();
foreach (string word in words)
{
if (frequency.ContainsKey(word))
{
frequency[word]++;
}
else
{
frequency[word] = 1;
}
}
foreach (var item in frequency)
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
Output:
CSharp: 3
SQL: 2
Azure: 1
Explanation of Code
-
ContainsKey()checks whether the word already exists. -
If it exists, increment the count.
-
Otherwise, insert the word with a count of 1.
Alternative: Using LINQ
var frequency = words
.GroupBy(x => x)
.ToDictionary(g => g.Key, g => g.Count());
Key Points
-
Dictionary stores unique keys.
-
Average lookup and update complexity is O(1).
-
Overall counting takes O(n) on average.
-
Use
StringComparer.OrdinalIgnoreCasewhen case-insensitive counting is required. -
Useful for log analysis, text processing, and reporting.
4. How do you find duplicate elements in a collection?
Explanation
We can use LINQ GroupBy() to group identical values and select groups whose count is greater than one.
Example Program
using System;
using System.Collections.Generic;
using System.Linq;
List<int> numbers = new()
{
10, 20, 30, 20, 40, 10, 50
};
var duplicates = numbers
.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => new
{
Number = g.Key,
Count = g.Count()
});
foreach (var item in duplicates)
{
Console.WriteLine($"{item.Number}: {item.Count}");
}
Output:
10: 2
20: 2
Diagram
Input
[10, 20, 30, 20, 40, 10, 50]
|
v
GroupBy()
|
v
10 -> 2
20 -> 2
30 -> 1
40 -> 1
50 -> 1
|
v
Count() > 1
|
v
10, 20
Key Points
-
GroupBy()groups matching elements. -
Where()filters groups containing duplicates. -
Count()determines the number of occurrences. -
Hash-based grouping is O(n) on average.
-
Useful for identifying duplicate IDs, transaction references, or records.
5. How do you group employees by department?
Explanation
LINQ GroupBy() groups employees based on a common property, such as Department.
Example Program
using System;
using System.Collections.Generic;
using System.Linq;
List<Employee> employees = new()
{
new Employee { Id = 1, Name = "John", Department = "IT" },
new Employee { Id = 2, Name = "David", Department = "HR" },
new Employee { Id = 3, Name = "Peter", Department = "IT" },
new Employee { Id = 4, Name = "Mary", Department = "HR" }
};
var groups = employees
.GroupBy(e => e.Department);
foreach (var group in groups)
{
Console.WriteLine($"Department: {group.Key}");
foreach (var employee in group)
{
Console.WriteLine(employee.Name);
}
}
class Employee
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Department { get; set; } = "";
}
Output:
Department: IT
John
Peter
Department: HR
David
Mary
Diagram
Employees
|
v
GroupBy(Department)
|
+---- IT
| |
| +---- John
| +---- Peter
|
+---- HR
|
+---- David
+---- Mary
Explanation of Code
-
GroupBy(e => e.Department)creates groups by department. -
group.Keyrepresents the department name. -
Each group contains the employees belonging to that department.
Key Points
-
GroupBy()is used to organize records by a common property. -
group.Keyidentifies the group. -
Multiple properties can be used for grouping.
-
Useful for department reports, sales summaries, and dashboards.
-
In EF Core, grouping and aggregation can often be executed directly in SQL Server.
6. How do you sort employees by salary and name?
Explanation
Use OrderBy() for primary sorting and ThenBy() for secondary sorting.
Suppose the requirement is:
-
Salary — highest to lowest.
-
Name — alphabetical when salaries are equal.
Example Program
using System;
using System.Collections.Generic;
using System.Linq;
List<Employee> employees = new()
{
new Employee { Name = "John", Salary = 50000 },
new Employee { Name = "David", Salary = 70000 },
new Employee { Name = "Peter", Salary = 50000 },
new Employee { Name = "Adam", Salary = 70000 }
};
var sortedEmployees = employees
.OrderByDescending(e => e.Salary)
.ThenBy(e => e.Name);
foreach (var employee in sortedEmployees)
{
Console.WriteLine($"{employee.Name}: {employee.Salary}");
}
class Employee
{
public string Name { get; set; } = "";
public decimal Salary { get; set; }
}
Output:
Adam: 70000
David: 70000
John: 50000
Peter: 50000
Diagram
Employees
|
v
OrderByDescending(Salary)
|
v
70000 -> Adam, David
50000 -> John, Peter
|
v
ThenBy(Name)
|
v
Adam -> 70000
David -> 70000
John -> 50000
Peter -> 50000
Key Points
-
OrderBy()sorts ascending. -
OrderByDescending()sorts descending. -
ThenBy()performs secondary ascending sorting. -
ThenByDescending()performs secondary descending sorting. -
Calling another
OrderBy()starts a new primary sort rather than adding a secondary sort. -
Sorting typically takes O(n log n) time.
7. How do you implement a generic repository in ASP.NET Core?
What is a Generic Repository?
A Generic Repository provides reusable data access operations for different entity types.
Instead of writing separate CRUD implementations for every entity, we create a generic interface and implementation.
Architecture Diagram
ASP.NET Core Web API
|
v
Controller
|
v
IGenericRepository<T>
|
v
GenericRepository<T>
|
v
EF Core DbContext
|
v
SQL Server
Step 1: Create Entity
public class Employee
{
public int Id { get; set; }
public string Name { get; set; } = "";
public decimal Salary { get; set; }
}
Step 2: Create DbContext
using Microsoft.EntityFrameworkCore;
public class AppDbContext : DbContext
{
public AppDbContext(
DbContextOptions<AppDbContext> options)
: base(options)
{
}
public DbSet<Employee> Employees => Set<Employee>();
}
Step 3: Create Generic Repository Interface
public interface IGenericRepository<T> where T : class
{
Task<T?> GetByIdAsync(int id);
Task<List<T>> GetAllAsync();
Task AddAsync(T entity);
void Update(T entity);
void Delete(T entity);
Task SaveChangesAsync();
}
Step 4: Implement Generic Repository
using Microsoft.EntityFrameworkCore;
public class GenericRepository<T> : IGenericRepository<T>
where T : class
{
private readonly AppDbContext _context;
private readonly DbSet<T> _dbSet;
public GenericRepository(AppDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
public async Task<T?> GetByIdAsync(int id)
{
return await _dbSet.FindAsync(id);
}
public async Task<List<T>> GetAllAsync()
{
return await _dbSet.ToListAsync();
}
public async Task AddAsync(T entity)
{
await _dbSet.AddAsync(entity);
}
public void Update(T entity)
{
_dbSet.Update(entity);
}
public void Delete(T entity)
{
_dbSet.Remove(entity);
}
public async Task SaveChangesAsync()
{
await _context.SaveChangesAsync();
}
}
Step 5: Register Dependencies in Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(
builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped(
typeof(IGenericRepository<>),
typeof(GenericRepository<>));
Step 6: Use Repository in Controller
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class EmployeesController : ControllerBase
{
private readonly IGenericRepository<Employee> _repository;
public EmployeesController(
IGenericRepository<Employee> repository)
{
_repository = repository;
}
[HttpGet]
public async Task<IActionResult> GetAll()
{
var employees = await _repository.GetAllAsync();
return Ok(employees);
}
[HttpPost]
public async Task<IActionResult> Create(Employee employee)
{
await _repository.AddAsync(employee);
await _repository.SaveChangesAsync();
return Ok(employee);
}
}
Example API Response
GET /api/employees
[
{
"id": 1,
"name": "John",
"salary": 50000
},
{
"id": 2,
"name": "David",
"salary": 70000
}
]
Explanation
-
Controller depends on
IGenericRepository<Employee>. -
Dependency Injection provides
GenericRepository<Employee>. -
Repository uses EF Core to access SQL Server.
-
SaveChangesAsync()persists changes.
Key Points
-
Generic repositories reduce repeated CRUD code.
-
where T : classrestricts the generic type to reference types. -
DbSet<T>represents the entity set. -
Use scoped lifetime for EF Core repositories.
-
A generic repository is not always necessary because EF Core already implements Repository and Unit of Work concepts.
-
For production applications, add pagination, validation, cancellation tokens, error handling, and transaction management.
-
In Clean Architecture, repository interfaces usually belong in the Application layer, while EF Core implementations belong in Infrastructure.
8. Which collection would you use for caching user details by UserId?
Explanation
For caching user details by UserId, use a Dictionary when access is single-threaded or externally synchronized.
For concurrent applications, use ConcurrentDictionary<TKey, TValue>.
Example Program
using System;
using System.Collections.Generic;
Dictionary<int, string> userCache = new()
{
{ 101, "John" },
{ 102, "David" },
{ 103, "Peter" }
};
if (userCache.TryGetValue(102, out string? userName))
{
Console.WriteLine(userName);
}
Output:
David
Diagram
Request UserId = 102
|
v
Dictionary<int, string>
|
v
Lookup Key 102
|
v
Return "David"
Explanation
-
UserIdis the dictionary key. -
User details are stored as the value.
-
TryGetValue()retrieves the value safely. -
Average lookup complexity is O(1).
Real-Time Scenario
In ASP.NET Core, user information may be cached to avoid repeated SQL Server queries.
Web API Request
|
v
Check User Cache
|
+---- Found ----> Return Cached User
|
+---- Missing
|
v
SQL Server
|
v
Cache Result
|
v
Return User
Key Points
-
Dictionary provides average O(1) lookup.
-
Use
ConcurrentDictionaryfor concurrent access. -
Use
IMemoryCachewhen expiration and eviction are required. -
Use Redis for shared caching across multiple application servers.
-
Avoid caching sensitive user data unnecessarily.
9. How do you implement a thread-safe in-memory cache?
Explanation
A thread-safe cache allows multiple threads to read and update cached data without corrupting the collection.
ConcurrentDictionary<TKey, TValue> supports thread-safe dictionary operations.
Namespace:
using System.Collections.Concurrent;
Example Program
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
ConcurrentDictionary<int, string> cache = new();
Parallel.For(1, 6, id =>
{
cache.TryAdd(id, $"User{id}");
});
foreach (var item in cache.OrderBy(x => x.Key))
{
Console.WriteLine($"{item.Key}: {item.Value}");
}
Output:
1: User1
2: User2
3: User3
4: User4
5: User5
Explanation of Code
-
ConcurrentDictionaryallows concurrent access. -
Parallel.For()performs iterations concurrently. -
TryAdd()adds a value only when its key does not already exist. -
The final output is sorted because concurrent insertion order is not guaranteed.
Diagram
Thread 1 ----+
|
Thread 2 ----+
|
Thread 3 ----+----> ConcurrentDictionary
| |
Thread 4 ----+ v
| Thread-Safe Operations
Thread 5 ----+ |
v
Cache Data
ASP.NET Core Example Using IMemoryCache
For production applications, IMemoryCache is often more appropriate because it supports expiration and eviction.
Step 1: Register Memory Cache
builder.Services.AddMemoryCache();
Step 2: Create Cache Service
using Microsoft.Extensions.Caching.Memory;
public class UserCacheService
{
private readonly IMemoryCache _cache;
public UserCacheService(IMemoryCache cache)
{
_cache = cache;
}
public string GetUser(int userId)
{
return _cache.GetOrCreate(
$"user_{userId}",
entry =>
{
entry.AbsoluteExpirationRelativeToNow =
TimeSpan.FromMinutes(10);
return $"User{userId}";
})!;
}
}
Step 3: Register Service
builder.Services.AddScoped<UserCacheService>();
Explanation
-
IMemoryCachesupports concurrent cache access. -
Cached entries expire after 10 minutes.
-
Expired entries can be removed and recreated.
-
The cache is local to the application instance.
Important: GetOrCreate() does not guarantee that the factory executes only once for concurrent cache misses. Multiple requests may execute the factory simultaneously. Use explicit synchronization or a single-flight mechanism when duplicate loading must be prevented.
Key Points
-
ConcurrentDictionaryis suitable for thread-safe key-value storage. -
IMemoryCachesupports expiration and eviction. -
Thread-safe dictionary operations do not automatically make multi-step workflows atomic.
-
GetOrAdd()factories can execute multiple times concurrently. -
Use Redis or another distributed cache when multiple servers must share cached data.
-
Cache invalidation is important when underlying data changes.
10. How do you convert a list of objects into a Dictionary?
Explanation
Use LINQ ToDictionary() to convert a list of objects into key-value pairs.
For example, convert employees into a dictionary where:
-
Key = Employee ID
-
Value = Employee object
Example Program
using System;
using System.Collections.Generic;
using System.Linq;
List<Employee> employees = new()
{
new Employee { Id = 101, Name = "John" },
new Employee { Id = 102, Name = "David" },
new Employee { Id = 103, Name = "Peter" }
};
Dictionary<int, Employee> employeeDict =
employees.ToDictionary(e => e.Id);
foreach (var item in employeeDict)
{
Console.WriteLine($"{item.Key}: {item.Value.Name}");
}
class Employee
{
public int Id { get; set; }
public string Name { get; set; } = "";
}
Output:
101: John
102: David
103: Peter
Diagram
List<Employee>
|
v
ToDictionary()
|
v
Dictionary<int, Employee>
|
+---- 101 -> John
|
+---- 102 -> David
|
+---- 103 -> Peter
Explanation of Code
employees.ToDictionary(e => e.Id);
-
Uses
Idas the dictionary key. -
Stores the entire
Employeeobject as the value. -
Keys must be unique.
Alternative: Store Only Employee Name
Dictionary<int, string> employeeDict =
employees.ToDictionary(
e => e.Id,
e => e.Name);
Handling Duplicate Keys
ToDictionary() throws an ArgumentException if duplicate keys are found.
If duplicate employee IDs are possible and you want to keep the first employee:
var employeeDict = employees
.GroupBy(e => e.Id)
.ToDictionary(
g => g.Key,
g => g.First());
This explicitly chooses the first employee for each ID. In business applications, duplicate keys may indicate invalid data, so validate them rather than silently discarding records unless that behavior is intended.
Key Points
-
ToDictionary()converts a sequence into a dictionary. -
A key selector is required.
-
A value selector is optional.
-
Duplicate keys cause an exception.
-
Average conversion complexity is O(n).
-
Dictionary lookup is O(1) on average.
Final Summary: Important Methods and Concepts
|
Question |
Recommended Approach |
Important Method |
|---|---|---|
|
Remove duplicates |
LINQ / HashSet |
|
|
Second-highest number |
LINQ sorting |
|
|
Count word frequency |
Dictionary |
|
|
Find duplicates |
LINQ grouping |
|
|
Group employees |
LINQ |
|
|
Sort employees |
LINQ |
|
|
Generic Repository |
Generics + EF Core |
|
|
Cache by UserId |
Dictionary / Cache |
|
|
Thread-safe cache |
ConcurrentDictionary / IMemoryCache |
|
|
List to Dictionary |
LINQ |
|
Most Important Interview Takeaways
Q1. Which LINQ methods are most important for collection coding interviews?
Where(), Select(), GroupBy(), OrderBy(), ThenBy(), Distinct(), Any(), FirstOrDefault(), and ToDictionary().
Q2. Which collection is best for removing duplicates?
HashSet<T> or LINQ Distinct().
Q3. Which collection is best for caching user details?
ConcurrentDictionary<TKey,TValue> for simple concurrent key-value storage, IMemoryCache for local caching with expiration, and Redis for distributed caching.
Q4. What is the difference between GroupBy() and ToDictionary()?
GroupBy() creates groups of elements sharing a key. ToDictionary() creates a key-value lookup and requires unique keys.
Q5. Is Generic Repository mandatory in ASP.NET Core?
No. EF Core already provides repository-like functionality. A custom generic repository is useful when it provides meaningful abstraction or reusable application-specific behavior.
Q6. What are the most important real-time coding scenarios for a .NET developer?
Focus on removing duplicates, finding the second-highest value, counting frequencies, grouping and sorting records, dictionary lookups, and implementing caching with correct concurrency handling.