Interview Questions
Every question has a short model answer and a link to the lesson example that shows it in code. Levels: F = Fresher (0-2 yrs), M = Mid (3-5 yrs), S = Senior (6-9 yrs), A = Architect / Lead (10+ yrs).
| Topic | F | M | S | A | Total |
|---|---|---|---|---|---|
| SQL Server | 89 | 89 | 59 | 27 | 264 |
| C# | 22 | 22 | 14 | 7 | 65 |
| OOP | 27 | 27 | 18 | 9 | 81 |
| Advanced C# | 27 | 27 | 18 | 9 | 81 |
| Data Access | 24 | 24 | 16 | 8 | 72 |
| Architecture | 14 | 13 | 10 | 4 | 41 |
| Web API | 93 | 94 | 60 | 30 | 277 |
| Security | 15 | 16 | 10 | 6 | 47 |
| System Design | 3 | 3 | 2 | 3 | 11 |
| Leadership | 1 | 2 | 3 | 0 | 6 |
SQL Server
Tools
FWhat is the difference between SQL Server and SSMS?concept
SQL Server is the database server: a service that stores the data and runs queries. SSMS (SQL Server Management Studio) is a client tool that connects to SQL Server so you can write queries and manage objects. Uninstalling SSMS does not touch your data.
FWhat is LocalDB, and when would you use it instead of the Developer edition?concept
LocalDB is a lightweight version of SQL Server that starts on demand and needs no
service setup; its server name is (localdb)\MSSQLLocalDB. It suits laptops with little
memory and simple development. The Developer edition is a full server with every feature,
such as SQL Server Agent, so it is better for realistic development and testing.
SWhich SQL Server edition would you use for development, testing, and the school's live server?design
Developer edition for development and testing: it has every feature and is free, but it is licensed only for non-production use. For the live server, Express is free but limited (for example, 10 GB per database). Most real schools need Standard edition, or a managed cloud database, depending on size, availability needs, and budget.
Separate the three environments and the licensing rule first. Size the SMS: one school's data fits Express for years, but 1,000 schools does not. Then weigh the trade-off: Express saves cost but has no SQL Server Agent for scheduled backups, so you would need another way to schedule them; Standard or a managed service costs more but handles backups and availability for you.
Scripts
MHow do you write a database script that can run more than once without errors?coding
Check before you create or change anything. Use IF DB_ID(N'SchoolDb') IS NULL before
CREATE DATABASE, IF OBJECT_ID(N'dbo.Students') IS NULL before CREATE TABLE, and
CREATE OR ALTER for views and procedures. Such scripts are called idempotent: running
them twice leaves the same result as running them once.
MWhat does GO do in a SQL script?concept
GO is not a T-SQL command; SSMS and sqlcmd use it to split a script into batches and
send each batch separately. Some statements, such as CREATE VIEW or CREATE PROCEDURE,
must be the first statement in a batch. Variables declared before a GO do not exist
after it.
RDBMS basics
FWhat is a relational database (RDBMS)?concept
A relational database stores data in tables made of rows and columns, and links tables to each other with keys. An RDBMS (Relational Database Management System), such as SQL Server, is the software that stores the data, runs queries, and enforces rules so many users can work safely at the same time.
See the example · Lesson 5: RDBMS concepts and SSMS · Permalink
FWhat are a table, a row, and a column?concept
A table stores one kind of thing, such as students. A row is one item of that thing,
such as one student. A column is one detail that every row has, such as DateOfBirth.
See the example · Lesson 5: RDBMS concepts and SSMS · Permalink
FWhat is the difference between DDL and DML?concept
DDL (Data Definition Language) defines the structure: CREATE, ALTER, and DROP
for tables and other objects. DML (Data Manipulation Language) changes the data inside
them: INSERT, UPDATE, and DELETE. Creating the Students table is DDL; adding a
student is DML.
See the example · Lesson 5: RDBMS concepts and SSMS · Permalink
MWhat are the system databases in SQL Server, and what does each one do?concept
master stores server-wide information such as databases and logins. model is the
template copied for every new database. msdb stores SQL Server Agent jobs and backup
history. tempdb holds temporary objects and work tables, and is recreated at every
restart. Application tables never belong in any of them.
See the example · Lesson 5: RDBMS concepts and SSMS · Permalink
MWhat is the difference between SQL and T-SQL?concept
SQL is the standard language for relational databases. T-SQL (Transact-SQL) is
Microsoft's extension of SQL for SQL Server. It adds variables, control flow such as
IF and WHILE, and error handling with TRY...CATCH.
See the example · Lesson 5: RDBMS concepts and SSMS · Permalink
MWhat is a schema such as dbo, and why write dbo.Students instead of Students?concept
A schema is a named group of objects inside a database, used for organization and
permissions; dbo is the default. Writing dbo.Students names the object exactly, so
SQL Server does not have to resolve it per user. That is clearer, avoids picking the
wrong object, and lets cached query plans be reused.
See the example · Lesson 5: RDBMS concepts and SSMS · Permalink
SA school keeps everything in spreadsheets and asks why it should move to a database. What do you tell them?scenario
A database lets many staff work at once without overwriting each other, enforces rules such as unique admission numbers and valid marks, and links data without copying it. It answers questions like "all of Kabir's absences this term" instantly, and adds security, backups, and an audit trail of who changed what.
Lead with the pain they already feel: lost changes, inconsistent copies, slow searches. Map each pain to a database feature with an SMS example. Be honest about the cost: setup, training, and someone to own backups, and propose moving one module first.
See the example · Lesson 5: RDBMS concepts and SSMS · Permalink
SWhat is tempdb used for, and why does it matter for performance?concept
tempdb holds temporary tables and table variables, and work areas for sorting, hashing,
and row versioning. Every database on the server shares it. A busy or badly sized
tempdb slows down every application on that server, so it is usually given several
data files on fast storage.
Explain what goes into tempdb (explicit temp objects and internal work), then why sharing makes it a bottleneck. Give an SMS case: a large report that sorts 600,000 attendance rows spills to tempdb. Close with the trade-off: more files and fast disks help, but fixing the query so it sorts less helps more.
See the example · Lesson 5: RDBMS concepts and SSMS · Permalink
AWould you use a relational database or a NoSQL database for a School Management System? Why?design
A relational database. School data has a stable structure and strict rules: every mark belongs to a real enrollment, and fees must add up exactly across invoices and payments. Constraints and transactions enforce exactly that. NoSQL fits flexible documents or huge write-heavy workloads, which is not the core of a school system.
Start from the data's shape and rules, not from fashion. Name the relational strengths the SMS needs: constraints, joins, and transactions for fees. Then show balance: a NoSQL store could still fit a side feature, such as an activity feed or a search index, next to the relational core.
See the example · Lesson 5: RDBMS concepts and SSMS · Permalink
Data modeling
FWhat is an ER diagram, and what are entities and attributes?concept
An ER (Entity-Relationship) diagram is a picture of what a database stores and how the parts connect. An entity is a thing to remember, such as a student, and becomes a table. An attribute is a detail of an entity, such as a date of birth, and becomes a column.
FWhat are one-to-one, one-to-many, and many-to-many relationships?concept
One-to-one: one row on each side at most, such as a teacher and their login. One-to-many: one row links to many, such as a class and its sections. Many-to-many: many on both sides, such as students and guardians, which needs a junction table.
FGive an example of a one-to-many relationship and say where the foreign key goes.concept
A class has many sections, and each section belongs to one class. The foreign key always
goes on the "many" side, so Sections has a ClassId column that points to Classes.
MHow do you implement a many-to-many relationship in tables?concept
Add a junction table with a foreign key to each side, and make the pair of keys its
primary key. For students and guardians, StudentGuardians has StudentId and
GuardianId; each row is one link.
MHow do you read crow's foot notation?concept
The symbols at each end of a line show how many rows can be on that side: || is exactly
one, o| is zero or one, |{ is one or more, and o{ is zero or more. The three-pronged
"crow's foot" means many. Classes ||--|{ Sections reads "one class has one or more sections".
MWhat is a junction table, and can it have columns of its own?concept
A junction table connects two tables in a many-to-many relationship. Yes, it can hold
facts about the link itself. StudentGuardians stores Relationship (Father, Mother,
Guardian) and IsPrimaryContact, because those describe the pair, not either side alone.
SWhy does the SMS have an Enrollments table instead of a SectionId column on Students?scenario
A student changes section every year. A SectionId on Students would overwrite last
year's section and break the history of attendance, marks, and fees. Enrollments
keeps one row per student per academic year, and everything year-specific links to it.
Explain that the section is a fact about a student in a given year, not about the student forever. Walk through Aarav in 9-A last year and 10-A this year. Mention the trade-off: queries need one more join, which is cheap compared to losing history.
SHow do you model a relationship that changes over time, such as which teacher teaches a subject?design
Make the relationship its own table and add the period it is valid for. In the SMS,
SubjectAllocations links teacher, subject, section, and academic year, with a unique
rule on (year, section, subject). Next year gets new rows, and old years stay correct
for reports.
State the pattern (a time-scoped association table). Use the SMS allocation example and show how a past report card still shows the right teacher. Discuss the alternative of start and end dates, which handles mid-year changes but makes queries more complex.
AHow would you design attendance so it can later become per period without breaking existing data?design
Keep attendance in its own table linked to the enrollment, and plan an optional period column now. Daily records leave it empty; period records fill it, with a unique rule on enrollment, date, and period. Reports read both, so old data stays valid and new data adds detail.
Show that you design for likely change without building it all now. Describe the migration path (add a nullable column, add a new unique index, backfill nothing). Weigh it against a separate PeriodAttendance table, which is cleaner but means every report must read two tables.
Normalization
FWhat is normalization, and why do we do it?concept
Normalization is a set of rules for splitting tables so that every fact is stored once. It prevents update, insert, and delete anomalies, where copies of the same fact disagree or are lost. The usual target for business systems is third normal form.
See the example · Lesson 7: Normalization 1NF-3NF · Permalink
FWhat is first normal form (1NF)?concept
A table is in 1NF when every cell holds a single value, there are no repeating columns
such as Guardian1 and Guardian2, and every row can be identified by a key. A cell
containing "Rajesh Sharma, Neha Sharma" breaks 1NF.
See the example · Lesson 7: Normalization 1NF-3NF · Permalink
FWhat are update, insert, and delete anomalies?concept
An update anomaly: a fact stored twice is changed in one place only, so copies disagree. An insert anomaly: you cannot record one fact without another, such as a class teacher without a student. A delete anomaly: deleting one row removes an unrelated fact, such as a guardian's phone.
See the example · Lesson 7: Normalization 1NF-3NF · Permalink
MExplain second normal form with an example.concept
A table is in 2NF when it is in 1NF and every non-key column depends on the whole key.
In a table keyed by (AdmissionNumber, GuardianName), StudentName depends only on the
admission number: a partial dependency. Moving student details to Students and guardian
details to Guardians fixes it.
See the example · Lesson 7: Normalization 1NF-3NF · Permalink
MExplain third normal form with an example.concept
A table is in 3NF when it is in 2NF and no non-key column depends on another non-key
column. ClassTeacher on Students depends on the section, which depends on the
student: a transitive dependency. Moving the class teacher to ClassTeachers (per
section and year) fixes it.
See the example · Lesson 7: Normalization 1NF-3NF · Permalink
MWhat is the difference between a partial dependency and a transitive dependency?concept
A partial dependency is a column that depends on only part of a composite key; 2NF removes it. A transitive dependency is a column that depends on the key only through another non-key column; 3NF removes it. Short test: a column must depend on "the key, the whole key, and nothing but the key".
See the example · Lesson 7: Normalization 1NF-3NF · Permalink
SWhen would you denormalize, and how do you keep the copied data correct?concept
Denormalize when a measured performance or history need justifies a copy, such as an
invoice total stored on FeeInvoices. Keep it correct with one owner: write it in the
same transaction as the source rows, block other updates, or rebuild it on a schedule.
Without an owner, copies drift.
Make clear denormalization is a deliberate trade-off, then give the SMS invoice total example with both reasons (speed, and a total that must not change after issue). Name the cost: more code paths to keep in sync, and the risk of drift if anyone forgets.
See the example · Lesson 7: Normalization 1NF-3NF · Permalink
SThe fee report joins six tables and is slow. A developer wants to copy the student name onto FeePayments. What do you say?scenario
First measure: most slow joins are missing indexes or return too many rows, and copying the name fixes neither. If the report is still slow after indexing, consider a reporting view or summary table rebuilt on a schedule. Copying names onto payments creates a second place to update whenever a name is corrected.
Show you diagnose before redesigning: check the execution plan and indexes. Explain why this particular copy is risky (names get corrected and the copy goes stale). Offer the better options in order of cost: indexes, a narrower query, then a reporting table with a clear refresh owner.
See the example · Lesson 7: Normalization 1NF-3NF · Permalink
Data types
FWhat is the difference between VARCHAR and NVARCHAR?concept
VARCHAR stores text in a limited code page, usually one byte per character, so letters
outside it become ?. NVARCHAR stores Unicode, so any language works, at about two
bytes per character. Use NVARCHAR for names, and write Unicode literals as N'...'.
FWhy should money use DECIMAL and not FLOAT?concept
FLOAT stores an approximation in binary, so values like 0.1 cannot be stored exactly
and totals drift by tiny amounts. DECIMAL stores exact decimal digits. In SQL Server,
0.1 + 0.2 - 0.3 as FLOAT is about 5.55E-17, while as DECIMAL it is exactly 0.00.
FWhat is the difference between DATE and DATETIME2?concept
DATE stores only the day, which suits a date of birth or an attendance date.
DATETIME2 stores the day and the time, with precision you choose; DATETIME2(0) keeps
whole seconds. DATETIME2 replaces the older DATETIME, with a wider range and better
precision.
MHow do you choose between TINYINT, SMALLINT, INT, and BIGINT?concept
Pick the smallest type that safely holds the largest value the column will ever need.
TINYINT covers 0 to 255, SMALLINT about ±32,000, INT about ±2.1 billion, and
BIGINT far more. For tables that grow with time, estimate rows per year times years of
growth; attendance across many schools needs BIGINT.
MWhen would you use CHAR instead of VARCHAR?concept
Use CHAR(n) only when every value has the same short length, because it always uses
n bytes and pads with spaces. Gender CHAR(1) is a good fit. For values of varying
length, VARCHAR saves space; CHAR(50) stores a five-letter name in 50 bytes.
MWhat do precision and scale mean in DECIMAL(10, 2)?concept
Precision (10) is the total number of digits, and scale (2) is how many of them come
after the decimal point. DECIMAL(10, 2) holds values up to 99,999,999.99, which is
enough for any school fee.
SThe total of fees collected is off by 0.01 from the bank statement. How do you investigate?scenario
Check the data types first: a FLOAT or REAL amount column, or a FLOAT used in
calculations, causes exactly this kind of drift. Then check rounding in code and reports,
for example rounding each line versus rounding the total. Fix the type to DECIMAL and
recalculate from the original payments.
Give a method: compare totals at each layer (database, API, report) to find where the
difference appears. Explain the FLOAT cause with a concrete number. Close with the fix
and its cost: changing a column type on a large table needs a migration, so it is
cheaper to choose DECIMAL from the start.
SWhy store timestamps in UTC instead of local time?concept
UTC never changes with time zones or daylight saving, so every server, user, and report
agrees on the same moment. Local times break when users or servers are in different
zones, and some local times repeat or are skipped. Store UTC with SYSUTCDATETIME() and
convert only for display.
Explain the problem with a concrete case: a payment at 11:30 PM local time lands on a
different day in another time zone. Give the rule (store UTC, convert at the edges). Note
the trade-off: every display needs a conversion, and dates without a time, like a date
of birth, should stay plain DATE.
AWould you use INT IDENTITY or GUID (UNIQUEIDENTIFIER) primary keys?design
INT IDENTITY is 4 bytes and always increasing, so new rows go to the end of the index
and joins stay fast. A GUID is 16 bytes and random, which fragments indexes, but it can
be created anywhere without a central counter. Use INT or BIGINT for a single
database, and GUIDs when many systems create rows independently.
Compare size, insert order, and index fragmentation, then the cases where GUIDs win:
offline clients, merging databases, or not exposing a guessable Id in public URLs. Give
the middle road: an INT key inside the database plus a GUID "public id" column for
outside use.
Tables
FWhat does CREATE TABLE do, and what do you define for each column?concept
CREATE TABLE creates a new, empty table. For each column you define a name, a data
type, and whether it allows NULL. You usually also define the primary key, and often
other constraints, in the same statement.
FWhat is an IDENTITY column?concept
An IDENTITY(seed, increment) column is filled automatically by SQL Server.
IDENTITY(1, 1) starts at 1 and adds 1 for each new row, so you never supply the value
yourself. It is the usual way to create surrogate primary keys like Id.
FWhat is a primary key? Can it contain NULL or duplicate values?concept
A primary key is the column, or set of columns, that uniquely identifies each row. It can never contain NULL and never contain duplicates; SQL Server enforces both. A table has at most one primary key.
MWhy name constraints yourself instead of letting SQL Server name them?concept
SQL Server generates names like PK__Subjects__3214EC07A1B2C3D4, which differ on every
server. Named constraints such as PK_Subjects appear clearly in error messages and can
be referenced reliably in change scripts, for example to drop or alter them.
MDoes an IDENTITY column guarantee numbers without gaps?concept
No. A failed or rolled-back insert still uses up its number, and a server restart can skip a block of cached values. Gaps are normal: an identity value identifies a row, it does not count rows. If the business needs gapless numbers, such as invoice numbers, generate them separately inside a transaction.
MWhat is the difference between a master (lookup) table and a transaction table?concept
A master table holds lists that other data refers to and rarely changes, such as classes and subjects. A transaction table records events that happen all the time, such as attendance or payments. Transaction rows point to master rows, not the other way round.
SA deployment script fails in production with "There is already an object named 'Students'". How should deployment scripts be written?scenario
They should be idempotent: safe to run more than once. Each change checks first, for
example IF OBJECT_ID(N'dbo.Students', N'U') IS NULL before CREATE TABLE, or
COL_LENGTH before adding a column. Then a re-run after a partial failure, or a hotfix
that already applied a change, does not break the release.
Name the principle (idempotent scripts), show the check pattern for tables, columns, and constraints, and explain the real cause: environments drift. Mention the trade-off: checks add lines to every script, while migration tools track applied scripts in a table instead.
SWhy does the SMS use an Id column as primary key instead of AdmissionNumber?design
Id is a surrogate key: small, never changes, and has no business meaning.
AdmissionNumber is a natural key from the real world, and real-world values can change
or be entered wrongly; changing a primary key means updating every foreign key that
uses it. So the natural key is protected with a UNIQUE constraint instead.
Define surrogate and natural keys, then use the SMS case: a typo in an admission number must be fixable without touching attendance, marks, and fees. Note when natural keys are fine, such as small code tables that never change, and that you still enforce the natural key's uniqueness.
AHow do you manage database schema changes across development, test, and production over years?design
Every change is a versioned script stored in source control and reviewed like code. The same scripts run in the same order in every environment, usually through a migration tool that records which scripts were applied. Nobody changes production by hand.
Describe the pipeline: numbered, idempotent scripts, code review, automated deployment, and a record of applied changes. Compare approaches: migration tools that run scripts in order versus state-based tools that compare a model with the database. Mention the hard parts: data migrations on large tables, and rolling back.
Constraints
FWhat kinds of constraints does SQL Server support?concept
NOT NULL (a value is required), PRIMARY KEY (unique and not NULL), UNIQUE (no duplicates), CHECK (the value must pass a test), DEFAULT (a value when none is given), and FOREIGN KEY (the value must exist in another table). Together they keep data valid no matter which program writes it.
FWhat is the difference between a PRIMARY KEY and a UNIQUE constraint?concept
Both prevent duplicates. A table has only one primary key, and it never allows NULL. A table can have many unique constraints, and each allows one NULL. The primary key is the row's identity; unique constraints protect other business values such as a subject code.
FWhat does a foreign key do?concept
A foreign key makes a column's values refer to an existing primary key in another table. SQL Server then refuses a value that does not exist there, such as a section for class 99, and refuses to delete a row that is still referenced. This is called referential integrity.
MHow do you allow only one row to have IsCurrent = 1?coding
Use a filtered unique index: CREATE UNIQUE INDEX ... ON dbo.AcademicYears (IsCurrent) WHERE IsCurrent = 1. Uniqueness then applies only to rows that match the filter, so many
past years can have 0 but only one year can have 1. A plain UNIQUE (IsCurrent) would
wrongly allow only one past year.
MWhat happens if you delete a class that still has sections?concept
The delete fails with error 547: "The DELETE statement conflicted with the REFERENCE
constraint FK_Sections_Classes". By default a foreign key blocks deleting a referenced
row. You can choose ON DELETE CASCADE or SET NULL instead, but for school records
blocking is safer.
MShould a rule like 'an academic year ends after it starts' be a CHECK constraint or application code?concept
Both, for different reasons. The application checks it to give the user a friendly
message. The CHECK constraint guarantees it for every path into the database, including
imports and manual fixes. A simple, permanent rule about one row belongs in a CHECK.
SYou need to add a foreign key to a table that already contains invalid rows. What do you do?scenario
First find and fix or archive the invalid rows, then add the constraint normally so SQL
Server checks all existing data. WITH NOCHECK can add it without checking old rows, but
the constraint is then marked untrusted, the optimizer cannot rely on it, and the bad
data stays.
Show a safe process: measure the bad rows with a query, agree with the business how to
fix each one, clean in a transaction, then add the constraint. Explain why WITH NOCHECK
is only a temporary step, and that untrusted constraints lose query-plan benefits.
SA script that creates a filtered index works in SSMS but fails through sqlcmd. Why?scenario
Filtered indexes require QUOTED_IDENTIFIER ON. SSMS turns it on by default, but sqlcmd
does not, so the CREATE INDEX fails with error 1934. Run sqlcmd with -I, or put SET QUOTED_IDENTIFIER ON in the script. Later inserts and updates on that table need the
setting too.
Explain the session setting and why tools differ. Add the hidden danger: stored
procedures and triggers remember the setting they were created with, so creating them
through sqlcmd without -I breaks them later. Close with the fix: standardize the
setting in every deployment tool.
AWhich business rules belong in database constraints, and which belong in application code?design
Rules that must always be true, whatever the context, belong in constraints: uniqueness, required values, valid ranges, and relationships. Rules that depend on who is acting, when, or on several steps belong in code: permissions, workflows, and time windows. Many important rules live in both places.
Give the test: "must this be true for every row, forever, from every source?" Use SMS examples on both sides (a mark belongs to an enrollment vs a teacher may edit marks only before results are published). Mention the cost of each: constraints are cheap and reliable but rigid; code is flexible but can be bypassed.
People tables
FWhy does the SMS store guardians in their own table instead of parent columns on Students?concept
A guardian is a person who can have several children in the school. With their own
table, each guardian is stored once, with one phone number, and the
StudentGuardians junction table links them to each child. Parent columns would repeat
the same data for siblings.
See the example · Lesson 11: Teachers, students, guardians · Permalink
FWhat is a composite primary key?concept
A composite primary key is made of two or more columns whose combination identifies the
row. In StudentGuardians, (StudentId, GuardianId) is the key: one guardian can be
linked to many students, but the same pair cannot appear twice.
See the example · Lesson 11: Teachers, students, guardians · Permalink
FWhat is the difference between writing constraints inline and adding them with ALTER TABLE?concept
Inline constraints are written inside CREATE TABLE, either after a column or after all
columns, so the whole definition is in one place. ALTER TABLE ... ADD CONSTRAINT adds a
rule to a table that already exists. The result is the same; inline is simpler for new
tables, ALTER for changes.
See the example · Lesson 11: Teachers, students, guardians · Permalink
MWhy does Students.Status use a CHECK list instead of a separate lookup table?design
The list of statuses is short, fixed, and drives code behaviour, so a CHECK constraint
is the simplest way to keep values valid. A lookup table is better when users must add
or rename values without a code change, or when each value needs extra data such as a
display name.
See the example · Lesson 11: Teachers, students, guardians · Permalink
MHow would you guarantee that each student has at most one primary contact guardian?coding
Add a filtered unique index on StudentGuardians (StudentId) WHERE IsPrimaryContact = 1.
Each student can then have any number of guardians, but only one row per student can be
marked as the primary contact.
See the example · Lesson 11: Teachers, students, guardians · Permalink
MWhy must tables be created in a particular order?concept
A foreign key can only reference a table that already exists. So tables that others
point to, such as Students and Guardians, are created first, and tables that point to
them, such as StudentGuardians, afterwards. Data must be inserted in the same order.
See the example · Lesson 11: Teachers, students, guardians · Permalink
SA student leaves the school and rejoins two years later. How does this design handle it?scenario
The student keeps the same Students row and admission number; their status changes to
Withdrawn when they leave and back to Active when they return. The gap years simply have
no enrollment, and the return year gets a new Enrollments row. History before and after
stays connected to one person.
Separate the person (permanent) from the yearly placement (enrollments). Walk through the rows that change. Mention the alternative some schools want, a new admission number on return, and why it splits one student's history in two.
See the example · Lesson 11: Teachers, students, guardians · Permalink
SThe people tables hold personal data. What would you do to protect it?design
Collect only what the school needs, restrict who can read sensitive columns, and use made-up data in development and test. SQL Server features such as permissions on columns or Dynamic Data Masking can hide values from users who do not need them, and backups must be protected like the live database.
Cover the principles (minimize, restrict, protect copies) before the features. Use SMS examples: a teacher needs a guardian's phone, but not every guardian's occupation. Note the trade-off: tighter access adds work for support staff, so define roles carefully.
See the example · Lesson 11: Teachers, students, guardians · Permalink
AWould you store teachers, students, and guardians in one People table or in separate tables?design
Separate tables, as in the SMS, are simpler: each has its own columns and rules, and queries stay clear. A single People table (a supertype) avoids repeating name and contact columns and suits people with several roles, such as a teacher who is also a parent, but it adds joins and nullable role-specific columns.
Present both designs fairly, then decide from the requirements: how often one person has several roles, and how different the roles' data is. Mention the middle path: separate tables now, plus a shared Users table for login, which is what the SMS does.
See the example · Lesson 11: Teachers, students, guardians · Permalink
Enrollment
FWhat is a unique constraint on multiple columns?concept
It makes the combination of the columns unique, not each column on its own.
UNIQUE (StudentId, AcademicYearId) lets a student appear once per year, so the same
student can still have one row for every year.
See the example · Lesson 12: Enrollments and allocation · Permalink
FHow does the database stop two students getting the same roll number in a section?concept
With UNIQUE (SectionId, AcademicYearId, RollNumber) on Enrollments. Roll number 3
can exist in 10-A and 10-B, and in 10-A next year, but only once in 10-A in the same year.
See the example · Lesson 12: Enrollments and allocation · Permalink
FWhat does an enrollment represent in the SMS?concept
One student in one section for one academic year, with a roll number. Attendance, marks, and fee invoices all link to the enrollment, so every record knows which year and section it belongs to.
See the example · Lesson 12: Enrollments and allocation · Permalink
MWhy do the SMS unique rules on Enrollments include AcademicYearId?concept
Because the facts they protect are true per year. A student is in one section per year,
and roll numbers repeat each year. Without the year, UNIQUE (StudentId) would allow a
student to be enrolled only once in their life.
See the example · Lesson 12: Enrollments and allocation · Permalink
MWhy is the class teacher stored in a ClassTeachers table instead of a column on Sections?design
The class teacher changes every year, while a section such as 10-A exists every year.
A column on Sections would be overwritten each year and lose history. ClassTeachers
keeps one row per section per year, protected by UNIQUE (AcademicYearId, SectionId).
See the example · Lesson 12: Enrollments and allocation · Permalink
MWhat does SubjectAllocations store, and which rule protects it?concept
It stores which teacher teaches which subject to which section in each academic year,
with four foreign keys. UNIQUE (AcademicYearId, SectionId, SubjectId) ensures one
teacher per subject per section per year, while one teacher can still teach many sections.
See the example · Lesson 12: Enrollments and allocation · Permalink
SA student moves from 10-A to 10-B in the middle of the year. How do you handle it with this design?scenario
The simple design allows one enrollment per year, so you correct the section on the existing enrollment and record the change in the audit columns. If the school needs to know which section the student was in on each date, the design must change: add a "valid from" date and one row per move, and adjust the unique rules.
Show that you see the hidden requirement: does history per date matter? Compare the two designs and their costs (simple updates vs more complex queries). End with the process: agree the rule with the school before coding, because both are valid.
See the example · Lesson 12: Enrollments and allocation · Permalink
SHow would you stop a section from going over its capacity of 40 students?design
A constraint cannot count rows, so this needs logic: a stored procedure or the API checks the current count before enrolling, inside a transaction that locks the section, so two enrollments at the same moment cannot both pass. A trigger can act as a safety net.
Explain why CHECK and UNIQUE cannot do it, then show the race condition: two clerks enrolling the 40th student at once. Describe the fix with a transaction and lock, and mention the trade-off between enforcing in the database (safe) and in the API (easier to show friendly messages).
See the example · Lesson 12: Enrollments and allocation · Permalink
AHow would you design the year-end rollover that promotes 10,000 students to the next year?design
Create the new academic year, then insert next year's enrollments in one set-based statement from this year's enrollments, moving each student up a class. Run it in a transaction, make it safe to re-run, and produce a review report first so the school can correct exceptions such as students who repeat a year or leave.
Cover the steps (prepare, preview, execute, verify) and the guarantees (all or nothing, re-runnable, auditable). Discuss the exceptions that need human decisions, and the trade-off between a fully automatic job and a reviewed, two-step process.
See the example · Lesson 12: Enrollments and allocation · Permalink
Attendance and marks
FWhy is the key of AttendanceRecords a BIGINT?concept
Attendance grows by one row per student per school day. One school of 10,000 students
adds about 2 million rows a year, and many schools together pass the 2.1 billion limit
of INT. BIGINT costs four more bytes per row and removes that risk.
See the example · Lesson 13: Attendance, exams, marks · Permalink
FHow does the database prevent marking a student's attendance twice on the same day?concept
With UNIQUE (EnrollmentId, AttendanceDate) on AttendanceRecords. A second record for
the same enrollment and date is refused, so a double click or a network retry cannot
create a duplicate.
See the example · Lesson 13: Attendance, exams, marks · Permalink
FWhy store NULL marks for an absent student instead of 0?concept
Zero means "attended and scored nothing"; absent means "did not write the paper". Storing
0 would lower averages and show a misleading report card. MarksObtained is NULL and
IsAbsent is 1, so reports can treat absence correctly.
See the example · Lesson 13: Attendance, exams, marks · Permalink
MExplain the CHECK constraint that combines IsAbsent and MarksObtained.coding
CHECK ((IsAbsent = 1 AND MarksObtained IS NULL) OR (IsAbsent = 0 AND MarksObtained >= 0))
allows only two valid states: absent with no marks, or present with marks of zero or
more. It refuses "absent with 45 marks" and "present with no marks". A CHECK can compare
columns of the same row like this.
See the example · Lesson 13: Attendance, exams, marks · Permalink
MWhy is there an ExamSubjects table between Exams and Marks?design
An exam such as "Unit Test 1" has many papers: one per class and subject, each with its
own date, maximum marks, and pass marks. ExamSubjects stores those papers, and each mark
belongs to one paper. Without it, every mark would have to repeat the paper's details.
See the example · Lesson 13: Attendance, exams, marks · Permalink
MWhy can't a CHECK constraint enforce 'marks cannot exceed the paper's maximum'?concept
A CHECK constraint can only see the row being saved. The maximum lives in
ExamSubjects, another table. Cross-table rules need a trigger, a stored procedure, or
application code; the SMS adds a trigger in Lesson 23 and the same check in the API.
See the example · Lesson 13: Attendance, exams, marks · Permalink
SEvery class teacher saves attendance between 8:45 and 9:00 AM. What design concerns does that create?scenario
Thousands of inserts arrive in a few minutes, so saves must be fast and safe to retry. Save a whole section in one set-based statement inside a transaction, rely on the unique constraint to stop duplicates, use an "insert or update" approach for corrections, and keep the table's indexes minimal so inserts stay cheap.
Describe the load pattern first (short, intense peak). Then cover correctness (unique rule, idempotent saves), efficiency (one statement per section, not one per student), and the trade-off between extra indexes for reports and insert speed at peak time.
See the example · Lesson 13: Attendance, exams, marks · Permalink
SA mark must be corrected after report cards were printed. How should the system handle it?scenario
Allow the correction only for authorized roles, record who changed it and when in the audit columns, and keep the old value in an audit log or history table. Reprint the affected report card and keep the reason for the change. A silent overwrite would make the printed and stored marks disagree with no explanation.
Combine process and design: permissions, audit trail, and a reason field. Name the SQL Server options for keeping old values (an audit table written by a trigger, or a system-versioned temporal table) and the cost of each in storage and complexity.
See the example · Lesson 13: Attendance, exams, marks · Permalink
AHow would you store ten years of attendance for 1,000 schools?design
That is billions of rows, so plan for size from the start: BIGINT keys, partitioning by
date so old periods can be managed separately, archiving closed years to cheaper storage,
and summary tables for reports that need totals rather than single days. Daily screens
then only touch the current year's data.
Estimate the volume with numbers first. Then explain each technique and what it solves: partitioning (maintenance and fast removal of old data), archiving (cost), summaries (report speed). Close with the trade-off: more moving parts to operate, justified only at that scale.
See the example · Lesson 13: Attendance, exams, marks · Permalink
Fees and audit
FWhat are audit columns?concept
Columns that record who created a row and when (CreatedAt, CreatedBy), and who last
changed it and when (UpdatedAt, UpdatedBy). CreatedAt usually has a default of
SYSUTCDATETIME(). They answer "who and when" for the latest change, not the full history.
See the example · Lesson 14: Fees, users, audit, soft delete · Permalink
FWhat is soft delete?concept
Instead of removing a row with DELETE, you mark it as deleted with IsDeleted = 1 and
a DeletedAt time. Screens and reports leave those rows out, and the row can be restored
by setting the flag back.
See the example · Lesson 14: Fees, users, audit, soft delete · Permalink
MWhat are the downsides of soft delete?concept
Every query must filter IsDeleted = 0, and forgetting it shows deleted rows in
reports. Unique constraints still see deleted rows, so a deleted record's unique value
stays taken. Tables never shrink. Use it only where restore or history is required, and
hide the filter in views.
See the example · Lesson 14: Fees, users, audit, soft delete · Permalink
SWhen are audit columns enough, and when do you need a full audit history?design
Audit columns record only the latest change, which is enough for "who touched this last". When you must know every old value, such as for marks or payments, you need a history: an audit table filled by triggers, a system-versioned temporal table, or change data capture. Each keeps old versions at the cost of storage.
Start from the question the business needs answered, then match a tool: audit columns, temporal tables (automatic history per table), or an audit log (custom, can include the reason). Mention storage growth and how long history must be kept.
See the example · Lesson 14: Fees, users, audit, soft delete · Permalink
MHow do you add a NOT NULL column to a table that already has rows?coding
Add it with a default in the same statement, for example ALTER TABLE dbo.Students ADD IsDeleted BIT NOT NULL CONSTRAINT DF_Students_IsDeleted DEFAULT (0). Existing rows get the
default value. Without a default, the statement fails because existing rows would have
no value.
See the example · Lesson 14: Fees, users, audit, soft delete · Permalink
FWhy are invoices stored as a header table and a lines table?design
The header (FeeInvoices) holds facts about the whole bill: number, date, student,
total, status. Each line (FeeInvoiceItems) holds one fee type and amount. One fact per
row means any number of fee types per invoice, and reports can total tuition or exam
fees across all invoices.
See the example · Lesson 14: Fees, users, audit, soft delete · Permalink
MHow does the SMS support part payments, and how do you calculate an invoice's balance?concept
An invoice can have many rows in FeePayments. The balance is the invoice total, minus
its discount, minus the sum of its payments. The invoice status (Unpaid, PartiallyPaid,
Paid) is updated in the same transaction that records a payment.
See the example · Lesson 14: Fees, users, audit, soft delete · Permalink
SA parent double-clicks 'Pay' and two identical payments are recorded. How do you prevent that?scenario
Make the payment request idempotent: the client sends a unique payment reference, and a unique constraint on it refuses the second insert. Record the payment and update the invoice status in one transaction, and have the screen disable the button after the first click. The database rule is what makes it reliable.
Explain the cause (retries and double clicks happen), then layer the defences: UI, API idempotency key, unique constraint, and a transaction. Name the trade-off: you need a reference that the client generates once per payment attempt.
See the example · Lesson 14: Fees, users, audit, soft delete · Permalink
Inserting data
FWhat is the syntax of INSERT, and why should you always list the columns?coding
INSERT INTO dbo.Table (Col1, Col2) VALUES (v1, v2), (v3, v4); adds one or more rows.
Listing the columns keeps the insert working when columns are added later, and stops
values going into the wrong columns if the column order changes.
FWhat happens to a column you leave out of an INSERT?concept
SQL Server uses the column's DEFAULT if it has one, the next value if it is an IDENTITY column, or NULL if the column allows it. If none of these apply, the insert fails with "Cannot insert the value NULL".
FWhat does SET IDENTITY_INSERT do?concept
It lets you insert your own values into an IDENTITY column for one table at a time. It is used for seed data that needs fixed Ids, or when copying data between databases. You must list the Id column, and you should turn it off right after the insert.
MWhy is a set-based insert faster than inserting rows one at a time in a loop?concept
A set-based statement inserts all rows in one operation, so SQL Server plans it once, writes the log efficiently, and avoids a round trip per row. A loop runs a separate statement, and often a separate commit, for every row. The SMS generator inserts 600,000 attendance rows in seconds this way.
MWhat is GENERATE_SERIES, and how did people generate numbers before SQL Server 2022?concept
GENERATE_SERIES(start, stop) returns one row per number in a range, which you can turn
into generated rows. Before SQL Server 2022, developers used a permanent numbers table,
a recursive CTE, or cross joins of system views to produce the same list of numbers.
MWhy does the bulk data script use one transaction with SET XACT_ABORT ON?concept
It makes the load all or nothing. With XACT_ABORT ON, any error rolls back the whole
transaction, so you never end up with students but no enrollments. Combined with the
"already exists" check at the top, the script can simply be run again after a failure.
SWhy test with 10,000 students when the sample school has only 12?scenario
With 12 rows every query is instant, so slow designs stay hidden until production. A realistic volume shows missing indexes, queries that return too many rows, and slow reports during development, when they are cheap to fix.
Give a concrete SMS example: a class report that scans every attendance row is invisible at 36 rows and obvious at 600,000. Describe how you size test data from the real school's numbers, and note that volume tests run on dedicated test databases, not on developers' shared servers.
SWhat is the difference between seed data and test data, and where does each run?design
Seed data is part of the product, such as roles, classes, and subjects, and runs in every environment, including production. Test data, such as generated students, runs only in development and test. Mixing them puts fake students into a real school or leaves production without required lookup rows.
Define both, give SMS examples of each, and describe how deployment keeps them apart: separate scripts or folders, and pipeline steps that run test data only in non-production environments. Mention keeping seed data re-runnable so redeploys are safe.
AHow do you create realistic test data at scale without using production data?design
Generate it from rules: numbers become names, dates, and statuses through repeatable formulas, with realistic distributions such as about 88 percent attendance. It is safe, repeatable, and sized to match real volumes. If production data must be used, it is masked or anonymized first, never copied as is.
Explain why production copies are risky (personal data, access control, compliance). Describe a generator's qualities: deterministic, realistic distributions, correct relationships, and fast set-based loading. Discuss the trade-off: generated data can miss rare real-world cases, so add known edge cases deliberately.
Querying basics
FIn what order does SQL Server process the clauses of a SELECT query?concept
Logically: FROM (and joins), WHERE, GROUP BY, HAVING, SELECT, ORDER BY,
then TOP. That is why a column alias defined in SELECT cannot be used in WHERE, but
can be used in ORDER BY.
FWhy does WHERE Occupation = NULL return no rows?concept
NULL means unknown, and comparing anything with unknown gives unknown, which is never
true. So = NULL matches nothing, even rows that are NULL. Use IS NULL or IS NOT NULL instead.
FWhat do the wildcards % and _ mean in LIKE?coding
% matches any text of any length, including none, and _ matches exactly one
character. LastName LIKE N'Sh%' matches Sharma and Sheikh; LIKE '_A' matches any
two-character value ending in A.
MWhy should TOP always be used with ORDER BY?concept
Without ORDER BY, SQL Server returns whichever rows it reaches first, and that can
change between runs, after an index change, or under load. TOP (3) ... ORDER BY DateOfBirth DESC always returns the three youngest students.
MWhy is SELECT * discouraged in application code?concept
It returns columns the code does not need, which wastes network and memory and can stop SQL Server from using a narrower index. It also changes silently when someone adds a column, which can break code that reads columns by position. List the columns you need.
MHow do you safely run an UPDATE or DELETE on important data?coding
Run a SELECT with the exact WHERE first and check the rows. Then start a
transaction, run the change with the same WHERE, check @@ROWCOUNT and the data,
and only then COMMIT; otherwise ROLLBACK.
SSomeone ran an UPDATE without a WHERE clause on production. What do you do?scenario
Stop further changes to that table, find out exactly what changed and when, and restore the correct values: from a backup restored to a separate database, from audit data, or from a point-in-time restore. Communicate early with the business. Afterwards, change the process so it cannot happen again.
Give a calm sequence: contain, assess, recover, communicate, prevent. Explain the recovery options and their trade-offs (restoring the whole database loses later work, so restore a copy and repair only the affected rows). Close with prevention: reviewed scripts, transactions, and limited write access to production.
SWhy can a search with LIKE N'%sharma%' be slow on a large table?concept
A pattern that starts with % cannot use an index seek, because the matching text
could be anywhere in the value; SQL Server has to scan every row. A pattern like
N'Sharma%' can seek. For true "contains" search on large tables, use full-text search
or a search index.
Explain index seeks vs scans in one sentence, then show the two patterns. Discuss the options for a student search screen: starts-with search, full-text search, or a separate search service, and the cost of each.
AHow would you design the process for data fixes on a production database?design
Data fixes are written as scripts, reviewed by a second person, tested on a copy of production, and run by the release process with a transaction and row-count checks. The script and its result are stored, so every change is traceable. Nobody types changes directly into production.
Describe the workflow (request, script, review, test, approved run, record) and the controls (permissions, transactions, expected row counts, backups). Mention the trade-off: urgent fixes take minutes longer, which is cheap compared with one bad manual update.
Joins
FWhat is the difference between INNER JOIN and LEFT JOIN?concept
INNER JOIN returns only rows that match on both sides. LEFT JOIN returns every row
from the left table, and fills the right table's columns with NULL when there is no
match. Every teacher with their class (LEFT) shows teachers who are not class teachers;
INNER would hide them.
FWhat is a join condition, and what usually goes in it?concept
The join condition, written after ON, says which rows of the two tables belong
together. It is usually a foreign key equal to the primary key it references, such as
ON e.StudentId = s.Id.
FWrite a query that lists the students of Class 10-A for the current year.coding
Join Students to Enrollments, then to Sections and AcademicYears, and filter on
the class, the section name, and IsCurrent = 1, ordered by roll number. Four tables,
each joined on its foreign key.
MWhy does adding a join sometimes multiply the number of rows?concept
If each row on the left matches several rows on the right, the left row appears once for each match. Joining 12 students to their guardians gives 24 rows, because each has two. Totals calculated after such a join are multiplied too.
MWhat is a self join? Give an example.coding
A self join joins a table to itself using two aliases. To find students who share a
guardian, join StudentGuardians to itself on the same GuardianId, with a condition
like sg2.StudentId > sg1.StudentId so a student does not match themselves.
MWhy does a condition on the right table belong in the ON clause of a LEFT JOIN, not in WHERE?concept
WHERE runs after the join. A condition there on the right table removes the rows
where that table is NULL, which turns the LEFT JOIN into an INNER JOIN. In the ON
clause, it only limits which right rows can match, and unmatched left rows are kept.
SA fee report total is exactly double what it should be. How do you find the cause?scenario
Doubled totals almost always come from a one-to-many join added after the data was already at the right level. Remove joins one at a time and watch the row count, or count rows per key to find the join that matches twice. Then aggregate before joining, or join only what the report needs.
Show a systematic method (compare row counts with a known number, isolate the join), then the fix (pre-aggregate in a CTE). Use the SMS example of joining payments to guardians. Mention adding a test that checks the total against a simpler query.
SWhen would you use FULL JOIN or CROSS JOIN in a real system?concept
FULL JOIN compares two lists where either side can have rows the other lacks, such as
reconciling an imported fee file with the payments table. CROSS JOIN builds every
combination, such as creating exam papers for every class and subject. Both are rare in
daily screens.
Give one concrete SMS use for each, and say how you keep CROSS JOIN results under control (filter both inputs first, check the expected row count). Mention that the bulk generator in Lesson 15 uses CROSS JOIN deliberately.
AA report needs eight joins and runs slowly every morning. Would you keep the joins or build a reporting table?design
First check whether the joins are the real problem: missing indexes on the join columns are the usual cause. If the report is still too slow, or many reports repeat the same joins, a reporting table or indexed view refreshed on a schedule can serve them, accepting slightly delayed data.
Compare the options on freshness, cost, and complexity: tuned joins (always fresh), indexed views (fresh, slower writes), and scheduled reporting tables (fast reads, delayed data). Make the decision from measured timings and how fresh the report must be.
Aggregation
FWhat is the difference between WHERE and HAVING?concept
WHERE filters rows before they are grouped, and cannot use aggregates. HAVING
filters groups after GROUP BY, so it can use aggregates, such as HAVING COUNT(*) < 5
to find small sections.
See the example · Lesson 18: GROUP BY and aggregates · Permalink
FWhat are the main aggregate functions?concept
COUNT counts rows or values, SUM adds them, AVG averages them, and MIN and MAX
return the smallest and largest. With GROUP BY, each returns one value per group, such
as students per section or fees per class.
See the example · Lesson 18: GROUP BY and aggregates · Permalink
FWhy does a query fail with 'column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause'?concept
Every column in SELECT must either be in GROUP BY or be inside an aggregate.
Otherwise one group could have several values for that column, and SQL Server cannot
choose one. Add the column to GROUP BY or aggregate it.
See the example · Lesson 18: GROUP BY and aggregates · Permalink
MWhat is the difference between COUNT(*), COUNT(column), and COUNT(DISTINCT column)?concept
COUNT(*) counts every row. COUNT(column) counts rows where that column is not NULL,
so absent students without marks are skipped. COUNT(DISTINCT column) counts different
values. Four 10-A Science papers give 4, 3, and 1 respectively.
See the example · Lesson 18: GROUP BY and aggregates · Permalink
MHow do you count only some rows inside a GROUP BY query?coding
Put a CASE inside SUM: SUM(CASE WHEN Status IN ('Present', 'Late') THEN 1 ELSE 0 END) adds 1 for each matching row and 0 for others. Divided by COUNT(*) and multiplied
by 100.0, it gives an attendance percentage per student.
See the example · Lesson 18: GROUP BY and aggregates · Permalink
MWhy does 100 * 2 / 3 give 66 instead of 66.67 in SQL Server?concept
When both sides of a division are whole numbers, SQL Server returns a whole number and
drops the fraction. Make one side decimal, for example 100.0 * 2 / 3, or CAST a value
to DECIMAL, to keep the fraction.
See the example · Lesson 18: GROUP BY and aggregates · Permalink
SThe class average looks too high because absent students are left out. How do you handle it?scenario
AVG skips NULLs, so absent students do not count. That is usually right, but the
school must decide the rule. If absent should count as zero, use AVG(ISNULL(MarksObtained, 0)); if not, show the number of papers averaged next to the average so readers know.
Explain how aggregates treat NULL, then turn it into a business decision with the school. Show both SQL forms. Recommend documenting the definition with the report so every report uses the same rule.
See the example · Lesson 18: GROUP BY and aggregates · Permalink
STwo reports show different attendance percentages for the same class. How do you resolve it?scenario
Compare the definitions first: which statuses count as present, which dates are included, and whether deleted or transferred students are counted. Then recompute the number by hand from raw rows. Agree one definition with the school and make both reports use it.
Show that you treat it as a definitions problem before a SQL problem. Walk through the checks, reconcile with raw counts, and close with prevention: one shared, documented definition, ideally in one view used by every report.
See the example · Lesson 18: GROUP BY and aggregates · Permalink
AThe principal's dashboard aggregates millions of rows on every page load. How would you redesign it?design
Pre-compute the numbers: a summary table updated as data changes or on a short schedule, or an indexed view that SQL Server keeps up to date. The dashboard then reads a few rows instead of millions, at the cost of extra work on writes or slightly delayed numbers.
Quantify the load first (rows scanned, page loads per minute). Compare indexed views, summary tables, and caching on freshness, write cost, and complexity. Choose based on how fresh the principal really needs the numbers to be.
See the example · Lesson 18: GROUP BY and aggregates · Permalink
Subqueries and CTEs
FWhat is a subquery?concept
A subquery is a query inside another query, in brackets. It can return one value (used
with = or >), a list (used with IN), or be used with EXISTS to test whether any
row matches.
See the example · Lesson 19: Subqueries and CTEs · Permalink
FWhat is a CTE?concept
A CTE (Common Table Expression) is a named query written with WITH name AS ( ... )
before a statement. That statement can use the name like a table. It makes long queries
readable as steps, and it exists only for that one statement.
See the example · Lesson 19: Subqueries and CTEs · Permalink
FFind the students who scored above their section's average.coding
Compare each mark with a subquery that returns the average for the same paper and
section: WHERE m.MarksObtained > (SELECT AVG(MarksObtained) FROM dbo.Marks WHERE ...).
In 10-A Unit Test 1 Mathematics, the average is 84.75, so Meera, Aarav, and Diya qualify.
See the example · Lesson 19: Subqueries and CTEs · Permalink
MWhy can NOT IN return no rows at all, and what should you use instead?concept
If the subquery's list contains a NULL, x NOT IN (...) is unknown for every row,
because "not equal to NULL" is never true. So the query returns nothing. Use NOT EXISTS, which is not affected by NULLs, or filter NULLs out inside the subquery.
See the example · Lesson 19: Subqueries and CTEs · Permalink
MWhat is the difference between IN and EXISTS?concept
IN checks whether a value is in a list returned by the subquery. EXISTS checks
whether the subquery returns any row at all, and can stop at the first match. For
positive checks they often perform the same; for negative checks, NOT EXISTS is safer
than NOT IN because of NULLs.
See the example · Lesson 19: Subqueries and CTEs · Permalink
MWhat is a correlated subquery?concept
A correlated subquery uses a column from the outer query, so it is evaluated for each
outer row. Example: each student's last attendance date with (SELECT MAX(a.AttendanceDate) FROM dbo.AttendanceRecords AS a WHERE a.EnrollmentId = e.Id).
See the example · Lesson 19: Subqueries and CTEs · Permalink
SWrite a query for invoices that still have money due, with the balance.coding
In a CTE, sum the payments per invoice. Then left join the invoices to it, compute
total minus discount minus ISNULL(paid, 0), and keep rows where that is above zero.
Summing first avoids multiplied totals, and the left join keeps invoices with no payments.
Write it in two steps aloud, explaining why each part exists (CTE to aggregate, LEFT JOIN for unpaid invoices, ISNULL for NULL sums). Mention edge cases: deleted payments, discounts, and cancelled invoices, and how you would test the result against one known invoice.
See the example · Lesson 19: Subqueries and CTEs · Permalink
SA nightly job using NOT IN suddenly sends no reminders at all. What happened?scenario
Most likely a NULL appeared in the column the NOT IN subquery returns, so the
condition became unknown for every row and nothing qualified. Rewrite it with NOT EXISTS, fix or explain the NULL data, and add an alert when a reminder job processes zero rows.
Diagnose from the symptom (zero rows, not an error), explain the three-valued logic briefly, then fix and prevent: NOT EXISTS, a data check, and monitoring that treats "zero processed" as suspicious.
See the example · Lesson 19: Subqueries and CTEs · Permalink
AWhen would you choose a CTE, a temporary table, or a view for a multi-step report?design
A CTE for readable steps inside one statement. A temporary table when an intermediate result is reused several times, is large, or benefits from its own index. A view when many queries and reports need the same logic. Each adds reuse or speed at the cost of more objects to manage.
Compare scope (one statement, one session, permanent), reuse, and performance. Use SMS examples for each: a report card CTE, a temp table for a year-end calculation, a view for "current enrollments". Mention measuring before switching for speed.
See the example · Lesson 19: Subqueries and CTEs · Permalink
Window functions
FWhat is the difference between ROW_NUMBER, RANK, and DENSE_RANK?concept
All three number rows in order. ROW_NUMBER always gives different numbers, even for
ties. RANK gives ties the same number and then skips (1, 2, 3, 3, 5). DENSE_RANK
gives ties the same number without gaps (1, 2, 3, 3, 4).
FHow is a window function different from GROUP BY?concept
GROUP BY collapses each group into one row. A window function calculates across a
group of rows but keeps every row, so you can show each student with their rank or the
section average next to their mark.
FWhat does PARTITION BY do in an OVER clause?concept
It splits the rows into groups, and the window function restarts for each group.
RANK() OVER (PARTITION BY SectionId ORDER BY Total DESC) ranks students within their
own section, starting at 1 in every section.
MHow do you remove duplicate rows from a table using ROW_NUMBER?coding
Number the rows within each group of duplicates with ROW_NUMBER() OVER (PARTITION BY the duplicate columns ORDER BY a tie-breaker) in a CTE. Rows numbered 1 are kept; delete
or ignore the rest.
MHow do you calculate a running total in SQL Server?coding
Use SUM(Amount) OVER (ORDER BY PaidOn, ReceiptNumber ROWS UNBOUNDED PRECEDING). Each
row gets the total of all rows up to it. A unique ORDER BY and the explicit ROWS frame
make the result predictable and fast.
MWhat do LAG and LEAD do?concept
LAG(column) returns the value from the previous row in the window, and LEAD(column)
from the next row. They compare a row with its neighbours, such as this exam's marks
against the previous exam's. The first row's LAG is NULL.
SWrite a query for the top 3 students in every section.coding
In a CTE, compute each student's total, then add ROW_NUMBER() OVER (PARTITION BY Section ORDER BY Total DESC, AdmissionNumber). In the outer query, keep rows numbered 1
to 3. Use RANK instead if tied students must both appear.
Write the two-step pattern and explain why the filter must be outside (window functions are not allowed in WHERE). Ask about the tie rule before choosing the function, and mention the tie-breaker that keeps results stable.
SA page loads 50,000 rows into C# only to rank them and show the top 3 per group. How would you improve it?scenario
Move the ranking into SQL with a window function and return only the rows the page shows. The database does it in one pass with the data already there, and the network, memory, and page time drop sharply.
Measure first (rows transferred, page time), then show the window-function rewrite and the expected gain. Discuss the trade-off: logic in SQL is less visible to C# developers, so keep it in a named view or procedure with tests.
AShould rankings and running totals be calculated in the database or in application code?design
For large data sets, in the database: it avoids moving rows and uses set-based processing. For small results that the screen already holds, or rules that change often and need unit tests in C#, application code can be simpler. Keep one place responsible for each calculation.
Weigh data volume, network cost, testability, and who maintains the logic. Give SMS examples on both sides (prize lists over 10,000 students in SQL; re-sorting 40 rows on a screen in code). Stress consistency: one calculation, one place.
Views and functions
FWhat is a view?concept
A view is a saved SELECT with a name. It stores no data; every time you query it, SQL
Server runs its query against the real tables. Views hide joins and filters, such as
vw_CurrentEnrollments hiding four joins and the soft-delete filter.
See the example · Lesson 21: Views and functions · Permalink
FWhat is the difference between a scalar function and a table-valued function?concept
A scalar function returns one value and is used in SELECT or WHERE, such as
dbo.fn_GetGrade(92). A table-valued function returns a table and is used in FROM, such
as dbo.fn_GetSectionResults(1, 19). An inline table-valued function is like a view that
takes parameters.
See the example · Lesson 21: Views and functions · Permalink
FWhy use a view to hide soft-deleted rows?concept
So the IsDeleted = 0 filter is written once. Every report and API that reads the view
automatically leaves deleted students out, and nobody can forget the filter. In the SMS,
soft-deleting Meera drops the 10-A count from 4 to 3 through the view.
See the example · Lesson 21: Views and functions · Permalink
MWhat is the difference between a view and an inline table-valued function?concept
Both are saved queries used in FROM. A view takes no parameters, while an inline
table-valued function does, such as an exam and section. SQL Server expands both into the
calling query, so both perform like the query written out in full.
See the example · Lesson 21: Views and functions · Permalink
MWhy can a scalar function make a query slow?concept
It runs once for every row it is applied to, and when used on a column in WHERE, it
hides that column from indexes. SQL Server 2019 and later can inline simple scalar
functions, but functions that read tables or use loops often cannot be inlined.
See the example · Lesson 21: Views and functions · Permalink
MCan you insert or update through a view?concept
Yes, if the change affects one base table and the view has no DISTINCT, GROUP BY, or
aggregates. Views with joins usually cannot accept changes to more than one table. In the
SMS, views are for reading; changes go through tables or procedures.
See the example · Lesson 21: Views and functions · Permalink
SA report using a scalar function over 2 million rows takes 40 minutes. How do you fix it?scenario
Look at what the function does. If it reads tables, rewrite that logic as a join or an inline table-valued function so SQL Server processes all rows as one set. Compare the plans and timings before and after; this kind of rewrite often turns minutes into seconds.
Explain the row-by-row cost and why the optimizer cannot see inside a non-inlined function. Describe the rewrite and how you would prove it with logical reads and duration. Mention checking whether SQL Server's scalar UDF inlining applies first.
See the example · Lesson 21: Views and functions · Permalink
SHow can views be used as a security boundary?design
Grant reporting users SELECT on views only, never on the tables. The view exposes only
the columns and rows they may see, and because the view and tables have the same owner,
SQL Server does not check table permissions (ownership chaining). The tables stay private.
Combine the design (views as the public shape of the data) with the permission model (GRANT on views, nothing on tables). Use the SMS reports role. Note the limit: a view is not row-level security per user; for that, use filters or Row-Level Security policies.
See the example · Lesson 21: Views and functions · Permalink
AWhen would you use an indexed view, and what does it cost?design
When a frequently run aggregate over large tables is too slow and must be up to date, such as daily attendance totals. The indexed view stores its result and SQL Server maintains it on every change to the base tables, so reads are fast but writes become slower, and strict rules apply.
Present it as one option among summary tables, caching, and better indexes. Weigh freshness against write cost, and mention the restrictions (schema binding, no outer joins). Decide from measured numbers on the real workload.
See the example · Lesson 21: Views and functions · Permalink
Stored procedures
FWhat is a stored procedure?concept
A stored procedure is a named block of T-SQL saved in the database, with parameters. It
can read and change data and use transactions. The SMS uses usp_RecordFeePayment to
check, save, number, and update a payment in one call.
FWhat is the difference between a stored procedure and a function?concept
A procedure can change data and use transactions, and is called with EXEC. A function
cannot change data, and is used inside a query, returning one value or a table. Use
procedures for business actions and functions for calculations and reusable reads.
FWhat is an OUTPUT parameter?concept
A parameter that sends a value back to the caller. usp_RecordFeePayment returns the new
receipt number through @ReceiptNumber OUTPUT, and the caller passes a variable with the
OUTPUT keyword to receive it.
MWhy use SET NOCOUNT ON in a stored procedure?concept
It stops SQL Server from sending a "rows affected" message after every statement. The application does not need them, they add network traffic, and some client libraries can confuse them with result sets.
MHow do stored procedures protect against SQL injection, and when can they still be vulnerable?concept
Parameters are treated as values, never as code, so input like '; DELETE ... is just
text. A procedure is still vulnerable if it builds dynamic SQL by joining strings with
input. Dynamic SQL must pass values through sp_executesql parameters.
MWhy use a SEQUENCE instead of MAX(...) + 1 to number receipts?concept
Two sessions running MAX + 1 at the same time can read the same maximum and produce the
same number. A sequence hands out each number exactly once, without locking the table.
Numbers used in rolled-back transactions are not returned, so gaps can appear.
STwo payments for the same invoice arrive at the same moment. How does usp_RecordFeePayment stay correct?scenario
It reads the invoice WITH (UPDLOCK) inside its transaction. The second payment waits
until the first commits, then sees the updated balance, so both cannot pass the balance
check. The receipt numbers come from a sequence, so they never collide.
Describe the race condition first (both read the same balance), then the fix (lock the row you will change, early), and the guarantee it gives. Mention the cost: payments for the same invoice are serialized, which is acceptable because they are rare.
SHow do you change a stored procedure that the API depends on without breaking it?design
Treat parameters and result columns as a contract. Use CREATE OR ALTER so permissions
are kept, add new parameters at the end with defaults, and do not rename or remove result
columns that callers read. Deploy the database change before the application change that uses it.
Explain backward compatibility, the deployment order, and how you test both old and new callers. Mention versioned procedure names as a last resort for breaking changes.
AShould business logic live in stored procedures or in application code?design
Data-heavy, transactional steps that must be atomic, such as recording a payment, fit procedures well. Workflows, validation messages, integrations, and logic that needs unit tests fit application code better. Most systems use both, with a clear rule for which goes where.
Avoid absolutes. Weigh testability, deployment, team skills, performance, and reuse across channels. Give SMS examples on both sides and describe the agreed boundary your team would write down.
Triggers
FWhat is a trigger?concept
A trigger is code attached to a table that SQL Server runs automatically when rows are inserted, updated, or deleted. It runs inside the same transaction, so it can refuse a change with an error or record it in another table.
FWhat are the inserted and deleted tables in a trigger?concept
They are temporary tables with the same columns as the real table. inserted holds new
rows (after an insert or update), and deleted holds old rows (after an update or delete).
For an update, joining them shows each row before and after.
FWhy does the SMS use a trigger for 'marks not above the maximum' instead of a CHECK constraint?concept
The maximum is in ExamSubjects, another table, and a CHECK constraint can only see the
row being saved. The trigger joins inserted to ExamSubjects and throws error 50001 if
any mark is too high.
MHow many times does a trigger fire for an UPDATE that changes 500 rows?concept
Once. Triggers fire per statement, with all 500 rows in inserted and deleted. Code
that assumes a single row, such as assigning a column to a variable, silently handles
only one of them; trigger code must be set-based.
MWhat happens when a trigger throws an error?concept
The statement that fired it fails, and its transaction is rolled back, including any rows the trigger itself wrote. In the SMS, updating two marks where one is 105 fails as a whole: both marks keep their old values.
MHow does the audit trigger know which user made a change?concept
The API stores the logged-in user's Id in SESSION_CONTEXT for the connection, with
sp_set_session_context. The trigger reads it with SESSION_CONTEXT(N'UserId'), so it
knows the user without the API passing it to every statement.
SSaving marks became slow after someone added a trigger. What do you check?scenario
Read the trigger: loops, cursors, queries without indexes, or calls to other databases or services run inside every save. Check the plan of the trigger's statements and the wait types during saves. Rewrite it set-based and move any external work out of the transaction.
Show that triggers are part of the write path. Describe how you would measure (plans, duration with and without the trigger) and the usual culprits. End with a rule: triggers only check and record.
SHow would you choose between a constraint, a trigger, and a stored procedure for a business rule?design
Use a constraint when the rule concerns one row and is always true. Use a procedure for a multi-step business action. Use a trigger only when the rule needs other tables and must hold for every way data is written, such as imports and manual fixes.
Give the decision order (constraint, then procedure, then trigger) and an SMS example for each. Mention the costs of triggers: hidden behaviour, write overhead, and testing effort.
AHow would you design auditing for a system that must keep every change for 7 years?design
Record old and new values with who and when for the tables that matter, using triggers, temporal tables, or change data capture. Store audit data so it cannot be edited by the application, partition or archive it by date, and plan its size, retention, and access.
Compare the options: triggers (flexible, custom), system-versioned temporal tables (automatic history per table), and change data capture (log-based, for downstream use). Cover storage growth, query needs, security, and how long data must be kept.
Transactions
FWhat is a transaction?concept
A group of changes that succeed together or fail together. BEGIN TRANSACTION starts it,
COMMIT makes every change permanent, and ROLLBACK undoes them all. Recording a payment
and updating the invoice status is one transaction.
FWhat does ACID stand for?concept
Atomicity (all or nothing), Consistency (rules hold before and after), Isolation (others do not see half-done work), and Durability (committed changes survive a crash). Together they describe what a database transaction guarantees.
FWhat is autocommit mode?concept
SQL Server's default: without BEGIN TRANSACTION, each statement is its own transaction
and is committed as soon as it succeeds. Two related statements run this way are not
protected; if the second fails, the first stays saved.
MInside a transaction, the second of two statements fails. Is the first one undone?concept
Not by default. SQL Server undoes only the failed statement; the transaction stays open with
the first change in it, and a later COMMIT saves it. Use TRY...CATCH with ROLLBACK, or
SET XACT_ABORT ON, so any error undoes everything.
MWhat does SET XACT_ABORT ON do, and why use it?concept
With it on, any run-time error rolls back the whole transaction and stops the batch.
It guarantees that a failure never leaves a half-done transaction, even when a CATCH
block is missing or a client times out. Procedures that change data should use it.
MWrite the standard TRY...CATCH pattern for a transaction.coding
BEGIN TRY BEGIN TRANSACTION; ...changes...; COMMIT TRANSACTION; END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; THROW; END CATCH;. The check avoids an error when no
transaction is open, and THROW; passes the original error to the caller.
SDoes SQL Server support nested transactions?concept
Not really. Each BEGIN TRANSACTION increases @@TRANCOUNT, and an inner COMMIT only
decreases it; nothing is saved until the outermost COMMIT. A ROLLBACK anywhere undoes
everything and sets the count to 0. Savepoints are the only way to undo part of a transaction.
Explain @@TRANCOUNT with the 2, 1, 0 example, then the consequence: a procedure that rolls
back destroys its caller's transaction. Show how callers should check @@TRANCOUNT after an
error, and mention savepoints for partial undo.
SUsers say saving 'hangs' every afternoon. You suspect an open transaction. How do you find it?scenario
Run DBCC OPENTRAN in the database to see the oldest open transaction and its session,
and query sys.dm_exec_requests for blocked sessions and who blocks them. Often it is an
SSMS window with an uncommitted change, or an application path that never commits. Close or
kill it, then fix the cause.
Give the diagnosis steps with the exact tools, then the decision (contact the owner before
killing a session), and the prevention: short transactions, XACT_ABORT, and alerts on
long-running transactions.
AA business action spans the database and an external payment service. How do you keep them consistent?design
A database transaction cannot include an external service. Record the intent first in the database, call the service with an idempotency key, then record the result; a background job retries or reconciles anything left pending. The design accepts brief inconsistency and always ends in a known state.
Explain why distributed transactions are avoided, then describe the pattern (outbox or pending-state plus reconciliation). Use the SMS online payment as the example, and cover retries, idempotency, and monitoring.
Locking and isolation
FWhat is the difference between a shared lock and an exclusive lock?concept
A shared lock is taken for reading; many sessions can hold it at once. An exclusive lock is taken for changing data; only one session can hold it, and it blocks other writers and, by default, readers. Exclusive locks last until the transaction ends.
See the example · Lesson 25: Isolation and deadlocks · Permalink
FWhat is SQL Server's default isolation level?concept
READ COMMITTED. Readers see only committed data, and wait for writers that hold locks on
the rows they need. It prevents dirty reads but still allows non-repeatable reads and phantoms.
See the example · Lesson 25: Isolation and deadlocks · Permalink
FWhat is a deadlock?concept
Two sessions each hold a lock the other needs, so neither can continue. SQL Server detects it within seconds, chooses one session as the victim, rolls it back, and returns error 1205, letting the other finish.
See the example · Lesson 25: Isolation and deadlocks · Permalink
MWhat is a dirty read, and which setting allows it?concept
A dirty read sees a change that another transaction has not committed and may roll back.
READ UNCOMMITTED and the NOLOCK hint allow it. In the SMS demo, a NOLOCK read returned a
phone number that was rolled back a moment later.
See the example · Lesson 25: Isolation and deadlocks · Permalink
MHow do you find which session is blocking another?coding
Query sys.dm_exec_requests for rows where blocking_session_id <> 0, joined to
sys.dm_exec_sql_text for the waiting query. It shows who waits, who blocks, the wait type
such as LCK_M_S, and for how long.
See the example · Lesson 25: Isolation and deadlocks · Permalink
MWhat is Read Committed Snapshot Isolation (RCSI)?concept
A database setting that makes normal READ COMMITTED reads use the last committed version
of each row instead of waiting for writers' locks. Readers stop blocking on writers, and
still never see uncommitted data. The cost is row versions kept in tempdb.
See the example · Lesson 25: Isolation and deadlocks · Permalink
SUsers get deadlock errors when saving attendance and fees. How do you reduce them?scenario
Capture the deadlock details (from the system_health session) to see which statements and resources are involved. Then make every code path touch tables and rows in the same order, keep transactions short, add indexes so fewer rows are locked, and retry error 1205 a few times.
Start from evidence (the deadlock graph), then apply the prevention techniques in order of
impact. Explain why retrying is still needed, and why NOLOCK is not a fix.
See the example · Lesson 25: Isolation and deadlocks · Permalink
SA developer added WITH (NOLOCK) to every report query to stop blocking. What is your response?scenario
NOLOCK can return uncommitted rows, skip rows, or read rows twice when pages move, so reports can show wrong numbers. Find the real cause of the blocking, usually long transactions or missing indexes, and consider RCSI so reports read committed versions without waiting.
Acknowledge the pain, explain the correctness risks with a concrete example, and offer better options. Mention where NOLOCK is acceptable: rough monitoring queries where accuracy does not matter.
See the example · Lesson 25: Isolation and deadlocks · Permalink
AHow would you choose isolation settings for an SMS serving 1,000 schools?design
Turn on RCSI so reads do not block on writes, keep READ COMMITTED as the default, and use
stronger locking only where needed, such as UPDLOCK on an invoice during payment. Test
under realistic concurrency, and monitor blocking and deadlocks in production.
Weigh correctness, concurrency, and tempdb cost. Explain why SERIALIZABLE everywhere does not scale, and why targeted locking for money operations does. Mention load testing at the 8:45 AM peak and monitoring.
See the example · Lesson 25: Isolation and deadlocks · Permalink
Indexes
FWhat is an index?concept
A sorted structure that lets SQL Server find rows without reading the whole table, like
the index at the back of a book. In the SMS, an index on AttendanceDate cut one day's
attendance query from 3,351 page reads to 38.
FWhat is the difference between a clustered and a nonclustered index?concept
The clustered index is the table itself, stored in key order; there is only one. A nonclustered index is a separate sorted copy of some columns with a pointer to the row; a table can have many. Primary keys are clustered by default.
FWhat is the difference between an index seek and an index scan?concept
A seek jumps straight to the matching values in a sorted index and reads few pages. A scan reads the whole index or table. Scans are fine for small tables or when most rows are needed, but a scan on a big table for a few rows usually means a missing or unusable index.
MWhat is a covering index, and what does INCLUDE do?concept
A covering index contains every column a query needs, so no key lookups to the table are
required. INCLUDE adds columns to the index's leaf level without sorting by them, such as
INCLUDE (Status) on the attendance date index.
MWhy does the order of columns in a multi-column index matter?concept
SQL Server can seek only on the leading column or columns. An index on (ExamSubjectId, EnrollmentId) helps a search by paper, but not a search by enrollment alone, which is why the
SMS needed a separate index starting with EnrollmentId.
MWhat is the cost of adding indexes?concept
Every insert, update, and delete on the indexed columns must also update each index, so writes get slower, and each index takes storage and memory. Add indexes for frequent, measured queries, and drop indexes that are never used.
SThe daily attendance report is slow. Walk through how you would decide which index to add.scenario
Measure the query with SET STATISTICS IO and the actual plan, and find the scan on the
big table. Index the filtered column first, here AttendanceDate, and include the columns
the query reads, here Status. Measure again: in the SMS, reads dropped from 3,351 to 38.
Follow measure, analyze, change, measure. Explain the column choice (filter first, include the rest) and confirm the plan changed to a seek. Mention checking the write cost on a busy table.
SA table has 23 indexes and inserts are slow. What do you do?scenario
Check sys.dm_db_index_usage_stats over a representative period to find indexes with many
updates and no reads, and look for indexes that duplicate or overlap others. Drop or merge
them after review, keeping the ones that serve important queries.
Show a data-driven clean-up: usage statistics, overlap analysis, and agreement with the team. Mention that usage stats reset on restart, so measure over a long enough period, and keep indexes that support constraints.
AHow do you plan an indexing strategy for a new system?design
Start with primary keys, unique constraints, and indexes on foreign keys used in joins. Then add indexes for the most frequent and most important queries, tested with realistic data volumes. Review usage and missing-index data after launch, and maintain indexes and statistics on a schedule.
Describe the lifecycle: design-time basics, query-driven additions, load testing, and production review. Balance read speed against write cost per table, such as the busy attendance table versus rarely written masters.
Execution plans
FWhat is an execution plan?concept
The step-by-step recipe SQL Server chooses to run a query: which indexes to read, in what order, and how to join the results. You read it in SSMS to see why a query is slow.
See the example · Lesson 27: Execution plans and tuning · Permalink
FWhat is the difference between an estimated and an actual execution plan?concept
The estimated plan is built without running the query and shows guessed row counts. The actual plan is captured while the query runs and adds real row counts. Use the actual plan when you can, because big gaps between estimated and actual rows point to problems.
See the example · Lesson 27: Execution plans and tuning · Permalink
FWhat does SET STATISTICS IO ON show, and why is it useful?concept
It reports the logical reads (8 KB pages read) for each table a query touches. Logical reads do not change with caching, so they are a fairer before-and-after measure than duration.
See the example · Lesson 27: Execution plans and tuning · Permalink
MWhat does SARGable mean? Give an example of a non-SARGable condition.concept
A SARGable condition lets SQL Server seek in an index. YEAR(AttendanceDate) = 2026 is not
SARGable because the function hides the column; in the SMS it read 1,567 pages, while
AttendanceDate = '2026-07-01' read 38. Compare the bare column, using a date range if needed.
See the example · Lesson 27: Execution plans and tuning · Permalink
MWhat is an implicit conversion, and how does it hurt performance?concept
When a value's type differs from the column's, SQL Server converts one of them. If it must
convert the column, as with a VARCHAR column compared to an NVARCHAR value, it cannot seek.
In the SMS, N'ADM-2017-0001' caused a scan of 39 reads; 'ADM-2017-0001' was a 4-read seek.
See the example · Lesson 27: Execution plans and tuning · Permalink
MWhat is a key lookup, and why might SQL Server ignore your index?concept
A key lookup fetches columns missing from a nonclustered index, one row at a time. For many
rows, SQL Server decides a full scan is cheaper: 668 matching Sharmas led to a scan of 126
reads. A covering index with INCLUDE turned it into a 6-read seek.
See the example · Lesson 27: Execution plans and tuning · Permalink
SA query ran in 1 second yesterday and takes 40 seconds today. Nothing was deployed. What do you check?scenario
Check for blocking first, then compare today's plan with the old one, using Query Store if it is on. Common causes are parameter sniffing (a plan built for unusual values), outdated statistics after a big data load, and data growth past a tipping point.
Give a calm, ordered checklist: blocking, plan change, statistics, data volume. Explain how you would prove the cause, then the fix, such as updating statistics, forcing a good plan, or adjusting an index. Mention measuring after the fix.
See the example · Lesson 27: Execution plans and tuning · Permalink
SWhat is parameter sniffing, and how do you deal with it?concept
SQL Server builds a procedure's plan for the first parameter values it sees and reuses it.
That plan can be poor for very different values, such as a tiny section versus a huge one.
Fixes include better indexes, OPTION (RECOMPILE) for a specific query, or forcing a good
plan with Query Store.
Explain that sniffing is normally helpful, then show when it hurts with the SMS class list example. Compare fixes by cost: RECOMPILE costs CPU per call, while a better index helps every value.
See the example · Lesson 27: Execution plans and tuning · Permalink
AHow would you set up ongoing query performance monitoring for a production database?design
Turn on Query Store to keep plan and runtime history, and track the top queries by total CPU and reads. Alert on regressions, blocking, and deadlocks, and review the top queries regularly, tuning with the measure, change, measure method.
Describe the tools (Query Store, DMVs, alerts), the routine (weekly top-N review), and the ownership (who acts on alerts). Link it to business outcomes, such as the attendance peak at the start of the school day.
See the example · Lesson 27: Execution plans and tuning · Permalink
Security and backup
FWhat is the difference between a login and a user in SQL Server?concept
A login is server-level and controls who can connect. A user is database-level, mapped to
a login, and holds permissions inside one database. For example, SchoolApiLogin connects,
and SchoolApiUser works in SchoolDb.
See the example · Lesson 28: Security and backup · Permalink
FWhat do GRANT, DENY, and REVOKE do?concept
GRANT allows an action, DENY forbids it, and REVOKE removes an earlier grant or deny.
DENY always wins over GRANT, which is how a role can delete in general but never delete
from Students.
See the example · Lesson 28: Security and backup · Permalink
FWhat are the full, differential, and transaction log backup types?concept
A full backup copies the whole database. A differential copies changes since the last full backup. A log backup copies changes since the last log backup and allows restoring to a point in time.
See the example · Lesson 28: Security and backup · Permalink
MWhat is the difference between the SIMPLE and FULL recovery models?concept
SIMPLE allows no log backups, so you can restore only to the last full or differential backup. FULL needs regular log backups, or the log keeps growing, and it allows restoring to any point in time. Use SIMPLE for development and FULL for production.
See the example · Lesson 28: Security and backup · Permalink
MWhy should an application not connect as sa or db_owner?concept
One bug or SQL injection would then have full control, including dropping the database. Least privilege gives the application its own login and a role with only the permissions it needs, such as running procedures and changing specific tables.
See the example · Lesson 28: Security and backup · Permalink
MWhat are RPO and RTO?concept
RPO (Recovery Point Objective) is how much data the business can afford to lose, such as 15 minutes. RTO (Recovery Time Objective) is how long the system may be down, such as 2 hours. Together they decide the backup types and schedule.
See the example · Lesson 28: Security and backup · Permalink
SThe school can lose at most 15 minutes of data. Design the backup schedule and explain a restore to 10:40 AM Wednesday.scenario
Use the FULL recovery model, a full backup on Sunday night, differentials on the other nights,
and log backups every 15 minutes. To restore: Sunday's full backup, Tuesday night's differential,
then every log backup up to 10:40 with STOPAT.
Start from RPO and RTO, then derive the schedule. Walk through the restore sequence step by step, and add retention, off-site copies, and regular restore tests.
See the example · Lesson 28: Security and backup · Permalink
SHow do you know your backups actually work?concept
RESTORE VERIFYONLY checks that a backup file is readable, but only a real restore proves it.
Restore regularly to a test server, run DBCC CHECKDB, check key row counts, and time it
against the RTO.
Distinguish verifying a file from proving a restore. Describe an automated, scheduled restore test with results someone reviews, and mention that the course's restored copy was checked by counting students.
See the example · Lesson 28: Security and backup · Permalink
AHow would you protect a school database against ransomware?design
Follow 3-2-1: three copies, on two kinds of storage, one off site, plus one immutable copy that cannot be changed or deleted for a set time. Limit who can reach backup storage, use separate credentials for it, and test restores from the immutable copy.
Cover prevention (least privilege, patching), protection of backups (immutability, separate accounts), and recovery (tested restore plan meeting the RTO). Keep it business-focused: the school must be running again on the next school day.
See the example · Lesson 28: Security and backup · Permalink
Interview practice
FWrite a query to find the second-highest mark in a paper.coding
Use DENSE_RANK() OVER (ORDER BY MarksObtained DESC) and keep rank 2, or use a subquery:
MAX(MarksObtained) WHERE MarksObtained < (SELECT MAX(...)). For 10-A's Mathematics marks of
95, 92, 85, and 67, both return 92.00, and DENSE_RANK handles ties and any N.
See the example · Lesson 29: SQL interview lesson · Permalink
FHow do you find duplicate values in a column?coding
Group by the column and keep groups with more than one row: SELECT LastName, COUNT(*) FROM dbo.Students GROUP BY LastName HAVING COUNT(*) > 1;. To delete duplicates while keeping one,
use ROW_NUMBER() partitioned by the column.
See the example · Lesson 29: SQL interview lesson · Permalink
FWhat is the difference between UNION and UNION ALL?concept
UNION ALL keeps every row; UNION removes duplicates, which needs a sort or hash. In the
SMS, 8 teacher and 22 guardian last names give 30 rows with UNION ALL but 19 with UNION.
Prefer UNION ALL when duplicates are impossible or wanted.
See the example · Lesson 29: SQL interview lesson · Permalink
MHow do you find rows in one table with no match in another?coding
Use NOT EXISTS with a correlated subquery, or a LEFT JOIN and WHERE right.Id IS NULL.
Avoid NOT IN when the subquery's column can be NULL, because one NULL makes it return no rows.
See the example · Lesson 29: SQL interview lesson · Permalink
MHow do you turn rows into columns, such as one column per attendance status?coding
Use conditional aggregation: SUM(CASE WHEN Status = 'Present' THEN 1 ELSE 0 END) AS Present, one
expression per status, grouped by student. SQL Server's PIVOT keyword does the same, but
SUM(CASE ...) is easier to read and works in every database.
See the example · Lesson 29: SQL interview lesson · Permalink
MWhy is DATEDIFF(YEAR, DateOfBirth, @Date) wrong for age, and how do you fix it?coding
DATEDIFF counts year boundaries crossed, not birthdays. Someone born 23 August 2011 gets
15 on 1 July 2026, but is really 14. Subtract 1 when the birthday has not yet happened in
that year.
See the example · Lesson 29: SQL interview lesson · Permalink
STell me about a database you designed.scenario
In about two minutes, cover what it is, the key design choices, the rules enforced in the database, the performance work with numbers, and safety. For the SMS: 22 tables, enrollments per year, a trigger and transactional procedures, indexes cutting 3,351 reads to 38, and least-privilege roles with tested backups.
Use the five-part structure and give at least one measured result. Be ready for follow-ups on any part, and mention a trade-off you made and why.
See the example · Lesson 29: SQL interview lesson · Permalink
SThe fee report is suddenly slow. Walk me through your answer.scenario
Restate the symptom and ask what changed, then measure: actual plan, logical reads, and blocking. List the likely causes in order, such as a plan change, outdated statistics, or a missing index after data growth. Make the smallest fix, measure again, and add monitoring.
Use the five-part scenario structure: symptom, measure, cause, fix, prevent. Keep each part short and concrete, naming the exact tools you would use.
See the example · Lesson 29: SQL interview lesson · Permalink
AAs a lead, how would you assess a candidate's SQL skills?design
Mix a short live task on a realistic schema, a design discussion, and a scenario question. Judge how they clarify requirements, handle NULLs and ties, test edge cases, and explain trade-offs, not just whether the query runs.
Describe a structured, fair process: the same tasks and scoring rubric for every candidate, with expectations matched to the level. Mention watching for thinking aloud and self-correction.
See the example · Lesson 29: SQL interview lesson · Permalink
Temp tables and cursors
FWhat is a temp table in SQL Server?concept
A table whose name starts with #, stored in tempdb and visible only to your session.
It lives until the session ends or you drop it. You use it to save an intermediate step
of a report and reuse it in later queries.
See the example · Extra 1: Temp tables and cursors · Permalink
FWhat is the difference between #Name and ##Name?concept
#Name is a local temp table, visible only to the session that created it. ##Name is a
global temp table, visible to every session. Reports should use local tables, so two users
never share or overwrite each other's data.
See the example · Extra 1: Temp tables and cursors · Permalink
FWhat is a cursor, and what are its steps?concept
A cursor reads a query's rows one at a time. The steps are DECLARE, OPEN, FETCH NEXT,
a WHILE @@FETCH_STATUS = 0 loop, and finally CLOSE and DEALLOCATE.
See the example · Extra 1: Temp tables and cursors · Permalink
MCompare a temp table, a table variable, and a CTE.concept
A CTE is not stored and belongs to one statement. A temp table lives in tempdb until the
session ends, has statistics, and can have any index. A table variable lives for one batch,
has no column statistics, and is not undone by ROLLBACK. Use temp tables for big or reused
steps.
See the example · Extra 1: Temp tables and cursors · Permalink
MWhy is a cursor usually slower than a set-based query?concept
A cursor runs one small query per row, each with its own overhead. A set-based query reads
the data once in one plan. In the SMS, a cursor over 10,012 students took about 2.9 seconds,
and one GROUP BY gave identical results in about 0.2 seconds.
See the example · Extra 1: Temp tables and cursors · Permalink
MWhat happens to a table variable's rows when a transaction rolls back?concept
They stay. Table variables are not part of the transaction, so ROLLBACK does not undo
them. This is useful in a CATCH block: save error details in a table variable, roll back,
and then write them to a log table.
See the example · Extra 1: Temp tables and cursors · Permalink
SA nightly job loops over 10,000 students with a cursor and takes 40 minutes. How do you fix it?scenario
Find what each loop iteration does, and express the whole job as a few set-based statements,
using temp tables for intermediate steps. Prove the new version gives the same rows with
EXCEPT, then measure. Keep a loop only for work that truly must happen one row at a time.
Show the method: understand, rewrite in sets, prove equality, measure. Mention SET NOCOUNT ON, testing on a copy of production-sized data, and running old and new versions side by
side before switching.
See the example · Extra 1: Temp tables and cursors · Permalink
SWhen is a cursor the right choice?concept
When each row must go through logic that handles one row at a time, such as calling a
procedure like usp_RecordFeePayment for every payment in an uploaded file. Admin scripts
that loop over databases or tables are another fair use. Use LOCAL FAST_FORWARD.
Avoid "never use cursors". Give the rule (if it can be one query, write one query), then real exceptions, and mention the alternative of a set-based procedure that accepts many rows.
See the example · Extra 1: Temp tables and cursors · Permalink
AHow would you guide a team that writes most reports with loops and cursors?design
Set a review rule: set-based first, cursors only with a written reason. Provide patterns (temp-table steps, window functions, conditional aggregation), pair on rewriting the slowest jobs, and track their run times so the gains are visible.
Focus on changing habits, not one query: standards, examples, mentoring, and measurement. Mention tempdb sizing and monitoring as temp tables become more common.
See the example · Extra 1: Temp tables and cursors · Permalink
MERGE and upserts
FWhat is an upsert?concept
Updating a row if it already exists and inserting it if it does not, in one operation. Loading a revised fee list, where some fees change and some are new, is an upsert.
FWhat are the three branches of a MERGE statement?concept
WHEN MATCHED (in both, usually update), WHEN NOT MATCHED BY TARGET (only in the source,
usually insert), and WHEN NOT MATCHED BY SOURCE (only in the target, usually delete). Each
is optional, and the statement must end with a semicolon.
FWhat is a staging table?concept
A table that holds incoming data, such as an uploaded file, before it touches the real tables. You can check and clean the data there, then apply it with a merge.
MHow do you see what a MERGE changed?coding
Add an OUTPUT clause. $action returns INSERT, UPDATE, or DELETE for each row, deleted
holds the old values, and inserted the new ones. You can also send the output into a log
table with OUTPUT ... INTO.
MWhat causes error 8672 in a MERGE, and how do you prevent it?concept
One target row matched more than one source row, so SQL Server would update or delete it twice. Prevent it with a primary key on the staging table, or remove duplicates before the merge.
MWhy add WITH (HOLDLOCK) to a MERGE?concept
Without it, two sessions running the same merge can both decide a row is missing and both
insert it, causing a duplicate key error. HOLDLOCK keeps the range locked for the whole
statement, so the second session waits.
SA teacher uploaded one section's student list and the import deleted every other section's students. What went wrong, and how do you prevent it?scenario
The merge had a WHEN NOT MATCHED BY SOURCE THEN DELETE branch on the whole table, so every
row missing from the file counted as deleted. Limit the target to the rows the file covers,
preview deletes before running, and require confirmation. Foreign keys and backups are the last
line of defense.
Explain the mechanism, then the immediate recovery (restore or rebuild from backup), then prevention at several layers: scoped targets, previews, confirmation, foreign keys, and tests with a partial file.
SMERGE or UPDATE plus INSERT: which do you choose for an upsert?concept
Both work when written carefully. MERGE is shorter and can also delete, but has more traps:
duplicates, scoping, and concurrency. UPDATE plus INSERT in a transaction with
UPDLOCK, HOLDLOCK is longer but easier to review. Follow the team standard.
Compare on correctness, readability, and concurrency, and show you know the traps of each. Mention making the upsert idempotent, so a repeated import changes nothing.
ADesign a safe fee-list import for 1,000 schools.design
Upload into a staging table with keys and validation, show a preview of inserts, updates, and
deletes, and require a person to confirm. Apply in a short transaction with locking hints,
log every change with OUTPUT, and keep the import idempotent so retries are harmless.
Cover the full flow: upload, validate, preview, approve, apply, audit. Address scale (one school's import must never touch another school's rows) and recovery (undo from the change log).
JSON
FWhat is JSON, and how does SQL Server store it?concept
JSON is a text format of objects (name and value pairs), arrays, and values, used by web and
mobile apps. SQL Server 2022 stores it as text in NVARCHAR(MAX); SQL Server 2025 adds a
native json type.
FWhat do FOR JSON PATH and OPENJSON do?concept
FOR JSON PATH turns query results into JSON text. OPENJSON does the opposite: it turns
JSON text into rows, which you can select or insert into a table.
FHow do you check whether a string is valid JSON?concept
ISJSON(@text) returns 1 for valid JSON and 0 otherwise. A trailing comma, for example,
makes it return 0.
MHow do you return nested JSON, such as a student with a guardians array?coding
Use FOR JSON PATH. A dot in a column alias, like [currentClass.section], creates a nested
object. A correlated subquery that ends with FOR JSON PATH creates an array. Add
WITHOUT_ARRAY_WRAPPER to return a single object.
MWhat is the difference between JSON_VALUE and JSON_QUERY?concept
JSON_VALUE returns a single value, such as a number or text. JSON_QUERY returns an object
or array. JSON_VALUE on an object returns NULL, which is a common surprise.
MHow do you insert rows from a JSON array?coding
Use INSERT ... SELECT ... FROM OPENJSON(@json) WITH (Column TYPE '$.path', ...). The WITH
clause names each column, its type, and its path; missing properties become NULL. Paths are
case-sensitive.
SThe attendance app sends one request per student and some fail on weak networks. How would you redesign it?scenario
Send the whole section as one JSON array and save it with one INSERT ... OPENJSON (or a
bulk insert from C#) inside a transaction. The section is then saved completely or not at
all, and the app can retry safely because the unique key prevents duplicates.
Explain the failure mode (partial saves), the new design (one atomic call), and the safety net (validation, constraints, unique keys for idempotent retries). Mention a clear error message for the teacher.
SWhen should data be stored as JSON instead of normal columns?concept
Use JSON for data with a varying shape that you store and return whole, such as audit details or extra settings. Use normal columns for anything you filter, join, sort, or must check with constraints.
Give both sides with examples, then the cost of getting it wrong: slow, unchecked filters on JSON, or many sparse columns. Mention computed columns with indexes for the occasional JSON filter.
AShould JSON for an API be built in SQL Server or in the application?design
Usually in the application: the API controls its own response format, and C# serializes
objects automatically. FOR JSON is a good option for large nested reports read straight from
the database, and OPENJSON for saving many rows sent in one call.
Weigh separation of concerns, performance, testability, and team skills. Show you would pick per case and keep one consistent default.
Temporal tables
FWhat is a temporal (system-versioned) table?concept
A table for which SQL Server automatically keeps every old version of every row in a history
table. Two period columns, ValidFrom and ValidTo, record when each version was valid.
FHow do you see a table as it was at a past moment?coding
Query it with FOR SYSTEM_TIME AS OF @moment. SQL Server combines current and history rows
to rebuild the table at that moment. FOR SYSTEM_TIME ALL returns every version.
FIn which time zone are a temporal table's period columns stored?concept
UTC. SQL Server fills them itself, so a local time must be converted to UTC before using it
in AS OF.
MHow do you turn an existing table into a temporal table?coding
Add two DATETIME2 columns GENERATED ALWAYS AS ROW START and ROW END with defaults and a
PERIOD FOR SYSTEM_TIME, then SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = ...)). HIDDEN
keeps the new columns out of SELECT *.
MCan you edit or delete rows in a temporal history table?concept
Not while versioning is on: updates fail with error 13561 and deletes are blocked too. Only
someone with ALTER TABLE permission can turn versioning off, which makes the history
trustworthy.
MDoes a temporal table record who changed a row?concept
No, only when. Keep an UpdatedBy column in the row; because it is versioned with the rest
of the row, the history then shows who made each change.
SA parent disputes last term's fee. How would temporal tables help, and how would you answer them?scenario
Query the fee table FOR SYSTEM_TIME AS OF the payment time, converted to UTC, and show the
amount that was valid then, plus who changed it and when from the versions list. The history
cannot be edited, so the answer is reliable.
Walk through the query, the UTC conversion, and how you present the evidence. Mention that without versioning you would depend on invoices and emails.
SCompare audit triggers, temporal tables, and change data capture.concept
Audit triggers keep exactly what you choose and need code. Temporal tables keep every version automatically and answer "as of" questions. Change data capture reads changes from the log for other systems and needs SQL Server Agent.
Compare setup, storage, query style, and requirements, then pick per need: temporal for prices and settings, triggers for custom audit rules, CDC for data feeds.
AWhich tables in a multi-school SaaS would you make temporal, and how would you manage history growth?design
Version tables whose past values matter and change rarely: fees, prices, rates, and settings. Avoid busy tables like attendance. Set a retention period that matches the legal rule, watch history size, and include history tables in backup and archiving plans.
Tie each choice to a business question ("what was it on that date?"), then cover cost, retention, compliance, and operations.
C#
.NET platform
FWhat is the difference between the .NET SDK and the .NET runtime?concept
The runtime only runs .NET applications that are already built. The SDK (Software
Development Kit) contains the runtime plus the compiler and the dotnet command-line
tools to create, build, test, and publish applications. Developers install the SDK;
servers that only run the app need just the runtime.
MWhat is the difference between LTS and STS releases of .NET, and which would you choose for a production system?concept
LTS (Long Term Support) releases, such as .NET 10, get fixes and security patches for 3 years. STS (Standard Term Support) releases get them for 2 years. For a production system that must run for years, like a school system, choose LTS and plan upgrades from one LTS to the next.
FWhat is the CLR?concept
The Common Language Runtime is the part of .NET that runs your compiled code. It loads the IL, turns it into machine code with the JIT compiler, manages memory with the garbage collector, and handles exceptions.
FWhat is IL, and where is it stored?concept
IL (Intermediate Language) is what the C# compiler produces: a platform-neutral set of
instructions stored in the .dll file. The CLR turns it into machine code when the program runs.
FWhat is the difference between .NET and .NET Framework?concept
.NET Framework is the original, Windows-only platform; version 4.8.1 is the last, and it only gets maintenance. .NET (Core, then .NET 5 and later) is cross-platform, faster, and where all new development happens.
MWhat does JIT compilation mean, and why does .NET use it?concept
Just-In-Time compilation turns IL into machine code the first time each method runs, on the
machine that runs it. The same .dll can therefore run on Windows, Linux, and macOS, and the
JIT can optimize for the exact processor.
MWhat are top-level statements?concept
Code written directly in Program.cs without a class or Main method. The compiler generates
the Main method for you. Only one file in a project can have them, and they are ideal for
small programs and for the startup code of ASP.NET Core apps.
MWhat files does dotnet build produce, and what are they for?concept
In bin/Debug/net10.0: the .dll with your IL, an .exe launcher on Windows, a .pdb with
debug information, runtimeconfig.json naming the .NET version to use, and deps.json listing
dependencies. They are rebuilt every time and never committed.
SAn app runs on your laptop but fails to start on the server. How do you check whether it is a .NET version problem?scenario
Read the app's runtimeconfig.json to see which framework and version it needs, then run
dotnet --list-runtimes on the server. If the matching runtime is missing, install it, or
publish the app self-contained so it carries its own runtime.
Show a methodical check: what the app needs, what the server has, and the startup error message. Mention ASP.NET Core apps need the ASP.NET Core runtime, not just the base runtime.
SHow would you organize a .NET solution for a system with an API, shared logic, data access, and tests?design
One solution with projects in src (for example SchoolApp.Api, SchoolApp.Core,
SchoolApp.Data) and tests. Core holds models and rules with no dependencies, Data
references Core, and the API references both. Tests reference what they test.
Explain the dependency direction and why: Core must not depend on databases or web
frameworks, so it stays easy to test and reuse. Mention consistent naming and the .slnx format.
AFramework-dependent or self-contained deployment: how do you choose?design
Framework-dependent apps are small and use the runtime installed on the server, which the operations team patches. Self-contained apps carry their own runtime: larger, but independent of the server, and you must ship updates for security fixes yourself. Containers make this choice simpler, because the image includes the runtime.
Compare size, patching responsibility, and control over the version. Tie the decision to how the organization deploys: managed servers, customer machines, or containers.
Variables and data types
FWhich C# types match the SQL Server types INT, BIGINT, DECIMAL, NVARCHAR, DATE, and BIT?concept
int, long, decimal, string, DateOnly, and bool. Matching the types avoids
conversion errors and silently wrong values when data moves between the database and C#.
See the example · Lesson 31: Variables and data types · Permalink
FWhat does the var keyword do?concept
It lets the compiler work out the variable's type from the value on the right, for example
var tuition = 42000.00m; is a decimal. The type is still fixed at compile time; var only
saves typing, so use it when the type is obvious.
See the example · Lesson 31: Variables and data types · Permalink
FWhat is the difference between Parse and TryParse?concept
int.Parse converts text to a number and throws an exception if it cannot. int.TryParse
returns true or false and gives the number through an out parameter, so bad input never
crashes the program. Use TryParse for anything a person types.
See the example · Lesson 31: Variables and data types · Permalink
MWhy should money use decimal instead of double?concept
double stores numbers in binary, so most decimal fractions are approximations: 0.1 + 0.2
gives 0.30000000000000004. decimal stores base-10 numbers exactly, so sums of fees and
payments are exact. double is for measurements where tiny errors do not matter.
See the example · Lesson 31: Variables and data types · Permalink
MWhat is the difference between an implicit conversion and an explicit cast?concept
An implicit conversion is always safe and needs no code, such as int to long. An explicit
cast may lose data, so you must write it, such as (int)92.75m, which gives 92 because the
fraction is cut off, not rounded.
See the example · Lesson 31: Variables and data types · Permalink
MWhat is the difference between value types and reference types?concept
A value type variable holds the value itself (numbers, bool, char, DateOnly, structs).
A reference type variable holds a reference to an object stored elsewhere (string, classes,
arrays), and can be null. Copying a value type copies the value; copying a reference copies
only the reference.
See the example · Lesson 31: Variables and data types · Permalink
SSeveral invoices stay 'Partially paid' although families paid in full. What would you check first?scenario
The types used for money. If payments are summed in double, the balance can be a tiny
non-zero number such as 0.00000000003, so the status check never sees zero. Change money
variables to decimal, then correct the stuck invoices with a one-off script.
Connect the symptom to floating-point rounding, show how you would prove it (print the exact balance), fix the root cause, repair existing data, and prevent it with code review rules.
See the example · Lesson 31: Variables and data types · Permalink
SWhat happens when an integer overflows in C#, and how do you guard against it?concept
By default it wraps around silently: a byte at 255 plus 1 becomes 0. Choose types large
enough for the data, such as long for Ids that can grow, and use a checked block or the
project's overflow-checking setting where a wrong value would be dangerous, so overflow throws
an exception instead.
Show the default behavior, the risk (wrong totals or Ids), and two defenses: correct type sizing and checked arithmetic. Mention matching SQL types such as TINYINT and BIGINT.
See the example · Lesson 31: Variables and data types · Permalink
AHow would you keep C# types consistent with the database across a large team?design
Document one type mapping (for example DECIMAL to decimal, DATE to DateOnly), generate
or scaffold entity classes from the database where possible, and check mismatches in code
review and integration tests that round-trip real data.
Combine a written standard, tooling (scaffolding, analyzers), and tests. Mention that the database stays the source of truth for column types, as the SMS reference file does.
See the example · Lesson 31: Variables and data types · Permalink
Conditions and loops
FWhat is the difference between = and == in C#?concept
= assigns a value to a variable; == compares two values and returns true or false.
Writing if (mark = 33) does not compile in C#: error CS0029, because an int is not a bool.
See the example · Lesson 32: Conditions and loops · Permalink
FWhat are the four loops in C#, and when do you use each?concept
for repeats a known number of times with a counter; while repeats while a condition is
true and may run zero times; foreach visits each item in a collection; do...while runs at
least once and then checks its condition.
See the example · Lesson 32: Conditions and loops · Permalink
FWhat is the difference between break and continue?concept
break leaves the loop immediately. continue skips the rest of the current round and starts
the next one. In a switch statement, break ends each case.
See the example · Lesson 32: Conditions and loops · Permalink
MWhat is the difference between a switch statement and a switch expression?concept
A switch statement runs a block of code per case, with case, break, and default. A switch
expression produces a value from arms like >= 91 => "A1", checked from top to bottom, with _
for anything else. Switch expressions are shorter for mapping one value to another.
See the example · Lesson 32: Conditions and loops · Permalink
MWhat happens if a switch expression has no arm that matches the value?concept
The compiler warns with CS8509 that the switch is not exhaustive, and at run time an unmatched
value throws SwitchExpressionException. Always end with a _ discard arm.
See the example · Lesson 32: Conditions and loops · Permalink
MWhat does the ternary operator do, and when should you avoid it?concept
condition ? a : b returns a when the condition is true, otherwise b, as a one-line if.
Avoid it when the condition or values are complex, or when nested ternaries make the code hard
to read; use if or a switch expression instead.
See the example · Lesson 32: Conditions and loops · Permalink
SStudents with exactly 33 marks are shown as failed. How do you find and prevent this kind of bug?scenario
It is a boundary error: the condition uses > where the rule says "33 or more", so it should be
>=. Fix the operator, use a named constant such as PassMark, and add tests at the exact
boundaries (32, 33, 34) for every rule.
Explain the off-by-one cause, the fix, and the prevention: boundary-value tests and rules kept in one place. Mention re-checking data already produced with the wrong rule.
See the example · Lesson 32: Conditions and loops · Permalink
SA console tool that reads input in a loop hangs forever when run by a scheduled job. Why, and how do you fix it?scenario
When input comes from a file or a job, it can end; Console.ReadLine then returns null every
time, so a validation loop never gets valid input. Check for null and break, and design
batch tools to read arguments or files instead of prompting.
Show you think about every way a loop can end, including end of input. Mention timeouts and clear exit codes for tools run by schedulers.
See the example · Lesson 32: Conditions and loops · Permalink
AThe grading scale exists in SQL (fn_GetGrade) and in C#. How would you keep business rules consistent?design
Keep each rule in one authoritative place where possible, for example in a shared C# library used by every application, or in the database when reports depend on it. If a rule must exist twice, document it, test both against the same boundary cases, and change them together.
Discuss the trade-off between one source of truth and practical needs such as SQL reports, then the safety nets: shared test cases, code review checklists, and configuration-driven rules.
See the example · Lesson 32: Conditions and loops · Permalink
Arrays and strings
FWhat is an array, and where do its positions start?concept
A fixed-size collection of items of one type, stored under one name. Positions (indexes)
start at 0, so the last item is at Length - 1, also written ^1.
FWhat happens when you read an array position that does not exist?concept
The program throws IndexOutOfRangeException: Index was outside the bounds of the array.
For a six-item array, valid positions are 0 to 5.
FWhat does string interpolation do?concept
$"..." inserts values into text inside braces, such as $"Topper: {name}". Format
specifiers control the output: {average:F2} for two decimals, {serial:D5} for zero
padding, {name,-8} for column width.
MWhat does it mean that strings are immutable?concept
A string can never change after it is created. Methods such as Trim and ToUpper return a
new string, so name.Trim(); without assigning the result does nothing. Immutability makes
strings safe to share between threads and use as dictionary keys.
MWhen should you use StringBuilder instead of +?concept
When building text in a loop. Each += copies all the text so far into a new string, so the
cost grows with every line; StringBuilder appends to one buffer. In the SMS, 10,000 CSV
lines took about 620 ms with += and 2 ms with StringBuilder.
MHow do you compare two strings while ignoring upper and lower case?coding
string.Equals(a, b, StringComparison.OrdinalIgnoreCase), or methods like Contains and
StartsWith with a StringComparison argument. Avoid a.ToUpper() == b.ToUpper(), which
creates extra strings and can behave differently across cultures.
SAn export of all students sometimes times out for the largest school. What would you look for?scenario
How the file text is built. Building it with += in a loop has a cost that grows with the
square of the size, so small schools are fine and big ones time out. Switch to StringBuilder
or write lines straight to a stream, then measure with production-sized data.
Explain why the problem grows faster than the data, how you would prove it with a timer or profiler, and the fix. Mention streaming large files instead of holding them in memory.
SWhat are the risks of parallel arrays, and what is the better design?design
Two arrays kept in step, such as names and marks, break when one is sorted, filtered, or
resized without the other, so values attach to the wrong student. Keep related values together
in one object, such as a Student class or record, in one collection.
Show the failure concretely (sorting marks only), then the object-based design. Mention that this is the step from procedural code to object-oriented code.
AWhat should a team standardize about string handling in a multi-school, multi-language system?design
Ordinal, case-insensitive comparison for codes, user names, and emails; culture-aware
formatting only for text shown to people; a fixed format such as yyyy-MM-dd for files and
APIs; Unicode everywhere (NVARCHAR, UTF-8); and StringBuilder or streaming for large output.
Separate machine-facing text (codes, files, APIs: invariant and ordinal) from human-facing text (names, dates on screen: culture-aware). Give one bug each rule prevents.
Methods and parameters
FWhat is the difference between a parameter and an argument?concept
A parameter is the named input in the method's definition, such as decimal maxMarks. An
argument is the actual value passed in a call, such as 50m.
See the example · Lesson 34: Methods and parameters · Permalink
FWhat is an optional parameter?concept
A parameter with a default value, such as decimal maxMarks = 100m. Callers can leave it out,
and the default is used. Optional parameters must come after the required ones.
See the example · Lesson 34: Methods and parameters · Permalink
FWhat is method overloading?concept
Several methods with the same name but different parameter lists, such as
CalculateAttendancePercentage(int, int) and CalculateAttendancePercentage(string). The
compiler picks one from the argument types. A different return type alone is not enough.
See the example · Lesson 34: Methods and parameters · Permalink
MWhat is the difference between ref and out?concept
Both pass the caller's own variable. With ref, the variable must be assigned before the call,
and the method may read and change it. With out, it need not be assigned before, and the method
must assign it before returning. Both keywords appear in the definition and the call.
See the example · Lesson 34: Methods and parameters · Permalink
MWhat is the Try pattern?concept
A method named TryX that returns true or false for success and hands back the result
through an out parameter, like int.TryParse. It handles expected failures, such as bad
input, without exceptions.
See the example · Lesson 34: Methods and parameters · Permalink
MWhat does the params keyword do?concept
It lets callers pass any number of arguments, which C# collects into an array (or another
collection type), such as Average(92m, 85m, 67m). It must be the last parameter.
See the example · Lesson 34: Methods and parameters · Permalink
SA method takes nine parameters. What do you suggest in review?scenario
Too many parameters usually mean the method does too much or the values belong together. Split it into smaller methods, or group related values into an object, such as a request class with properties. Named arguments help readability in the meantime.
Name the smell, explain the risks (wrong order of arguments, hard to test), and offer refactorings: parameter objects, smaller methods, sensible defaults.
See the example · Lesson 34: Methods and parameters · Permalink
SThe same calculation exists in four screens and they disagree after a rule change. How do you fix and prevent it?scenario
Move the calculation into one well-named method with tests at its boundaries, and make every screen call it. Remove the copies. In reviews, treat duplicated business logic as a defect.
Cover the immediate fix (one method, tests), the data fix (correct anything produced wrongly), and prevention (shared library, review rule).
See the example · Lesson 34: Methods and parameters · Permalink
AWhen should an operation return a bool with out parameters, and when should it throw an exception?design
Use the Try pattern when failure is expected in normal use, such as parsing what a user typed.
Throw an exception when failure means something is truly wrong, such as a missing database
record that must exist. Many APIs offer both, like int.Parse and int.TryParse.
Tie the choice to how often failure happens and who handles it. Mention the cost of exceptions and the clarity of result types in larger systems.
See the example · Lesson 34: Methods and parameters · Permalink
Nullable types
FWhat is a nullable value type?concept
A value type with ?, such as int? or decimal?, that can hold either a value or null.
It has HasValue and Value. It maps to SQL columns that allow NULL, such as a mark for an
absent student.
FWhat do the ?? and ?. operators do?concept
?? returns the left value unless it is null, then the right value, like SQL's COALESCE.
?. accesses a member only if the object is not null, and returns null otherwise, instead of
throwing NullReferenceException.
FWhat causes a NullReferenceException?concept
Using a member, such as .Length or a method, on a variable that holds null. The message is
"Object reference not set to an instance of an object." Check for null first, or use ?. and ??.
MWhat are nullable reference types?concept
A compiler feature, turned on with <Nullable>enable</Nullable>, where string means "never
null" and string? means "may be null". The compiler warns (CS8600, CS8602, and others) when
code might use a null unsafely. It changes warnings, not run-time behavior.
MWhat does the null-forgiving operator ! do, and why is it risky?concept
x! tells the compiler to treat x as not null and silences the warning. It does nothing at
run time, so if x is null the program still crashes. Use it only when you truly know better
than the compiler, with a comment.
MHow should NULL marks be handled when calculating a class average?coding
Skip them: add only marks that are not null and divide by how many were counted, as SQL's AVG
does. Treating an absent student's null as 0 lowers the average unfairly: 395 ÷ 5 is 79, but
395 ÷ 6 is 65.83.
SThe parent app crashes for about one family in five. The log shows NullReferenceException. How do you find and fix it?scenario
Read the stack trace for the exact line, then find which value is null and why, for example a
guardian with no email. Fix it where the value is used, with a null check and a sensible fallback,
and make the type string? so the compiler warns at every other use.
Show the route from symptom to cause (stack trace, data pattern), the fix, and the prevention: nullable reference types on, warnings as errors, tests with missing data.
SHow do you introduce nullable reference types into a large existing codebase?design
Enable it project by project or file by file (#nullable enable), starting with shared core
code. Fix warnings in small batches, annotate public APIs first, and turn warnings into errors
once a project is clean, so it stays clean.
Emphasize a gradual, measurable rollout rather than enabling everything at once, and explain why public method signatures are the most valuable place to start.
AHow should 'no value' flow consistently from the database through the API to the client?design
Decide per field: NULL-able columns map to ? types in C#, and the API either returns null in
JSON or omits the field, documented in the API contract. Never turn missing data into fake values
such as 0 or empty strings, which clients cannot tell apart from real data.
Walk through each layer (database, data access, domain, API, client) and the rule at each. Mention consistency, documentation, and tests with missing data.
Exceptions and debugging
FWhat do try, catch, and finally do?concept
try holds code that might fail. catch runs when a matching exception is thrown in try.
finally always runs afterwards, success or failure, so it is used for cleanup.
See the example · Lesson 36: Exceptions and debugging · Permalink
FWhat happens when an exception is not caught anywhere?concept
It travels up through every calling method. If none catches it, the program stops and prints "Unhandled exception" with the exception type, message, and stack trace.
See the example · Lesson 36: Exceptions and debugging · Permalink
FWhat is a breakpoint, and what do F10 and F11 do?concept
A breakpoint (F9) pauses the program at a line so you can inspect variables. F10 steps over the current line; F11 steps into the method called on that line. The Locals and Call Stack windows show the values and the chain of calls.
See the example · Lesson 36: Exceptions and debugging · Permalink
MWhat is the difference between throw; and throw ex;?concept
throw; rethrows the current exception and keeps its original stack trace. throw ex; restarts
the stack trace from the rethrow, so the place where the error really happened is lost. The
analyzer warns with CA2200. Always use throw;.
See the example · Lesson 36: Exceptions and debugging · Permalink
MWhy does the order of catch blocks matter?concept
C# uses the first catch block whose type matches. If catch (Exception) comes first, it matches
everything, and later specific blocks never run. Put the most specific types first.
See the example · Lesson 36: Exceptions and debugging · Permalink
MWhen should you throw an exception, and when should you return a result such as false?concept
Return a result for expected situations, such as invalid user input, using the Try pattern. Throw when a rule is broken in a way the method cannot handle, such as a mark above the maximum reaching the save method. Exceptions are for exceptional cases, not normal flow.
See the example · Lesson 36: Exceptions and debugging · Permalink
SPayments sometimes go missing with no error anywhere. What would you suspect, and how would you investigate?scenario
A swallowed exception: an empty catch or one that logs nothing. Search for empty catch blocks
around the save path, use the debugger's Exception Settings to stop where exceptions are thrown,
and check database logs. Remove the empty catch so the real error appears, then fix the cause.
Show a structured investigation: reproduce, break on thrown exceptions, read logs, then fix the root cause. Add prevention: review rules against empty catch blocks, and logging with context.
See the example · Lesson 36: Exceptions and debugging · Permalink
SHow do you read a stack trace to find a bug?concept
Read the exception type and message first, then the at lines from the top, skipping framework
methods (System.) until you reach your own code. The first line in your code with a file and
line number is usually where to start; the lines below show who called it.
Walk through a real trace. Mention that release builds still give method names, that missing
.pdb files remove line numbers, and that throw ex; destroys the useful part.
See the example · Lesson 36: Exceptions and debugging · Permalink
ADesign an exception-handling strategy for a layered application with an API.design
Validate input early and return clear validation results. Let lower layers throw specific exceptions for broken rules, and do not catch what you cannot handle. Handle everything once, at the edge: global middleware logs the full exception with a correlation id and returns a safe, consistent error response, without exposing internal details.
Cover where exceptions are thrown, where they are caught (one place), what is logged, and what the client sees. Mention custom exception types for domain rules and mapping them to HTTP status codes.
See the example · Lesson 36: Exceptions and debugging · Permalink
OOP
Classes and objects
FWhat is the difference between a class and an object?concept
A class is a design that describes what something has (properties) and can do (methods).
An object is one instance created from it with new. Student is a class; Aarav and Diya
are objects with their own values.
See the example · Lesson 37: Classes and objects · Permalink
FWhat is a constructor?concept
A special method with the class's name and no return type that runs once when an object is created. It sets up the object, and by requiring parameters it ensures no object exists without the values it needs, such as a student's name.
See the example · Lesson 37: Classes and objects · Permalink
FWhat are the four pillars of object-oriented programming?concept
Encapsulation (a class protects its data and rules), inheritance (a class reuses and extends another), polymorphism (one call behaves differently for different types), and abstraction (code depends on what something does, not how).
See the example · Lesson 37: Classes and objects · Permalink
MTwo variables refer to one object, and you change a property through one of them. What does the other see?concept
The change. Classes are reference types: assignment copies the reference, not the object, so both variables point to the same object. To get an independent copy, create a new object.
See the example · Lesson 37: Classes and objects · Permalink
MWhy does == return false for two Student objects with identical values?concept
For classes, == compares references by default: it asks whether both variables point to the
same object. Two separately created objects are different objects. Records, or overriding
Equals, give value-based equality.
See the example · Lesson 37: Classes and objects · Permalink
MHow do you create a custom exception, and why would you?coding
Create a class that inherits from Exception and passes the message to base(message), adding
any extra data, such as an ErrorNumber. Callers can then catch your application's rule
failures separately from system errors and react to them specifically.
See the example · Lesson 37: Classes and objects · Permalink
SA report shows one student's name with another student's marks. The code uses two arrays. How would you fix it?scenario
The parallel arrays went out of step, probably after sorting one of them. Replace them with one
class that holds the student and the mark together, such as ExamResult, and keep a single
collection of those objects.
Explain the root cause, the object-based design, and why it removes the whole class of bug. Mention testing the report after sorting and filtering.
See the example · Lesson 37: Classes and objects · Permalink
SWhat is a 'god class', and how do you avoid one?design
A class that knows and does too much, such as a SchoolManager with hundreds of methods for
students, fees, and exams. It is hard to change and test. Split it by responsibility into
smaller classes, each with its own data and the rules that use that data.
Give the warning signs (huge files, unrelated methods, many reasons to change), the refactoring approach, and connect it to the Single Responsibility Principle (Lesson 46).
See the example · Lesson 37: Classes and objects · Permalink
AHow would you decide which classes go into a shared core library?design
Put the domain model and business rules that every application needs (entities, rule exceptions, interfaces) in the core library, with no dependency on UI, databases, or web frameworks. Keep presentation and infrastructure code in the projects that need them, which reference the core, never the other way round.
Explain dependency direction and why it matters for reuse and testing. Mention keeping the core
free of Console, HTTP, and SQL so it works from an API, tests, or background jobs.
See the example · Lesson 37: Classes and objects · Permalink
Encapsulation
FWhat is encapsulation?concept
Keeping an object's data private and offering only safe, public ways to change it, so the
object enforces its own rules. A fee invoice's status can change only through RecordPayment.
FWhat do the access modifiers public, private, protected, and internal mean?concept
public: any code. private: only the same class (the default for members). protected: the
class and classes that inherit from it. internal: any code in the same project (assembly).
FWhat is the difference between a field and a property?concept
A field is a variable inside a class. A property looks like a field from outside but runs code when read or written, so it can validate, calculate, or restrict access. Public data should be exposed through properties, not public fields.
MWhat is the difference between { get; }, { get; private set; }, and { get; init; }?concept
{ get; } can be set only in the constructor. { get; private set; } can be changed by the
class's own methods at any time. { get; init; } can be set in the constructor or an object
initializer, and is fixed afterwards.
MWhat is a class invariant? Give an example.concept
A rule that must always be true for an object. For a fee invoice: the status always matches the payments. The constructor checks the starting state and every method keeps the rule, so no sequence of calls can break it.
MWhat does 'tell, don't ask' mean?concept
Instead of asking an object for its data and applying rules outside it, tell the object what to
do and let it apply its own rules, such as invoice.RecordPayment(amount). This keeps each rule
in one place.
SInvoices show 'Paid' without any payments. How would you find the cause and stop it happening again?scenario
Find every code path that writes the status, such as an admin screen or a direct SQL update.
Make the status private set, changed only by the payment method, and restrict direct updates in
the database. Correct existing invoices through proper adjustments that leave an audit trail.
Cover investigation (who writes the field), the design fix (encapsulation), the database side (permissions or procedures), and data repair. Mention audit logs to find when it happened.
SShould every property be private or read-only? When is a public setter fine?design
No. Decide per property who legitimately changes it. Ids are set by data access code, simple descriptive fields may be freely editable, but values tied to rules, such as status or balance, should change only through methods.
Show judgment rather than a blanket rule. Give examples of each case and mention that DTOs, used only to carry data, often have public setters by design.
ABusiness rules exist both in SQL constraints and procedures and in C# classes. Is that duplication a problem?design
It is deliberate defense in depth: C# gives fast, clear feedback, and the database protects the data if any path skips the application. Keep the rules consistent with shared error numbers and tests, and decide which layer is authoritative for each rule.
Weigh duplication against safety. Explain which rules belong in the database (integrity) and which in code (workflow), and how to keep both in step.
Inheritance
FWhat is inheritance?concept
A class (the derived class) building on another (the base class), getting all its members and
adding its own. Student : Person gets the names and FullName from Person and adds an
admission number. Use it for true "is a" relationships.
FWhat does the base keyword do in a constructor?concept
: base(...) calls the base class's constructor with the given arguments, before the derived
constructor's own code runs. It is required when the base class has no parameterless
constructor.
FDoes C# support multiple inheritance?concept
Not for classes: a class has exactly one base class (error CS1721 otherwise). A class can implement many interfaces, which covers most needs for multiple "kinds" of behavior.
MWhat is the difference between private and protected?concept
private members are visible only inside the class itself, even derived classes cannot use
them. protected members are visible to the class and every class that inherits from it, but
not to outside code.
MWhat is the difference between is, as, and a cast when converting a base type to a derived type?coding
is with a pattern (if (p is Student s)) checks and converts safely. as returns null when
the conversion fails. A cast (Student)p throws InvalidCastException when the object is not
a Student. Prefer the is pattern.
MWhy can catch (SchoolRuleException) catch an InvalidMarkException?concept
Because InvalidMarkException inherits from SchoolRuleException, so it is one. A catch block
matches the exception's type or any base type, which lets callers handle a whole family of
errors at once or a specific one separately.
SA hierarchy Person > Staff > Teacher > ClassTeacher breaks when a teacher stops being a class teacher. What went wrong?scenario
A role that changes over time was modeled as a subclass, but an object cannot change its type.
Replace the deep tree with one Teacher class plus data that records the role per year, such as
a ClassTeachers table, and keep hierarchies shallow.
Identify the misuse (role as type), the fix (composition or data), and the general rule: if something can change, it is data or a role, not a subclass.
SComposition or inheritance: how do you choose?design
Inherit for true, permanent "is a" relationships, such as a guardian being a person. Use composition, holding another object, for "has a", such as an invoice having payments. Composition is more flexible, so prefer it when in doubt.
Give the tests ("is a" vs "has a", permanence), examples of each, and the costs of wrong inheritance: tight coupling and fragile base classes.
AThe C# model has a Person base class, but the database has separate Students, Teachers, and Guardians tables. Is that a problem?design
No. The class design shares code; the table design suits storage and queries. They may differ as long as the mapping is clear: each derived class maps to its own table, including the inherited columns. ORMs support several mapping strategies for hierarchies.
Explain the object-relational mismatch, the table-per-concrete-type approach used here, and the alternatives (one shared table, or a table per type) with their trade-offs.
Polymorphism
FWhat is polymorphism?concept
One call behaving differently depending on the object's real type. Calling Describe() on a
Person variable runs the student, teacher, or guardian version, depending on which object it
holds.
FWhat do virtual and override do?concept
virtual in the base class allows derived classes to replace a method or property. override
in the derived class provides the replacement. base.Method() inside an override runs the base
version too.
FWhy would you override ToString?concept
By default it returns the class name, such as SchoolApp.Core.Entities.Student. Overriding it
gives a readable description, which interpolation, logging, and the debugger then use. Never put
private data such as passwords in it.
MWhat is the difference between method overloading and method overriding?concept
Overloading: same name, different parameters, usually in one class, chosen at compile time.
Overriding: same signature, new body in a derived class, needs virtual and override, chosen
at run time from the object's real type.
MWhat is the difference between override and new on a method?concept
override replaces the base method for every caller. new hides it: callers using a base-type
variable still get the base version, and only derived-type variables see the new one. Hiding is
almost always a mistake; the compiler warns with CS0114 when neither keyword is used.
MWhat is the Template Method pattern?concept
A non-virtual method defines a fixed process and calls virtual (or abstract) steps that derived
classes fill in. FeeNotice.Send always formats the same way, while Channel and Compose
differ per channel.
SA 300-line switch on payment mode must change for every new mode. How would you refactor it?scenario
Create a base class or interface for payment modes with the varying behavior as virtual or abstract members, and one class per mode. Choose the class in one place, then call it through the base type. A new mode becomes one new class, with no edits to existing logic.
Describe the steps (extract behavior, one class per case, single creation point), the benefit (open for extension, closed for modification), and safety (tests per mode before removing the switch).
SWhen is creating a subclass the wrong way to handle a difference?design
When only a value differs, such as a rate or a limit: use a property set in the constructor instead. Use subclasses for different behavior, and interfaces when behavior must be swapped or combined at run time.
Show judgment with examples, and mention the cost of unnecessary classes: more code, deeper hierarchies, and harder changes.
AHow does polymorphism help a large system grow without breaking existing features?design
New behavior is added as new classes behind an existing base type or interface, so tested code is not edited. Only the place that chooses the implementation changes. This keeps changes small, reviewable, and low-risk, and lets teams work on different implementations independently.
Connect it to the Open/Closed Principle, plug-in architectures, and dependency injection in ASP.NET Core, with an SMS example such as notification channels.
Abstract classes and interfaces
FWhat is an abstract class?concept
A class marked abstract that cannot be created with new, only inherited from. It can hold
shared code and abstract members without a body, which every derived class must override, such
as ReportCard.FormatScore.
See the example · Lesson 41: Abstract classes and interfaces · Permalink
FWhat is an interface?concept
A contract listing members a class promises to provide, such as INotificationSender with
Channel, CanReach, and Send. It has no constructors or instance fields; a class implements
it with : IName and must provide every member.
See the example · Lesson 41: Abstract classes and interfaces · Permalink
FCan a class implement more than one interface?concept
Yes, any number, while having only one base class. A Teacher inherits from Person and is
also an ISoftDeletable; a class could implement several capability interfaces at once.
See the example · Lesson 41: Abstract classes and interfaces · Permalink
MWhat is the difference between an abstract class and an interface, and when do you use each?concept
An abstract class can hold fields, constructors, and shared code, and a class can have only one; it models "is a" for related classes. An interface is a contract a class can implement alongside others; it models "can do". Use an abstract class for shared code, an interface for a capability with possibly unrelated implementations.
See the example · Lesson 41: Abstract classes and interfaces · Permalink
MWhat are default interface methods?concept
Since C# 8, an interface member can have a body that implementing classes inherit, such as
Describe() in INotificationSender. They are reachable only through the interface type, and
they let you add members to an interface without breaking existing implementations.
See the example · Lesson 41: Abstract classes and interfaces · Permalink
MWhat is abstraction in OOP?concept
Depending on what something does, not how it does it, and hiding the details. Notify knows
only that a sender can reach a guardian and send a message; email servers and phone gateways
stay hidden in the implementations.
See the example · Lesson 41: Abstract classes and interfaces · Permalink
SThe SMS calls a text-message provider directly from forty places, and the school wants to switch providers. What would you do?scenario
Introduce an interface such as INotificationSender, wrap the current provider in one class
that implements it, and change the forty call sites to use the interface. Then the new provider
is one new class plus one line of setup, and both can be tested with fakes.
Describe the refactoring steps safely (introduce the interface, migrate callers gradually, keep behavior identical), then the payoff: vendor independence and testability.
See the example · Lesson 41: Abstract classes and interfaces · Permalink
SShould every class have an interface?design
No. Add an interface when you need to swap implementations, fake a dependency in tests, or define a contract between teams or layers. An interface with one implementation and no such need only adds files and indirection.
Show judgment: name concrete reasons to add an interface and the cost of adding them everywhere. Mention that dependency injection in ASP.NET Core makes interfaces cheap where they are useful.
See the example · Lesson 41: Abstract classes and interfaces · Permalink
AHow do interfaces and abstract classes differ when a public contract must evolve over years?design
Adding an abstract member to either breaks every implementation. Adding a normal or virtual method to an abstract class, or a default method to an interface, does not. Design contracts small and stable, version them deliberately, and prefer adding new interfaces over changing widely used ones.
Discuss compatibility for libraries and APIs, the role of default interface methods, and strategies such as new interfaces, adapters, and versioning.
See the example · Lesson 41: Abstract classes and interfaces · Permalink
static, sealed, partial
FWhat is the difference between a static member and an instance member?concept
A static member belongs to the class and has one shared copy, called on the class name, such as
SchoolRules.GetGrade(92m). An instance member belongs to each object and is called on an
object, such as diya.GetAgeOn(date).
See the example · Lesson 42: static, sealed, partial · Permalink
FWhat is a static class?concept
A class that holds only static members and can never be instantiated (error CS0712). It suits
pure helpers and rules with no stored state, such as SchoolRules or Math.
See the example · Lesson 42: static, sealed, partial · Permalink
FWhat does sealed mean on a class?concept
No other class may inherit from it; trying fails with error CS0509. Seal concrete classes that
were not designed for extension, such as Student and Teacher.
See the example · Lesson 42: static, sealed, partial · Permalink
MWhat is the difference between const and static readonly?concept
const is fixed at compile time and allowed only for simple types such as numbers and strings;
other projects copy the value when they compile. static readonly is set once at run time and
works for any type, such as DateOnly, and callers always read the current value.
See the example · Lesson 42: static, sealed, partial · Permalink
MWhat are partial classes used for?concept
Splitting one class across several files that the compiler joins. The main use is generated code: a tool such as EF Core scaffolding writes one file, and your own code in another file survives regeneration.
See the example · Lesson 42: static, sealed, partial · Permalink
MWhen should you avoid making a class static?concept
When it has dependencies or behavior you may want to replace, such as sending messages or reading the database. Static code cannot be swapped through an interface or faked in tests. Keep static for pure, deterministic rules.
See the example · Lesson 42: static, sealed, partial · Permalink
STwo clerks received the same admission number on the first day of admissions. The number came from a static field. Explain and fix.scenario
A static field is shared by every request in the app and resets when the app restarts; with several copies of the API, each has its own counter. Concurrent requests and restarts produce duplicates. Move the counter to the database, such as a SQL SEQUENCE, protected by a unique constraint.
Explain the three failure modes (concurrency, restarts, multiple instances), the fix, and the safety net of a unique constraint. Mention reviewing every non-readonly static field.
See the example · Lesson 42: static, sealed, partial · Permalink
SShould classes be sealed by default?design
Many teams seal concrete classes by default and unseal deliberately. Sealing documents that a class was not designed for inheritance, prevents fragile overrides, and allows small performance gains. Leave classes open only when extension is part of the design.
Present both views: the safety of sealing versus flexibility for library users. Mention that
removing sealed later is safe, while adding it later breaks existing subclasses.
See the example · Lesson 42: static, sealed, partial · Permalink
AWhere should shared, changing state live in a system that runs several copies of a web API?design
Never in static fields or memory of one instance. Durable shared state belongs in the database (sequences, rows with constraints) or a shared store such as a distributed cache; per-request state lives in objects created for that request. Static members hold only constants and pure logic.
Discuss scale-out, restarts, and concurrency, then the right home for each kind of state. Mention that this is why ASP.NET Core services are registered with scoped or transient lifetimes.
See the example · Lesson 42: static, sealed, partial · Permalink
Enums, structs, records
FWhat is an enum, and why use one?concept
A type with a fixed list of named values, such as InvoiceStatus { Unpaid, PartiallyPaid, Paid, Cancelled }. It replaces magic strings: invalid values and typos fail the build, and
editors list the valid options.
See the example · Lesson 43: Enums, structs, records · Permalink
FWhat is a record in C#?concept
A type, written in one line such as record MarkEntry(int EnrollmentId, int ExamSubjectId, decimal? MarksObtained), with init-only properties, value equality, a readable ToString(),
and with expressions for copies with changes.
See the example · Lesson 43: Enums, structs, records · Permalink
FHow do you convert an enum to and from a string?coding
status.ToString() returns the member name, such as "PartiallyPaid". Enum.Parse<InvoiceStatus>("Paid")
converts text back, and Enum.TryParse does it safely, returning false for unknown text.
See the example · Lesson 43: Enums, structs, records · Permalink
MWhat is the difference between a struct and a class?concept
A struct is a value type: assignment copies the whole value, it cannot be null (unless nullable), and it cannot inherit. A class is a reference type: assignment copies a reference to one shared object. Use structs for small immutable values, classes for entities.
See the example · Lesson 43: Enums, structs, records · Permalink
MWhat is the default value of an enum, and why can it be a problem?concept
0, whether or not a member has that value. If no member is 0, an unset enum field holds an
undefined value, as with Gender. Make 0 a safe default, or require the value in the constructor,
and use Enum.IsDefined for values from outside.
See the example · Lesson 43: Enums, structs, records · Permalink
MWhat does a with expression do?coding
It creates a copy of a record (or struct) with some properties changed, leaving the original
untouched: entered with { MarksObtained = 90m }. It is how you "change" immutable data.
See the example · Lesson 43: Enums, structs, records · Permalink
SA report shows zero 'partial' invoices because C# used the string 'Partial' and the database stores 'PartiallyPaid'. How do you prevent this class of bug?scenario
Replace status strings with an enum whose member names match the database CHECK constraint
exactly, and convert with ToString() and Enum.Parse. Typos then fail the build, and a test can
check that every enum name is allowed by the constraint.
Explain the root cause (unchecked strings), the fix (enums matching the schema), and an automated guard (a test comparing enum names with the CHECK constraint values).
See the example · Lesson 43: Enums, structs, records · Permalink
SShould entities like Student be records?design
Usually not. Entities have identity: two students with identical names and birth dates are still different people, distinguished by Id, and they change over time. Records compare by value and are meant to stay fixed, which suits DTOs, messages, and value objects.
Contrast identity equality with value equality, and mutability with immutability. Mention that ORMs track entities by key, which value equality can confuse.
See the example · Lesson 43: Enums, structs, records · Permalink
AHow would you choose between class, struct, record, and record struct across a codebase?design
Classes for entities with identity and behavior; records for data passed between layers and compared by value; readonly record structs for tiny, frequently created values; enums for fixed option lists. Document the convention so the whole team chooses the same way.
Give the decision criteria (identity, mutability, size, equality semantics) and the costs of wrong choices, such as large struct copies or entities compared by value.
See the example · Lesson 43: Enums, structs, records · Permalink
Model classes
FWhat is an entity (model) class?concept
A class that represents one row of a database table, with one property per column, such as
Mark for the Marks table. Data access code reads rows into entities and saves entities back
as rows.
See the example · Lesson 44: Model classes for every table · Permalink
FWhat does the required keyword do on a property?concept
Introduced in C# 11, it forces the property to be set in the object initializer when the object
is created; otherwise the build fails with CS9035. It suits entities without constructors, such
as Subject.Name.
See the example · Lesson 44: Model classes for every table · Permalink
FHow do you represent a column that allows NULL in an entity class?concept
Add ? to the type: decimal? for MarksObtained, string? for Remarks, DateOnly? for a
return date. NOT NULL columns use the plain type, so the class tells callers exactly what may be missing.
See the example · Lesson 44: Model classes for every table · Permalink
MWhat is the difference between a foreign key property and a navigation property?concept
The foreign key property, such as StudentId, holds the stored Id and is always present. The
navigation property, such as Student, holds the linked object and is filled only if the code
loaded it, so it is nullable.
See the example · Lesson 44: Model classes for every table · Permalink
MHow do you avoid repeating audit columns in many entity classes?design
Put them in an abstract base class, such as AuditableEntity with CreatedAt, CreatedBy,
UpdatedAt, and UpdatedBy, and a SoftDeletableEntity for soft-delete columns. Entities
inherit what their table has.
See the example · Lesson 44: Model classes for every table · Permalink
MHow are junction tables such as StudentGuardians modeled in C#?concept
As a class with the two foreign keys, which together form the key, plus any data about the link,
such as Relationship and IsPrimaryContact, and optional navigation properties to both sides.
There is no separate Id.
See the example · Lesson 44: Model classes for every table · Permalink
SDiscounts of 2.50 are saved as 2. The column is DECIMAL(10,2). What do you check, and how do you prevent it?scenario
The entity property type: it is probably int, which drops the fraction. Change it to decimal,
fix affected data, and add an automated test that compares every entity's property types and
nullability with the database columns, so a mismatch fails the build.
Trace the value through the layers to find where the fraction is lost, then fix code, data, and process. Mention schema-comparison tests or scaffolding as long-term protection.
See the example · Lesson 44: Model classes for every table · Permalink
SRich entities or plain (anemic) entities: which do you prefer?design
A mix. Put rules inside entities where breaking them is expensive, such as payments and identity, so invalid objects cannot exist. Keep simple lookup tables plain, with rules in services. Fully anemic models scatter rules; fully rich models can fight data access tools.
Define both styles, give their trade-offs, and show judgment per entity. Mention that EF Core supports private setters and constructors, which makes rich entities practical.
See the example · Lesson 44: Model classes for every table · Permalink
AHand-written entities or entities generated from the database: what would you choose for a large system?design
For a database-first system with many tables, generate (scaffold) them so they always match the schema, and keep custom code in partial classes. Hand-write them when the domain model must differ from the tables. Either way, verify them automatically against the schema.
Compare effort, accuracy, and flexibility. Mention partial classes for custom code, regeneration on schema changes, and code-first migrations as the alternative where C# owns the schema.
See the example · Lesson 44: Model classes for every table · Permalink
DTOs and mapping
FWhat is a DTO?concept
A data transfer object: a simple class or record that carries exactly the data one API call or
screen needs, such as StudentDto. It has no business rules and no database-only fields.
FWhy should an API not return entity classes directly?concept
Entities mirror the database and expose everything: password hashes, audit columns, soft-delete flags, and any column added later. DTOs send only what the client needs, so internal data cannot leak and database changes do not break clients.
FWhat is an extension method?concept
A static method in a static class whose first parameter has this, such as
ToDto(this Student student). It can be called like an instance method, student.ToDto(),
without changing the original class. Its namespace must be imported with using.
MWhy use separate DTOs for responses, create requests, and update requests?design
Each direction needs different fields: a response has an Id and readable values; a create request has no Id; an update request contains only fields that may change. One shared DTO would accept or expose fields it should not.
MWhat is over-posting (mass assignment), and how do DTOs prevent it?concept
A client adds extra fields to a request, such as a status or role, and the server saves them because it bound the request to an entity. Request DTOs contain only the allowed fields, so anything else in the JSON is ignored.
MWhat is flattening in mapping?concept
Building one simple DTO from values spread across linked entities, such as an invoice summary
that includes the student's name from Enrollment.Student. Clients get a small, ready-to-show
shape without knowing how tables are joined.
SA security review finds the profile endpoint returns the password hash. How do you fix it and prevent it across the codebase?scenario
Introduce a DTO with only the needed fields and map the entity to it before returning. Check all other endpoints for returned entities, add a review rule or analyzer that forbids entities in controller responses, and consider rotating credentials if hashes were exposed.
Cover the immediate fix, the audit of similar endpoints, prevention (rules, tests that inspect response shapes), and the incident response, such as assessing exposure.
SHand-written mapping or a mapping library such as AutoMapper?design
Hand-written mapping is explicit, compiler-checked, and easy to debug; renamed properties break the build. Libraries reduce repetitive code for many similar mappings but can silently skip properties. Choose per project size, and test mappings either way.
Weigh explicitness and safety against less code. Mention configuration validation in libraries and source-generated mappers as a middle ground.
AHow do DTOs help an API evolve without breaking mobile apps?design
The DTO is the public contract; the database and entities can change behind it while mapping adapts. Breaking changes to the DTO are made deliberately, with versioning, so apps already on phones keep working.
Explain contract stability, additive versus breaking changes, and API versioning strategies, with an example such as renaming a database column without changing the JSON.
Advanced C#
Collections
FWhat is the difference between an array and a List<T>?concept
An array has a fixed size set when it is created. A List<T> grows and shrinks with Add and
Remove, using an array inside that it replaces with a bigger one when full. Use arrays for fixed
data and lists for data that changes.
FWhen would you use a Dictionary<TKey, TValue>?concept
When you look values up by a unique key, such as students by admission number. Lookup time stays about the same however many items it holds, unlike searching a list.
FWhat is the difference between a Queue and a Stack?concept
A Queue<T> is first in, first out: Enqueue adds at the back, Dequeue takes from the front,
like a fee counter line. A Stack<T> is last in, first out: Push and Pop work on the top,
like undo.
MWhy is looking up by key in a Dictionary faster than searching a List?concept
A list search checks items one by one, so on average half the list per search. A dictionary hashes the key to jump almost directly to its bucket, taking about constant time. In the SMS, finding 1,006 students took about 24 ms with a list and 0.1 ms with a dictionary.
MWhat is the difference between dict[key] and dict.TryGetValue(key, out value)?coding
The indexer throws KeyNotFoundException when the key is missing. TryGetValue returns false
instead and gives the value through out when found. Use TryGetValue whenever the key may be absent.
MWhat does 'Collection was modified; enumeration operation may not execute' mean?concept
The list was changed, for example by Remove, while a foreach was looping over it. Collect the
items to change and apply the changes after the loop, or loop backwards with a for loop.
SAn end-of-term report takes minutes. It loops over students and, for each, searches a list of all marks. How do you fix it?scenario
The nested search does students times marks comparisons. Group the marks into a Dictionary
keyed by student once, then look each student up in constant time. The work drops from
millions of comparisons to one pass plus one lookup per student.
Identify the nested loop as the cause, explain the complexity in plain terms, and show the dictionary fix with a measurement before and after.
SHow should a class expose a collection it owns?design
Keep the real list private and expose it as IReadOnlyList<T> or IReadOnlyCollection<T>, so
callers can read but not add or remove. Changes go through the class's own methods, which keep
its rules. Initialise collections to empty, never null.
Connect it to encapsulation, show the property pattern, and mention that returning the mutable list lets callers bypass validation.
AHow do you choose the right collection for a feature?design
Start from how the data is used: in order and by position (List), by key (Dictionary), uniqueness checks (HashSet), arrival order (Queue), last-first (Stack), and whether many threads share it (concurrent collections). Measure with realistic data volumes before optimizing further.
Give a decision approach based on access patterns and scale, with SMS examples, and mention thread safety and memory as further factors.
Generics
FWhat are generics in C#?concept
Classes, records, interfaces, and methods written with a type parameter, such as T, that the
caller fills in. List<Student> and PagedResult<StudentDto> reuse one definition for many types,
with full compile-time type checking.
FWhat is a generic method?concept
A method with its own type parameter, written after its name, such as
ToPage<T>(List<T> all, int page, int pageSize). The compiler usually infers T from the
arguments, so callers rarely write it.
FWhy are generic collections better than ArrayList?concept
ArrayList holds object, so wrong types are accepted and fail later with
InvalidCastException, and value types are boxed. List<decimal> rejects a string at compile time
and stores decimals directly, which is safer and faster.
MWhat are generic constraints? Give examples.concept
Rules on what a type parameter may be, written with where: where T : ISoftDeletable,
where T : class, where T : struct, where T : new(). They let the generic code use the
promised members, and the compiler rejects types that do not qualify (CS0311).
MWhat is boxing, and how do generics avoid it?concept
Boxing wraps a value type, such as a decimal, in an object on the heap so it can be stored as
object; unboxing casts it back. It costs memory and time. Generic collections store the value
type directly, so no boxing happens.
MWhy can't a List<Student> be assigned to a List<Person>, while IEnumerable<Person> works?concept
Through a List<Person>, code could add a Teacher into a list of students, so C# forbids it
(CS0029). IEnumerable<T> only reads, so treating students as persons is safe; this is
covariance, marked by out T on the interface.
SFour teams each built their own paging response for the API, and the mobile app struggles. What do you do?scenario
Define one generic PagedResult<T> in the shared library with agreed names, 1-based pages, and
a maximum page size, and migrate endpoints to it gradually, keeping old shapes until clients
move. Clients then need one paging routine.
Cover the shared contract, the rules it enforces (page size limits), and a safe migration plan with versioning or a transition period for existing clients.
SWhat are the pros and cons of a generic repository, IRepository<T>?design
Pros: less repetitive code and a uniform interface. Cons: every entity gets the same methods, including Delete where rules forbid it; special queries are awkward; and it often duplicates what an ORM already provides. Specific repositories, possibly sharing a small generic base, are a common choice.
Present both sides fairly, tie the decision to business rules (forbidden deletes) and query needs, and mention that EF Core's DbSet already acts as a generic repository.
AHow would you design shared generic building blocks for a large API without over-generalizing?design
Generalize proven, repeated shapes, such as paging, result wrappers, and soft-delete filters, with minimal constraints, in a shared library. Keep domain logic specific. Make something generic only when a second real use appears, and document the conventions so teams use them consistently.
Show judgment: name good candidates and bad ones (generic services hiding business rules), discuss constraints and naming, and the cost of changing widely used generic types.
IEnumerable and IQueryable
FWhat is IEnumerable<T>?concept
The interface for anything you can walk through one item at a time. It provides an enumerator,
which foreach uses. Arrays, lists, dictionaries, and LINQ queries all implement it.
See the example · Lesson 49: IEnumerable vs IQueryable · Permalink
FWhat does yield return do?concept
It hands back one item from a method that returns IEnumerable<T>, then pauses the method until
the caller asks for the next item. Data is streamed: reading the marks file with Take(3) read
only 71 of 12,012 lines.
See the example · Lesson 49: IEnumerable vs IQueryable · Permalink
FWhat is deferred execution in LINQ?concept
Building a query with methods like Where does not run it; it creates a plan. The plan runs only
when results are requested, by foreach, Count(), First(), ToList(), and similar calls.
See the example · Lesson 49: IEnumerable vs IQueryable · Permalink
MWhat is the difference between IEnumerable<T> and IQueryable<T>?concept
An IEnumerable query holds compiled code and runs in your application's memory. An
IQueryable query holds an expression tree that a provider translates, for example EF Core
into SQL, so filtering happens in the database before rows are sent.
See the example · Lesson 49: IEnumerable vs IQueryable · Permalink
MWhat is multiple enumeration, and how do you avoid it?concept
Enumerating the same deferred query more than once repeats all its work each time; two Count()
calls read the marks file twice. If the results are needed several times, call ToList() once and
reuse the list.
See the example · Lesson 49: IEnumerable vs IQueryable · Permalink
MWhy prefer Any() over Count() > 0?coding
Any() stops at the first matching item; Count() must examine every item. Against a database,
Any() becomes an EXISTS check while Count() counts every matching row.
See the example · Lesson 49: IEnumerable vs IQueryable · Permalink
SA dashboard calls context.AttendanceRecords.ToList() and then filters today's records in C#. It is slow and uses a lot of memory. Why, and what is the fix?scenario
ToList() runs the query immediately, so every attendance row is loaded into memory before
filtering. Apply Where while the query is still IQueryable, then ToList(), so SQL Server
returns only today's rows.
Explain where the query runs before and after the fix, quantify the waste (all rows versus a few hundred), and mention checking the generated SQL or logs to confirm.
See the example · Lesson 49: IEnumerable vs IQueryable · Permalink
SShould a repository return IQueryable<T>?design
Usually not. Returning IQueryable lets callers compose any query against the database, spreading
data access logic and making performance unpredictable. Return materialized results such as
IReadOnlyList<T>, and add specific methods for the queries you need.
Present the trade-off: flexibility versus control and testability. Mention cases where exposing IQueryable is deliberate, such as OData or internal query builders.
See the example · Lesson 49: IEnumerable vs IQueryable · Permalink
AHow would you process millions of rows in .NET without running out of memory?design
Stream instead of materializing: read rows one at a time with an iterator, a data reader, or
IAsyncEnumerable<T>, process each, and keep only aggregates. Filter and aggregate in the database
where possible, and batch writes.
Combine database-side work, streaming in the application, batching, and measuring memory. Mention that ToList on huge results is the usual cause of memory spikes.
See the example · Lesson 49: IEnumerable vs IQueryable · Permalink
LINQ
FWhat is LINQ?concept
Language Integrated Query: a set of C# methods and syntax for querying any collection, such as
Where, Select, OrderBy, and GroupBy. The same queries work on lists, files, and, through EF
Core, databases.
FWhat is the difference between Where and Select?concept
Where filters: it keeps items that match a condition. Select projects: it turns each item into
a new shape, such as a name and a mark. Filter first, then shape.
FWhat is the difference between First, FirstOrDefault, and Single?concept
First returns the first match and throws if there is none. FirstOrDefault returns default
(null for classes) instead of throwing. Single expects exactly one match and throws for none or
for more than one.
MHow does LINQ's Average treat null values?concept
For nullable values, Average, Max, and Min skip nulls, like SQL's AVG. The Class 10
Mathematics average of 60.23 ignored 20 absent students, matching SQL Server exactly.
MHow does GroupBy work in LINQ?coding
GroupBy(m => m.Subject) returns groups; each has a Key (the subject) and is itself the sequence
of items with that key, so g.Average(...) and g.Count() work per group, like SQL's GROUP BY.
MWhat is the difference between Select and SelectMany?concept
Select returns one result per item, so a list of sections with enrollments becomes a list of
lists. SelectMany flattens those inner lists into one sequence: 22 sections become 1,006
enrollments.
SA results screen shows a different rank-1 student on every refresh. What is wrong?scenario
Several students share the top mark and the sort has no tie-breaker, so their order is not fixed.
Add ThenBy on a stable key, and agree a ranking rule with the school, such as equal marks
sharing a rank (DENSE_RANK).
Explain non-deterministic ordering, the technical fix, and the business question behind it. Mention the same issue in SQL ORDER BY without a unique key.
SQuery syntax or method syntax: which do you use?concept
Both compile to the same calls. Method syntax is shorter for simple chains and offers every operator; query syntax reads more easily for joins and multiple sources. Many teams default to method syntax and use query syntax for complex joins.
Show you can read and write both, then state a consistent team preference and its reason.
AWhen does LINQ hurt performance, and how do you prevent it in a team?design
Through searches inside loops instead of dictionaries, repeated enumeration of deferred queries, materializing database queries too early, and sorting before filtering. Prevent it with code review rules, analyzers for multiple enumeration, logging of generated SQL, and measuring hot paths.
Give concrete anti-patterns with fixes, then the team-level practices. Emphasize measuring before replacing readable LINQ with hand-written loops.
Delegates and events
FWhat is a delegate?concept
A type-safe reference to a method, stored in a variable and passed around like a value. Func,
Action, and EventHandler<T> are built-in delegate types; LINQ's Where takes a delegate.
See the example · Lesson 51: Delegates and events · Permalink
FWhat is the difference between Func and Action?concept
Func returns a value; its last type parameter is the return type, as in Func<decimal, string>.
Action returns nothing, as in Action<string>.
See the example · Lesson 51: Delegates and events · Permalink
FWhat is a lambda expression?concept
An inline, unnamed method, such as m => m.MarksObtained is null. It is converted into a delegate
(or an expression tree for IQueryable), with parameter types inferred from where it is used.
See the example · Lesson 51: Delegates and events · Permalink
MWhat is an event, and how does it differ from a public delegate field?concept
An event is a delegate wrapped so that outside code can only subscribe (+=) and unsubscribe
(-=); only the owning class can raise it. A public delegate field could be overwritten or invoked
by anyone, faking notifications (CS0079 prevents this for events).
See the example · Lesson 51: Delegates and events · Permalink
MWhat is a closure?concept
A lambda that captures variables from its surrounding scope. It captures the variable itself, not a copy, so changes are shared and the captured objects stay alive as long as the lambda does.
See the example · Lesson 51: Delegates and events · Permalink
MWhy raise an event with Event?.Invoke(...), and why can't you unsubscribe an inline lambda?coding
With no subscribers the event is null, so ?. avoids a NullReferenceException. Each inline lambda
creates a new delegate, so -= with another identical-looking lambda removes nothing; keep a
reference or use a named method.
See the example · Lesson 51: Delegates and events · Permalink
SRecording a payment calls six features directly, and a bug in any one breaks payments. How would you redesign it?scenario
Have the payment raise one "payment recorded" event, or publish a message, after the payment is saved, and let each feature subscribe independently. Failures in optional reactions should not undo or block the payment; isolate them and log errors.
Connect to Single Responsibility and Open/Closed, discuss error isolation, and mention that across services this becomes messaging with background processing.
See the example · Lesson 51: Delegates and events · Permalink
SWhen would you use an event, an interface, or a Func parameter?design
An event for optional listeners that come and go; an interface for a required collaborator with
several methods, injected through the constructor; a Func parameter for a single rule or formula
chosen by the caller.
Give one SMS example for each and the trade-offs: discoverability, testability, and how failures propagate.
See the example · Lesson 51: Delegates and events · Permalink
AHow do in-process events relate to messaging in a distributed system?design
Both announce that something happened and let others react. In-process events run synchronously in the same program and are lost if it crashes. Messaging (queues, an outbox) is durable, crosses services, retries on failure, and runs asynchronously, at the cost of eventual consistency.
Compare reliability, coupling, and timing, then explain when to promote an event to a message, for example a payment notification that must not be lost.
See the example · Lesson 51: Delegates and events · Permalink
Modern C# features
FWhat is a collection expression?concept
C# 12 syntax for creating collections with square brackets: string[] subjects = ["English", "Science"];. It works for arrays, lists, and other collection types, and .. spreads another
collection's items in.
FWhat is a raw string literal, and when is it useful?concept
Text between three double quotes ("""), where quotes and backslashes need no escaping and the
closing quotes' indentation is removed. It suits SQL, JSON, and other text with quotes; values still
belong in SQL parameters.
FWhat does the C# 14 field keyword do?concept
Inside a property accessor, field refers to the property's compiler-made backing field, so a
property with a custom setter, such as trimming or validation, no longer needs a hand-written
private field.
MWhat are list patterns?concept
Patterns that match the shape of a sequence: ["Absent", ..] (first item is Absent),
[.., "Absent", "Absent"] (last two), [_, _, _] (exactly three items). _ matches one item and
.. any number of items.
MHow do primary constructors on classes differ from records?concept
Both declare parameters after the type name. A record turns them into public init-only properties with value equality. A class with a primary constructor only makes the parameters available inside the class body; they are not properties, and they are not read-only.
MWhat are C# 14 extension members?concept
An extension(Type receiver) { ... } block inside a static class that can declare extension
properties as well as methods, such as student.Initials. Before C# 14, only extension methods
were possible.
SA developer converted half the codebase to new C# syntax in one pull request. How do you handle it?scenario
Ask to split it: a large mechanical change hides real changes and is hard to review. Agree on which features the team adopts, apply them in new and changed code, and convert old code gradually or with automated, separately reviewed refactorings.
Balance modernization against reviewability and risk. Mention coding standards, analyzers or editorconfig rules, and keeping behavior changes separate from style changes.
SShould a switch over an enum end with a default arm? What are the trade-offs?design
Without _, the compiler warns about missing statuses (CS8509) and unnamed values (CS8524), which
helps when new statuses are added. A quiet default hides new statuses. A common choice is to list
every status and end with _ => throw, so unexpected values fail loudly.
Explain both warnings, the risk of silent defaults, and the throwing default as a middle path.
AHow would you keep a large codebase current with new .NET and C# versions?design
Upgrade to each LTS release on a plan, update the target framework and packages, fix analyzer warnings, and adopt new language features through agreed coding standards in new and changed code. Automated tests make upgrades safe, and editorconfig or analyzers keep the style consistent.
Cover the upgrade cadence (LTS), risk control (tests, staged rollout), and team practices for adopting features. Mention support end dates as the business driver.
async and await
FWhat do async and await do?concept
async marks a method that can use await and returns Task or Task<T>. await pauses the
method until a task completes, giving the thread back in the meantime, and continues from the same
line afterwards.
FWhat is the difference between Task and Task<T>?concept
Both represent work that finishes later. Task has no result, like writing a log file;
Task<T> produces a value of type T, like Task<string> for a send status.
FDoes async make code run faster?concept
Not by itself. It makes waiting for I/O cheaper by freeing threads, which improves scalability and lets independent waits overlap. CPU-bound calculations need parallelism instead.
MHow do you run several independent async operations together?coding
Start them without awaiting, collect the tasks, and await Task.WhenAll(tasks). Ten 250 ms sends
took about 260 ms together instead of 2.6 seconds one by one. Results come back in the same order.
MWhat is a CancellationToken, and why pass it through async methods?concept
A token that signals that work should stop, created by a CancellationTokenSource, for example with
a time limit. Passing it down to every async call lets the whole operation stop early; ASP.NET Core
cancels it when the client disconnects.
MWhy should you avoid async void?concept
An async void method cannot be awaited, so callers cannot know when it ends, and exceptions it
throws cannot be caught by the caller and can crash the process. Return Task instead; use
async void only for event handlers.
SA web portal stops responding under load, but the CPU is nearly idle. The code uses .Result on database calls. What is happening?scenario
Thread pool starvation: each request blocks a thread while waiting for the database, so the pool
runs out and new requests queue. Make the call chain async all the way with await, so threads are
released during waits.
Explain the symptom (low CPU, high latency), the cause (blocked threads), the fix, and how to confirm it with thread pool counters or dotnet-counters.
SHow are exceptions handled with Task.WhenAll?concept
WhenAll waits for all tasks to finish, then await rethrows the first exception; the other
tasks still complete. Inspect each task's status or the aggregate Exception to see all failures,
and design batches so one failure does not stop the rest.
Show awareness that only the first exception surfaces through await, and how to collect all errors for reporting in batch jobs.
AHow would you send fee reminders to 10,000 families reliably and quickly?design
Send asynchronously with limited concurrency (for example batches or a semaphore) so the provider is not overwhelmed, with timeouts and cancellation, retries for temporary failures, and a record of what was sent so a restart does not send twice. Run it as a background job, not inside a web request.
Cover throughput (bounded concurrency), reliability (retries, idempotency, persistence), provider limits, and observability. Mention background workers in Module 12.
Threading
FWhat is the difference between a thread and a task?concept
A thread is a path of execution managed by the operating system. A task is a unit of work that
.NET schedules, usually on a reusable thread-pool thread. Modern code uses tasks, Parallel, and
async instead of creating threads by hand.
FWhat is a race condition?concept
A bug where the result depends on the timing of threads that share data. Two threads doing
count++ can both read the same value and write the same result, losing an update; one million
increments gave about 62,000.
FWhat does Parallel.ForEach do?concept
It processes the items of a collection on several thread-pool threads at once, using multiple cores. It suits CPU-bound, independent work, such as building 1,006 report cards about three times faster on 16 cores.
MWhen would you use Interlocked instead of lock?concept
Interlocked performs a single update on one number, such as increment or add for int and
long, as one uninterruptible step, without blocking. Use lock when several fields change
together, for types like decimal, or for collections.
MWhy might parallel code be much less than N times faster on N cores?concept
Splitting and combining work costs time, cores share memory bandwidth, locks make threads wait, and parts of the program run on one thread only (Amdahl's law). Very small work items can even be slower in parallel.
MIs Dictionary thread-safe? What do you use instead?concept
No: adding from several threads at once can lose items or corrupt it. Use ConcurrentDictionary
(or other System.Collections.Concurrent types), or let each thread build its own results and
combine them afterwards.
SA parallel term-end report gives a slightly different fee total each run. What is wrong, and how do you fix it?scenario
Threads add to one shared total without protection, so updates are lost in a race. Fix it by computing independent partial totals per section or thread and adding them at the end, or by protecting the shared total with a lock. Then test repeatedly with production-sized data.
Recognise the symptom of nondeterminism, name the race, give the share-nothing fix first and the lock as an alternative, and mention why such bugs pass normal tests.
SWhat is a deadlock with locks, and how do you avoid one?concept
Two threads each hold one lock and wait for the lock the other holds, so both wait forever. Avoid it by taking locks in a fixed order, keeping locked sections small, avoiding nested locks, and never awaiting or calling unknown code while holding a lock.
Explain the circular wait, prevention rules, and diagnosis with a debugger or dump showing thread stacks. Relate it to the SQL deadlocks of Lesson 25.
AHow do you design a web application so that concurrent requests do not corrupt shared state?design
Keep request handling stateless: per-request objects, no mutable static fields, data in the database with transactions and constraints. Where in-memory sharing is needed, such as caches, use thread-safe types and immutable data, and keep heavy CPU work in background jobs.
Cover the request model (many threads), where state should live, synchronization choices, and service lifetimes in dependency injection (singleton services must be thread-safe).
Memory and GC
FWhat is the difference between the stack and the heap?concept
The stack holds method-local value-type variables and references, freed automatically when a method returns. The heap holds objects such as class instances, arrays, and strings, freed by the garbage collector when nothing references them.
FWhat does the garbage collector do?concept
It periodically finds objects that running code can no longer reach and reclaims their memory, then compacts the surviving objects. Developers do not free managed memory by hand.
FWhat is IDisposable, and what does using do?concept
IDisposable has one method, Dispose, which releases resources the GC does not manage, such as
files and connections. using calls Dispose automatically at the end of a block, even if an
exception is thrown.
MWhat are GC generations?concept
Generation 0 holds new objects and is collected often; survivors move to generation 1, then to generation 2, which is collected rarely. Most objects die young, so this makes collection cheap. Objects of 85,000 bytes or more start on the large object heap, counted as generation 2.
MWhat is the difference between Dispose and a finalizer?concept
Dispose releases resources at a known moment, called by your code or using. A finalizer runs
when the GC collects the object, at an unknown time, on a special thread, and delays collection.
Prefer Dispose; modern code rarely writes finalizers because SafeHandle covers them.
MHow can events cause memory leaks?concept
An event holds delegates, and each delegate references its subscriber. If a long-lived object's
event keeps a short-lived subscriber, the subscriber cannot be collected until it unsubscribes.
Unsubscribe with -=, often in the subscriber's Dispose.
SAn API's memory grows every day until it needs a restart. How do you investigate?scenario
Watch memory and GC counters over time, take memory snapshots at two points, and compare which object types grow and what keeps them alive. Typical culprits are event subscriptions on long-lived services, static collections, unbounded caches, and undisposed resources.
Describe a measured approach (dotnet-counters, dumps or Visual Studio snapshots, retention paths), the common causes, and verifying the fix under load.
SShould you call GC.Collect() in application code?concept
Almost never. The GC tunes its own timing, and forcing collections usually costs performance by promoting objects and pausing the program. It is useful in experiments and tests that measure memory, as in this lesson.
Explain the cost, the rare legitimate cases, and what to do instead: fix the cause of memory growth.
AHow do you design a high-traffic .NET service to be memory-efficient?design
Stream large data instead of loading it, avoid many short-lived large objects, reuse buffers (pooling), bound every cache, dispose resources promptly, avoid long-lived references to per-request objects, and monitor GC metrics in production.
Cover allocation patterns, lifetimes, pooling, caching limits, and observability, with an example such as exporting attendance for a whole school.
Data Access
ADO.NET
FWhat is ADO.NET, and what are its main classes for SQL Server?concept
ADO.NET is the low-level part of .NET for talking to databases. For SQL Server, the main classes
come from Microsoft.Data.SqlClient: SqlConnection (the connection), SqlCommand (SQL text or a
procedure name), SqlParameter (a value), SqlDataReader (rows, forward only), SqlTransaction,
and SqlException. Dapper and EF Core are built on top of it.
FWhat is the difference between ExecuteScalar, ExecuteReader, and ExecuteNonQuery?concept
ExecuteScalar returns one value, the first column of the first row, such as a COUNT(*).
ExecuteReader returns a reader over many rows. ExecuteNonQuery runs INSERT, UPDATE, or DELETE
and returns the number of rows changed. Each has an Async version that should be preferred.
FWhat is SQL injection, and how do you prevent it in C#?concept
SQL injection is user input changing what a query does, because the input was joined into the SQL
text. Typing x' OR '1'='1 into an unsafe student search returned all 10,012 students. Prevent it
with parameters: the value travels separately from the SQL, so it is only ever a value.
MWhat is connection pooling, and how should code use connections because of it?concept
ADO.NET keeps a pool of open connections per connection string. Disposing a connection returns it to the pool, and the next open takes it back almost instantly: 100 opens took 3 ms after a first open of 472 ms. So open as late as possible, dispose as early as possible, and never keep a connection in a field.
MHow do you read a nullable column with a SqlDataReader?coding
A NULL column arrives as DBNull.Value, not null, and calling GetString or GetInt32 on it
throws. Check reader.IsDBNull(ordinal) first, then read the value, for example
Address = reader.IsDBNull(i) ? null : reader.GetString(i).
MHow do you call a stored procedure with an output parameter from ADO.NET?coding
Set CommandType = CommandType.StoredProcedure and use the procedure name as the command text.
Add input parameters normally, and add the output parameter with
Direction = ParameterDirection.Output and a size. After ExecuteNonQueryAsync, read its Value,
as with @ReceiptNumber from usp_RecordFeePayment.
SWhy can AddWithValue slow a query down, and what do you use instead?concept
AddWithValue guesses the SQL type from the C# value, and a string becomes NVARCHAR. Against a
VARCHAR column, SQL Server converts the column and can scan instead of seek: finding a student by
admission number read 37 pages instead of 2. Use Parameters.Add with an explicit SqlDbType and
size that match the column.
Explain the type guess, the implicit conversion on the column side, and its effect on index use. Give the fix, and mention that plan-cache bloat from varying string lengths is a second reason.
SA school portal becomes slow at 9 a.m., then fails with 'Timeout expired... obtaining a connection from the pool'. What do you check?scenario
The pool is exhausted: connections are being opened and not returned. Look for connections, readers,
or commands that are not disposed, especially on exception paths, and for connections held during
slow work. Fix with await using everywhere, short-lived connections, and async calls; only then
consider raising Max Pool Size.
Name the cause (pool exhaustion), how to confirm it (counters or sp_who for sleeping connections from the app), the code fixes, and why raising the pool size first only hides the leak.
AWhen would you still choose plain ADO.NET over Dapper or EF Core in a new system?design
When you need full control or maximum throughput with no mapping layer: bulk loading with
SqlBulkCopy, streaming huge result sets, table-valued parameters, or a small hot path measured
as too slow. For ordinary CRUD, the hand-written mapping costs more in time and bugs than it saves,
so most teams use EF Core or Dapper and drop to ADO.NET only where measured.
Show you know the trade-off: control and speed against code volume and mistakes. Give concrete cases, and say how you would keep such code isolated behind a repository.
Dapper
FWhat is Dapper, and how is it different from EF Core?concept
Dapper is a micro-ORM: you write the SQL, and it adds parameters and maps result rows to objects. EF Core is a full ORM: it also writes the SQL from LINQ, tracks changes to entities, and saves them. Dapper gives more control with less magic; EF Core gives more productivity for everyday CRUD.
FWhich Dapper methods would you use to get a list, a single row, and a count?coding
QueryAsync<T> for a list, QuerySingleOrDefaultAsync<T> for one row by a unique key (null if
none, an exception if two), and ExecuteScalarAsync<int> for a count. ExecuteAsync runs INSERT,
UPDATE, DELETE, or a procedure and returns the rows changed.
FHow do you pass parameters to a Dapper query, and is it safe from SQL injection?concept
Pass an object, often anonymous: new { LastName = lastName } fills @LastName. Dapper creates
real parameters, so the values never become part of the SQL text and injection is not possible.
Joining user input into the SQL string yourself is still unsafe.
MHow does Dapper match columns to properties, and what happens when a name does not match?concept
By name, ignoring case. A column with no matching property is ignored, and a property with no column keeps its default value; neither raises an error. That makes renamed columns or missing aliases a silent bug, so use explicit column lists with aliases and test each query.
MWhat is multi-mapping in Dapper, and what does splitOn do?coding
Multi-mapping reads one joined row into several objects, for example an Enrollment and its
Student, and a function you supply connects them. splitOn names the column where each next
object starts; it defaults to Id. For one-to-many results, a dictionary keeps one parent per key.
MHow do you read an output parameter from a stored procedure with Dapper?coding
Use DynamicParameters: add the inputs, and add the output with a type, a size, and
direction: ParameterDirection.Output. Call ExecuteAsync with
commandType: CommandType.StoredProcedure, then read it with Get<string>("@ReceiptNumber").
SYour Dapper search by admission number is slow, although the column has a unique index. What could be wrong?scenario
Dapper sends C# strings as NVARCHAR(4000). Against the VARCHAR column, SQL Server converts the
column and may scan the index instead of seeking. Pass new DbString { Value = ..., IsAnsi = true, Length = 15 } so the parameter matches the column type, then confirm the seek in the plan.
Start from the symptom, explain the implicit conversion, show the DbString fix, and say how you would prove it: the execution plan or logical reads before and after.
SA dashboard makes six small queries per page load. How would you reduce the cost with Dapper?scenario
Send them together with QueryMultipleAsync and read each result in order from the grid reader:
one round trip instead of six. Select only the columns the dashboard shows, mapped to DTOs or
tuples. If it is still slow, look at the queries' plans and consider caching (Module 12).
Name round trips as the cost, show QueryMultiple, mention the order-dependent reading, and add what you would measure before and after.
AWould you use EF Core and Dapper in the same application? How would you organize it?design
Yes, commonly: EF Core for writes and ordinary reads of entities, where change tracking and LINQ save time, and Dapper for heavy reports and dashboards, where hand-tuned SQL matters. Both use the same connection string and can share a transaction. Keep each behind a repository or query interface so callers do not know which tool answers.
Give the split by workload, mention sharing connections and transactions, and explain how the abstraction keeps the choice reversible. A brief CQRS mention fits at this level.
EF Core basics
FWhat is EF Core, and what is a DbContext?concept
EF Core is Microsoft's ORM for .NET: you query with LINQ, and it writes the SQL, creates objects, tracks
changes, and saves them. A DbContext is one session with the database; each DbSet<T> is a table you
can query. A context should be short-lived, such as one per web request.
See the example · Lesson 59: EF Core database-first · Permalink
FWhat is the difference between database-first and code-first?concept
Database-first starts from an existing database: schema changes are made in SQL, and EF Core maps to the result, often helped by scaffolding. Code-first starts from C# classes, and EF Core migrations create and change the database. Database-first fits shared or older databases; code-first fits new apps that own their database.
See the example · Lesson 59: EF Core database-first · Permalink
FWhat does SaveChangesAsync do?concept
It looks at every tracked entity, finds the added, modified, and deleted ones, and sends the matching INSERT, UPDATE, and DELETE statements in one transaction. Changing one property of a loaded student produced an UPDATE of that single column.
See the example · Lesson 59: EF Core database-first · Permalink
MWhat does dotnet ef dbcontext scaffold generate, and why use --no-onconfiguring?concept
It reads a database and writes one class per table and view, plus a DbContext with the full mapping:
lengths, types, keys, indexes, defaults, and relationships. Without --no-onconfiguring, it also puts
the connection string in the generated code, which leaks credentials into source control.
See the example · Lesson 59: EF Core database-first · Permalink
MWhat is a value converter in EF Core? Give an example.coding
It translates a property value to a column value and back. In the SMS, Gender is an enum in C# and
a CHAR(1) code in the database: HasConversion(g => ((char)g).ToString(), c => (Gender)c[0]).
HasConversion<string>() stores an enum by name. Converters also apply to query parameters.
See the example · Lesson 59: EF Core database-first · Permalink
MWhy might EF Core throw 'The LINQ expression could not be translated', and how do you fix it?concept
The query calls something SQL cannot run, such as your own C# method (s.GetAgeOn(date)). EF Core
throws instead of loading everything. Rewrite the condition with translatable parts, such as a date
comparison, rather than calling ToList first and filtering in memory.
See the example · Lesson 59: EF Core database-first · Permalink
SHow do you map rich domain entities, with constructors and read-only properties, to an existing database with EF Core?design
Keep the entities in the core project and put all mapping in IEntityTypeConfiguration classes in the
data project. EF Core can call a constructor whose parameter names match properties, map read-only
properties that are configured explicitly, and use value converters for enums. The entities need no
EF Core attributes or base classes.
Describe the layering first, then the specific features (constructor binding, explicit property mapping, backing fields, converters), and say how you verified the mapping, such as comparing against the scaffolded model.
See the example · Lesson 59: EF Core database-first · Permalink
SA colleague wants to scaffold the database again every time a column changes, overwriting the entity classes. What do you advise?scenario
Regenerating overwrites anything added by hand, such as rules, constructors, or enums, and makes the domain model follow the database. Keep scaffolded output as a reference only, map hand-written entities through configuration classes, and change one configuration line when a column changes.
Explain the risk with a concrete loss, propose the separation, and mention a check that catches mapping drift, such as an integration test or a column comparison script.
See the example · Lesson 59: EF Core database-first · Permalink
AWho should own schema changes in a database-first EF Core system, and how do you keep code and database in step?design
The database scripts are the source of truth, reviewed and versioned like code, and run in order by deployment. EF Core mappings follow them. Keep both in one repository, deploy the script before the code that needs it, prefer additive changes, and run integration tests against a database built from the scripts so drift fails the build.
Cover ownership, versioning, deployment order, backward-compatible changes, and automated drift detection. Compare briefly with code-first migrations.
See the example · Lesson 59: EF Core database-first · Permalink
EF Core relationships
FWhat is a navigation property in EF Core?concept
A property that points to a related entity instead of holding a value. A reference navigation points
to one entity, such as Enrollment.Section; a collection navigation holds many, such as
FeeInvoice.Payments. The foreign key, such as SectionId, is always loaded; navigations only when
requested.
See the example · Lesson 60: EF Core relationships · Permalink
FWhat do Include and ThenInclude do?coding
Include loads a navigation of the entity being queried; ThenInclude continues from that navigation
to the next level. Kabir's student, enrollments, sections, classes, and years came back in one query
with Include(s => s.Enrollments).ThenInclude(e => e.Section).ThenInclude(...).
See the example · Lesson 60: EF Core relationships · Permalink
FWhat is the difference between eager, explicit, and lazy loading?concept
Eager loading fetches related data in the same query with Include. Explicit loading fetches it later,
on request, with Entry(...).Collection(...).LoadAsync(). Lazy loading fetches it automatically the
first time a navigation is read, through proxies, which hides queries and often causes N+1 problems.
See the example · Lesson 60: EF Core relationships · Permalink
MHow do you configure a one-to-many relationship with the Fluent API?coding
From either side, for example builder.HasMany(i => i.Payments).WithOne(p => p.FeeInvoice) .HasForeignKey(p => p.FeeInvoiceId). Use WithMany() with no argument when the other side has no
collection. Configuring it explicitly is needed when names do not follow conventions, such as
ClassId with a SchoolClass navigation.
See the example · Lesson 60: EF Core relationships · Permalink
MWhat is a global query filter, and when would you use one?concept
A condition EF Core adds to every query on an entity, configured with HasQueryFilter. It suits rules
that must never be forgotten, such as hiding soft-deleted rows or, in multi-tenant systems, other
tenants' rows. IgnoreQueryFilters switches it off; EF Core 10 can switch off one named filter.
See the example · Lesson 60: EF Core relationships · Permalink
MWhy might SaveChanges fail on a table with triggers, and how do you fix it?concept
EF Core reads generated values back with an OUTPUT clause, and SQL Server rejects OUTPUT without
INTO on tables with enabled triggers. Declare each trigger with
ToTable("Marks", t => t.HasTrigger("tr_Marks_Audit")), and EF Core switches to a method that works.
See the example · Lesson 60: EF Core relationships · Permalink
SA fee report shows full balances for invoices that are partly paid. The code calls invoice.Balance. What do you suspect?scenario
The invoices were loaded without their payments, so Payments is empty and AmountPaid is 0. Nothing
throws. Add Include(i => i.Payments), or better for a report, project the sum of payments in SQL,
and add a test with a partly paid invoice so the bug cannot return quietly.
Explain why missing related data is silent, give both fixes and when each fits, and finish with how you would prevent a regression.
See the example · Lesson 60: EF Core relationships · Permalink
SWould you enable lazy loading in a web API? Why or why not?design
Generally no. Every navigation access can issue a query, so loops become N+1 problems, the queries
are invisible in the code, and serializing an entity can trigger loads across the whole graph. Load
deliberately with Include or project to DTOs, which also keeps the API's shape independent of the
entities.
Give the performance and predictability reasons, mention serialization, and offer the alternative you would use. Acknowledge where lazy loading can be acceptable, such as small desktop tools.
See the example · Lesson 60: EF Core relationships · Permalink
AHow would you design soft delete and multi-tenancy filtering so no query can forget them?design
Put both rules in named global query filters on every affected entity, set the tenant from the request through the DbContext, and allow switching a filter off only in specific admin or repair code paths. Add database-side protection too, such as row-level security or filtered views for tenancy, and tests that fail if an entity lacks the filter.
Cover the EF Core mechanism, the source of the tenant value, the controlled escape hatch, defense in depth in the database, and automated checks. Module 13 builds the multi-tenant version.
See the example · Lesson 60: EF Core relationships · Permalink
EF Core performance
FWhat does AsNoTracking do, and when should you use it?concept
It tells EF Core not to keep snapshots of the loaded entities for change tracking. Use it for read-only queries such as lists, reports, and searches. Loading 10,012 students took about 65-77 ms with it and 376-456 ms without it, with identical SQL.
See the example · Lesson 61: EF Core performance · Permalink
FWhat is the N+1 problem?concept
Running one query for a list and then one more query for each item: 1 + N round trips. It often hides
in a loop or behind lazy loading. Fix it with one query that returns everything, such as an Include
or a projection with a subquery: 51 queries became 1 for a section's average marks.
See the example · Lesson 61: EF Core performance · Permalink
FHow do you page results with EF Core?coding
Order the query, then use Skip((page - 1) * pageSize).Take(pageSize), which becomes OFFSET and
FETCH in SQL. Return the total count for page numbers, and end the ordering with a unique column such
as Id, so rows never repeat or disappear between pages.
See the example · Lesson 61: EF Core performance · Permalink
MWhy is a projection to a DTO often better than loading entities?concept
It reads only the columns the DTO needs, creates no entities, and is never tracked. It also keeps internal columns, such as audit fields, away from the screen or API. Use entities when you will change and save them; use projections for everything that only reads.
See the example · Lesson 61: EF Core performance · Permalink
MWhat are ExecuteUpdateAsync and ExecuteDeleteAsync, and what do they skip?concept
They send one UPDATE or DELETE statement for every row matching a LINQ query, without loading entities. Graduating 1,006 students took 1 command instead of 25. They skip the change tracker, entity rules, and anything your SaveChanges logic does, such as setting audit columns, and tracked objects in memory are not refreshed.
See the example · Lesson 61: EF Core performance · Permalink
MWhat is a cartesian explosion, and how does AsSplitQuery help?concept
Including several collections in one query repeats each parent row for every combination of child
rows, so the result can grow enormously. AsSplitQuery() loads each collection with its own query.
The trade-off is extra round trips and possible inconsistency if data changes between the queries.
See the example · Lesson 61: EF Core performance · Permalink
SA page in the SMS is slow, and the team blames EF Core. How do you investigate?scenario
Measure first: log the SQL and count the queries for one page load. Many similar queries mean N+1; one huge query may mean too many includes or columns; a slow single query needs its execution plan and indexes. Then apply the matching fix and measure again with the same data.
Give a method, not a list of tips: measure, classify the cause, fix, and verify. Mention tools such as the EF Core command log, query counters, and SQL Server execution plans.
See the example · Lesson 61: EF Core performance · Permalink
SWhen would you use a compiled query, and what does it actually save?concept
For very frequent queries with the same shape, such as a lookup by key on every request. It saves EF Core's work of matching the LINQ expression to its cached SQL, not the database time. On SchoolDb, 1,000 lookups were about 20% faster. For ordinary queries, the extra code is not worth it.
State what is cached anyway, what compiling removes, and give a measured gain to keep it in proportion.
See the example · Lesson 61: EF Core performance · Permalink
AHow would you set data-access performance rules for a team, so problems are caught before production?design
Agree on defaults: projections for reads, no lazy loading, paging on every list, bulk methods for simple mass changes. Log slow queries and query counts per request in every environment, add tests or alerts that fail when a request exceeds a query budget, and run load tests on realistic data volumes such as 10,000 students before release.
Combine coding standards, automated detection, and production monitoring. Mention a realistic data set and query budgets per endpoint.
See the example · Lesson 61: EF Core performance · Permalink
Testing
FWhat is the difference between a unit test and an integration test?concept
A unit test checks one class's logic alone, with fakes for its dependencies, and runs in milliseconds. An integration test checks parts working together, such as a repository with the real SQL Server, so it finds wrong SQL, mapping, and constraints that fakes cannot.
See the example · Lesson 63: Testing data access · Permalink
FWhat is the difference between [Fact] and [Theory] in xUnit?coding
A [Fact] is a test with no inputs. A [Theory] runs once for each set of inputs supplied by
[InlineData] rows or a [MemberData] property; each set is reported as its own test. The fee tests
use a theory for zero, negative, and too-large amounts.
See the example · Lesson 63: Testing data access · Permalink
FWhat does Arrange, Act, Assert mean?concept
A structure for each test: Arrange sets up the objects and data, Act calls the one behaviour being tested, and Assert checks the result. It keeps tests short, readable, and focused on one behaviour.
See the example · Lesson 63: Testing data access · Permalink
MWhat is a fake, and how is it different from a mock?concept
A fake is a simple working implementation, such as a repository that stores students in a list. A mock is usually generated by a library and set up with expected calls and answers. Fakes are easy to read and reuse; mocks are handy for checking interactions. Both depend on the code using interfaces.
See the example · Lesson 63: Testing data access · Permalink
MHow do you keep integration tests from leaving data in the database?coding
Run each test inside a transaction that is rolled back. With TransactionScope created with
TransactionScopeAsyncFlowOption.Enabled and never completed, every connection opened inside joins
the transaction, whatever the tool, and disposal rolls it back. Alternatives are a fresh database
per run or deleting test data, both slower or more fragile.
See the example · Lesson 63: Testing data access · Permalink
MWhat is a regression test? Give an example.concept
A test written for a bug that already happened, so it cannot come back unnoticed. In SchoolApp, an invoice loaded without its payments showed the full balance; a test now loads Diya's partly paid invoice and checks that the balance is 25,500.
See the example · Lesson 63: Testing data access · Permalink
SYou have three repository implementations. How do you make sure they behave the same?design
Write one integration test class against the interface, and run every test once per implementation
with a theory and MemberData. Four tests became twelve for ADO.NET, Dapper, and EF Core. Where
behaviour legitimately differs, such as exception types, assert on what matters to callers or
translate the differences in the data layer.
Describe the shared contract tests, how inputs select the implementation, and what to do about differences the tests uncover.
See the example · Lesson 63: Testing data access · Permalink
SYour integration tests pass alone but fail when run together. What do you check?scenario
Shared state: tests that rely on data left by others, missing rollbacks, identity or sequence values
assumed to be fixed, or a transaction lost after await because the scope was not created with async
flow. Make each test arrange its own data, roll back everything it writes, and never assume run order.
List the likely causes, explain how to confirm each (run in a different order, inspect the data), and give the structural fix rather than ordering the tests.
See the example · Lesson 63: Testing data access · Permalink
AHow would you design a test strategy for a system like the SMS, from unit tests to production?design
Many fast unit tests for rules and services with fakes; integration tests for every repository and query against a database built from the real scripts; a few end-to-end API tests for key flows such as admission and fee payment; and data-consistency checks. Run unit tests on every change, integration tests in CI, and keep realistic data volumes for performance tests.
Use the test pyramid, say what each level catches and when it runs, and mention test data, database setup in CI, and how failures block a release.
See the example · Lesson 63: Testing data access · Permalink
Transactions and concurrency
FHow does EF Core use transactions when you call SaveChangesAsync?concept
Each SaveChangesAsync call runs all its INSERT, UPDATE, and DELETE statements in one transaction:
either all are saved or none. Taking a payment saved the payment and the invoice's new status
together. For several saves or mixed raw SQL, use Database.BeginTransactionAsync().
See the example · Lesson 64: Transactions and concurrency · Permalink
FWhat is the lost update problem?concept
Two users read the same data, both change it based on what they read, and the second save overwrites or contradicts the first. Two clerks each took 6,000 from an invoice with 6,000 due, because both screens showed 6,000; without a check, the invoice ended paid 16,000 of 10,000.
See the example · Lesson 64: Transactions and concurrency · Permalink
FWhat is a ROWVERSION column in SQL Server?concept
A binary value that SQL Server changes automatically on every update of the row; you never set it. Comparing the version you read with the current one tells you whether someone changed the row in between, which is the basis of optimistic concurrency.
See the example · Lesson 64: Transactions and concurrency · Permalink
MWhat is the difference between pessimistic and optimistic concurrency?concept
Pessimistic concurrency locks the data while working, so nobody else can change it, as
usp_RecordFeePayment does with UPDLOCK. Optimistic concurrency takes no lock and checks at save
time that the data has not changed, such as with a rowversion. Optimistic suits screens where users
think between reading and saving; pessimistic suits short operations with frequent conflicts.
See the example · Lesson 64: Transactions and concurrency · Permalink
MHow do you add optimistic concurrency to an entity in EF Core?coding
Map a ROWVERSION column as a concurrency token with IsRowVersion(), either on a byte[] property
or as a shadow property, builder.Property<byte[]>("RowVersion").IsRowVersion(). EF Core then adds
AND RowVersion = @original to every UPDATE and DELETE, and throws DbUpdateConcurrencyException
when no row matches.
See the example · Lesson 64: Transactions and concurrency · Permalink
MWhat happens to tracked entities when you roll back an EF Core transaction?concept
Nothing: the rollback undoes the database, but the objects keep their changed values, and the change tracker treats them as saved. The cancelled invoice still said Cancelled in memory while the database said Unpaid. Discard the DbContext and reload after a rollback.
See the example · Lesson 64: Transactions and concurrency · Permalink
SA clerk gets a concurrency error when taking a fee payment. What should the application do?scenario
Not show the raw exception. Translate DbUpdateConcurrencyException into a clear error, such as
50014 "the invoice was changed by someone else", reload the invoice so the clerk sees the current
balance, and let them decide again. Never retry a money operation automatically with stale values.
Cover translation, reloading, and user decision, and explain why automatic retry or "client wins" is wrong for payments, while it may be fine for low-risk fields.
See the example · Lesson 64: Transactions and concurrency · Permalink
SWhen do you need an explicit transaction in EF Core instead of relying on SaveChanges?concept
When one business operation needs several SaveChangesAsync calls, mixes EF Core with raw SQL or
another tool, or must read and write under one isolation level. Keep it short, commit at the end,
and roll back on any error. If connection retries are enabled, run the transaction inside the
execution strategy, because retries cannot replay a user-started transaction.
Give concrete triggers, show the begin, commit, rollback shape, and mention the execution strategy caveat and why long transactions hurt concurrency.
See the example · Lesson 64: Transactions and concurrency · Permalink
AHow would you choose between ADO.NET, Dapper, and EF Core for a new system like the SMS?design
Default to EF Core for everyday work with rules, because units of work, concurrency tokens, and LINQ remove much code and many bugs. Use Dapper for heavy reports where hand-tuned SQL matters, and ADO.NET for special cases such as bulk copy or streaming. Keep all of them behind repository interfaces, and keep critical rules in the database too.
Decide by workload, not by preference; mention mixing tools in one solution, shared transactions, team skills, and measurement before switching tools.
See the example · Lesson 64: Transactions and concurrency · Permalink
EF Core migrations
FWhat is an EF Core migration?concept
A C# class, written by dotnet ef migrations add, with an Up method that changes the database to match the
model and a Down method that undoes it. EF Core records applied migrations in the __EFMigrationsHistory
table, so database update runs only the ones a database does not have yet.
FWhat is the difference between database-first and code-first?concept
Database-first: the database is designed in SQL, and EF Core reads it. Code-first: the C# classes are the design, and migrations create and change the database. Database-first suits shared or older databases; code-first suits a database that one application owns.
FWhich dotnet ef commands do you use day to day with migrations?coding
migrations add <Name> to create one, migrations script to see its SQL, database update to apply all
pending ones, database update <Name> to go back or forward to one, migrations remove to delete the last
unapplied one, and migrations list to see them.
MWhy must you read a migration before applying it? Give examples.scenario
EF Core guesses from the model difference, and some guesses lose data. A rename combined with another change
becomes DropColumn plus AddColumn. A new required foreign key gets default 0 that matches no row. Defaults such
as cascade delete may not be what the business wants. Read Up, Down, and the SQL, and fix them.
MHow do you write a data migration, for example moving a text column into a new table?coding
Edit the generated migration: create the new table, copy the data with migrationBuilder.Sql, add and fill
the new key column, and only then drop the old column and add the foreign key. Write Down to move the data
back. Wrap hand-written SQL in EXEC(N'...') so idempotent scripts still compile, and test both directions.
MHow do you apply migrations to a production database?concept
Not from the app at startup. Generate an idempotent script (migrations script --idempotent) for a reviewer
or DBA to run, or build a migration bundle (migrations bundle) that the release pipeline runs with the
production connection string. Both skip migrations already in __EFMigrationsHistory.
SShould an application call Database.Migrate() when it starts?design
Usually not in production. EF Core 9 and later take a lock so two instances do not both migrate, but the app's login then needs permission to change the schema, a long migration delays start-up, and a failed migration stops the app from starting. Run migrations once in the release, with a script or bundle, under a separate login.
Mention the lock, then permissions, start-up time, failure handling, and the alternative.
SHow do you keep migrations healthy in a team where several developers change the model?scenario
One kind of change per migration, reviewed in pull requests with its SQL. Never edit or remove a migration
others have applied; add a new one. Resolve snapshot merge conflicts carefully and run
migrations has-pending-model-changes in the build so a model that disagrees with the migrations fails CI.
Test migrations, including Down, against a copy of real data before release.
Cover small migrations, review, never rewriting applied ones, snapshot conflicts, CI checks, and testing.
AHow do you change a large production database without downtime?design
Use expand and contract: first add new structures without breaking the old ones (new nullable columns or tables, both written by the app), backfill data in batches, switch reads to the new structure in a release, and only later remove the old columns. Keep every step backward compatible with the running version, avoid long blocking operations (online index builds, batched updates), and have a tested rollback for each step.
Explain expand and contract, compatibility between versions, batching, locking, and rollback per step.
Architecture
Backend basics
FWhat does a backend developer do?concept
A backend developer builds the parts of an application that users never see: the database, the business rules, and the APIs that apps call. They are responsible for data being correct, safe, and fast. In a School Management System, that means the attendance tables, the rule that marks cannot exceed the maximum, and the API the mobile app uses.
FWhat is the difference between frontend and backend?concept
The frontend is what users see and click: a website or mobile app running on their device. The backend runs on a server: it stores data, applies business rules, and exposes APIs. The frontend never touches the database; it calls the backend over HTTP.
MWhy do teams usually design the database before writing application code?concept
Data outlives applications: screens and services get rewritten, but school records stay for years. The tables decide what the code can do, and changing a table after real data exists means migrating that data. Designing the data model early also exposes missing requirements, such as a student having two guardians, before any code depends on them.
SWhat makes a project production-ready rather than tutorial-level?concept
A production-ready system enforces its rules on the server and in the database, not only on the screen. It handles two users saving at once, records who changed what, restricts data by role, logs errors, has tests, and stays fast with realistic data volumes. A tutorial usually shows only the happy path with ten rows.
Start with the definition (rules, security, reliability, performance, observability). Then give SMS examples: marks above the maximum are refused by the database, receipt numbers are unique even with double clicks, every marks change is audited. Finish with the trade-off: you cannot build everything at once, so say what you would add first (data integrity and security) and what can wait (caching, advanced monitoring).
Modules
MHow would you split a School Management System into modules?design
Split it by business capability: masters (years, classes, sections, subjects), students and guardians, enrollment, attendance, exams and marks, fees, users and roles, and a dashboard. Each module owns its tables and endpoints. Note the dependencies: attendance, marks, and fees all depend on enrollment, so masters and enrollment are built first.
Layers
SWhere should validation happen: in the screen, the API, or the database?scenario
In all three, for different reasons. The screen validates for quick feedback, the API validates every request because any client can call it directly, and the database constraints are the last safety net. For example, "marks cannot exceed 100" is shown on the screen, rejected by the API with 400, and blocked again by the database.
Explain the three purposes (user experience, authority, integrity). Use one SMS rule and show it at each layer. Then cover the trade-off: duplicated rules must stay in sync, so the API is the single source of business rules and the database holds only rules that must never be broken (uniqueness, required values, valid ranges).
See the example · Lesson 2: Client, API and database · Permalink
SThe class list page takes 8 seconds to load. How do you find which layer is slow?scenario
Measure each layer instead of guessing. The browser's network tab shows how long the API call took; the API logs show how much of that was the database call; the database's execution plan shows why the query is slow. The usual causes are a missing index, a query returning far more rows than the screen needs, or many small queries in a loop.
Describe the top-down method (browser, API, database) with the tool for each layer. Give the SMS case: the list of 40 students in 10-A should not scan 10,000 enrollments. End with the fix and how you would prove it worked: before and after timings.
See the example · Lesson 2: Client, API and database · Permalink
AWhy should a website or mobile app never connect directly to the database?design
The database credentials would have to live on every user's device, where anyone can extract them. There would be no single place to apply business rules and permissions, and every app would break whenever a table changes. An API keeps the database private, applies the rules once for every client, and gives apps a stable contract.
State the risks (security, duplicated rules, tight coupling to the schema). Use the SMS: a parent's app must only see their own child's marks, which only the server can enforce. Acknowledge the cost: an extra layer adds a little latency and code to maintain, which is worth it for any system with more than one client or sensitive data.
See the example · Lesson 2: Client, API and database · Permalink
Requirements
FWhat is the difference between functional and non-functional requirements?concept
A functional requirement describes what the system does, such as "a teacher marks daily attendance". A non-functional requirement describes how well it does it: speed, security, reliability, such as "the class list loads in under 2 seconds". Both must be written down and testable.
FWhat is a user story?concept
A user story is a short requirement written from the user's point of view: "As a [role], I want [action], so that [benefit]". For example: "As a class teacher, I want to mark attendance for my whole section in one screen, so that I finish it in five minutes." It is completed with acceptance criteria that define when it is done.
FWhat is a role in requirements, and why do we list roles?concept
A role is a type of user with its own permissions, such as Teacher, Accountant, or Parent. Listing roles shows who uses each feature and what each must not be allowed to do. Roles later become rows in a Roles table and permission checks in the API.
MWhat are acceptance criteria, and how do you write them?concept
Acceptance criteria are the checks that prove a story is done. A common format is Given / When / Then: given a starting situation, when the user acts, then a specific result happens. Each criterion should be testable, for example "given attendance for 10-A is already saved today, when the teacher saves again, then records are updated, not duplicated".
MHow do requirements turn into database tables?design
The important nouns become tables (Student, Section, Exam), and their details become columns. Rules become constraints, such as a unique admission number or marks within the maximum. Relationships become foreign keys or junction tables, for example a student with two guardians. Non-functional requirements shape the design too, such as soft delete and audit columns.
Environments
SThe code works on your machine but fails on the test server. How do you investigate?scenario
Compare everything that can differ: tool and runtime versions, configuration and connection strings, environment variables, database data and schema, and permissions. Read the server's logs for the exact error, then reproduce it locally with the same settings. Most cases come down to a version or configuration difference.
Give a structured checklist (versions, configuration, data, permissions, logs) rather than guesses. Use an SMS example: the test server runs an older SQL Server or a different connection string. Finish with prevention: pinned versions written in the README, the same database scripts for every environment, and configuration kept out of code.
SOLID
FWhat does SOLID stand for?concept
Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion: five principles that keep object-oriented code easy to change as it grows.
FWhat is the Single Responsibility Principle? Give an example.concept
A class should have one reason to change. In the SMS, FeeNotice decides the wording and an
INotificationSender decides the delivery channel, so a wording change and a provider change
each touch one class.
FWhat is a repository?concept
An object that stores and finds entities so the rest of the code never deals with SQL, such as
IStudentRepository with GetByAdmissionNumber and Add. Its interface lives with the business
rules; implementations live in the data layer.
MWhat is the Open/Closed Principle?concept
Code should be open for extension and closed for modification: add behavior by writing new
classes behind an existing abstraction, not by editing tested code. A new notification channel is
a new INotificationSender; the Notify method never changes.
MWhat is the Liskov Substitution Principle? Give an example of breaking it.concept
A derived class must work wherever its base class is expected, keeping the base class's
promises. A ReportCard subclass whose FormatScore throws NotSupportedException compiles, but
breaks any code holding a ReportCard.
MWhat is the difference between dependency inversion and dependency injection?concept
Dependency inversion is a design principle: high-level rules depend on abstractions they own, not on low-level details. Dependency injection is a technique: dependencies are passed in from outside, such as through a constructor. A DI container automates the injection.
SA service creates its own SQL repository with new inside a method, and now an offline mode is needed. What do you change?scenario
Introduce or use a repository interface, have the service receive it through its constructor, and create the concrete repository in one composition root. The offline version becomes a new class implementing the interface, and the service can be tested with an in-memory fake.
Name the principle (dependency inversion), show the refactoring steps safely, and mention the side benefit: unit tests without a database.
SWhat is a composition root?concept
The single place, usually at application startup, where concrete implementations are chosen and
wired together, such as new StudentService(new InMemoryStudentRepository()). The rest of the
code depends only on abstractions; in ASP.NET Core, Program.cs with the DI container plays this role.
Explain why one place matters: swapping an implementation is one change, and nothing else knows concrete types. Connect it to service registration in ASP.NET Core.
AWhen does applying SOLID become over-engineering?design
When principles are applied without a real cost to remove: interfaces with one implementation and no tests, factories for simple objects, layers that only pass calls through. Apply a principle when you can name the change or test it makes easier; keep pure, stable code simple, like a static rules class.
Show judgment with examples on both sides, and explain how you decide in reviews: name the expected change, weigh indirection against benefit, refactor when the need appears.
Design patterns
FWhat is a design pattern?concept
A named, proven solution to a recurring design problem, described as a shape of code rather than a library. Names such as Factory or Decorator let developers communicate a whole design in a word.
FWhat is the Factory pattern? Give an example.concept
A method or class whose job is to decide which object to create, so the decision lives in one
place. NotificationSenderFactory.For(guardian) returns an email or phone sender; callers never
write new EmailSender() themselves.
FWhat is the Strategy pattern?concept
A family of interchangeable rules behind one interface, chosen at run time. IGradeScale has a
primary-band strategy and an A1-to-E strategy; the report code uses whichever it is given.
MWhat is the Decorator pattern, and where does .NET use it?concept
A class that implements an interface and wraps another object of the same interface, adding
behavior before or after passing calls on, like LoggingSender. ASP.NET Core middleware, stream
wrappers, and retry or caching layers follow the same idea.
MWhat is the Observer pattern, and how does C# support it?concept
A subject notifies many observers when something happens, without knowing them. C# events
implement it directly: FeeInvoice.PaymentRecorded notifies every subscriber after a payment.
MWhat are the problems with the classic Singleton pattern?concept
Any code can reach it through a static property, so dependencies are hidden; tests cannot replace it; and its shared state must be thread-safe. Registering the class as a singleton service with dependency injection keeps one instance while making it explicit and replaceable.
SA text-message provider needs retries and logging. A colleague proposes copying the provider class and adding both. What do you suggest?scenario
Use decorators: a retrying wrapper and a logging wrapper around the unchanged provider, all implementing the same interface. Each concern stays separate and testable, and any future provider gets both by being wrapped the same way.
Name the pattern, explain why copying duplicates and couples concerns, and mention ordering of decorators (logging outside retrying shows each attempt or not) and DI registration.
SWhen is using a design pattern over-engineering?design
When it removes no real cost: a factory that always returns one class, a strategy interface with one implementation and no plan for more, decorators no one needs. Apply a pattern when you can name the repeated decision, changing rule, or extension it supports.
Give concrete signs of over-engineering and the cost (more files, indirection), then a rule for deciding, such as introducing the pattern when the second variant appears.
AWhich patterns does ASP.NET Core rely on, and how does that shape application design?design
Dependency injection (replacing factories and singletons), decorators in the middleware pipeline, strategies for logging and configuration providers, and options and observers for configuration changes. Applications should depend on interfaces, register implementations in one place, and add cross-cutting concerns as middleware or decorators.
Connect each framework feature to its pattern, then explain the consequences for structuring SMS code: thin controllers, services behind interfaces, cross-cutting concerns outside business logic.
Repository pattern
FWhat is the repository pattern?concept
An interface between business code and data access. The service asks the repository for entities,
such as GetByAdmissionNumberAsync, and never sees SQL, connections, or the ORM. Implementations can
use ADO.NET, Dapper, EF Core, or memory without the service changing.
FWhat are the layers of a typical .NET application, and which way do the dependencies point?concept
A host layer (console or Web API) that starts the app and wires implementations, a core layer with entities, rules, services, and interfaces, and a data layer with the database code. Host and Data reference Core; Core references neither, so the rules depend on nothing that changes often.
FWhat is a composition root?concept
The single place where concrete implementations are chosen and connected to the interfaces that use them. In SchoolApp's console it is Program.cs; in an ASP.NET Core app it is the dependency injection registration at startup. Everywhere else, code receives interfaces through constructors.
MHow does the repository pattern apply dependency inversion?concept
The high-level core project defines the interface it needs, IStudentRepository, and the low-level
data project implements it. The compile-time dependency points from Data to Core, the opposite of
the call direction, so the rules never depend on database details.
MWhat is a unit of work, and how does it relate to EF Core's DbContext?concept
A unit of work collects several changes and saves them together in one transaction, or not at all.
A DbContext already is one: it tracks changes and SaveChangesAsync writes them atomically. With
ADO.NET or Dapper you build it yourself, sharing one connection and transaction across operations.
MWhy should a repository not return IQueryable?concept
Callers would then compose queries, so database concerns, such as what can be translated to SQL and when the query runs, leak into the service layer, and the repository can no longer be swapped or faked faithfully. Return finished results: entities, DTOs, or pages.
SA teammate proposes a generic IRepository<T> with GetAll, GetById, Add, Update, and Delete for every table on top of EF Core. How do you respond?design
It mostly duplicates DbSet<T>, offers methods nobody should call, such as GetAll on 10,000
students, and often leaks IQueryable. Specific repositories with methods named after real needs,
or the DbContext directly in simple apps, give the same isolation with clearer intent.
Acknowledge the goal (consistency), list the concrete problems, propose the alternative, and say when you would accept a small generic base class for shared plumbing.
SYour in-memory test repository and the real database behave differently in a test. Give an example and say how you would handle it.scenario
In SchoolApp, a rolled-back transaction removed a new student from SQL Server, but the in-memory repository kept it, because a dictionary does not take part in transactions. Unique constraints, case rules, and generated values can differ too. Keep fast unit tests on fakes for rules, and run integration tests against the real database for behaviour that depends on it.
Name the leaky abstraction, give a concrete example, and describe the two-level test strategy that covers it.
AWhen is adding a repository layer not worth it, and how do you decide for a new system?design
In small apps with few rules, a stable data-access choice, and tests that can use a real database, a repository adds files without removing a cost; the DbContext can be used directly. Add repositories where rules need protecting, data access may change, or tests need fakes, and keep the decision reversible by keeping database code out of the core.
Give criteria rather than a yes or no, connect them to costs (change, testing, complexity), and show how a layered structure lets you add the pattern later without a rewrite.
Web API
HTTP basics
FWhat are a client and a server?concept
A client is a program that sends requests, such as a browser or a mobile app. A server is a program that waits for requests and answers them, such as our ASP.NET Core API. In the SMS, the teacher's browser is the client and the API is the server.
See the example · Lesson 2: Client, API and database · Permalink
FWhat is an API?concept
An API (Application Programming Interface) is a set of addresses that other programs call
to read or change data. A Web API works over HTTP and usually exchanges JSON. For example,
GET /api/classes/10/sections/A/students returns the students of Class 10-A.
See the example · Lesson 2: Client, API and database · Permalink
FWhat do the HTTP status codes 200, 201, 400, 404 and 500 mean?concept
200 OK means the request worked. 201 Created means something new was saved. 400 Bad Request means the client sent invalid data, 404 Not Found means the item does not exist, and 500 Internal Server Error means the server failed. Codes starting with 2 are success, 4 are client errors, 5 are server errors.
See the example · Lesson 2: Client, API and database · Permalink
MWhat is the difference between GET and POST?concept
GET reads data, has no body, and is safe to repeat; browsers may cache or bookmark it. POST creates something new and sends data in the body; repeating it can create duplicates. Never change data with GET, because a refresh or a link preview could repeat it.
See the example · Lesson 2: Client, API and database · Permalink
MWhat does it mean that HTTP is stateless, and how does the server know who is calling?concept
Stateless means each request stands alone; the server does not remember earlier
requests. So every request must carry the caller's identity, usually a token in the
Authorization header or a cookie. The API validates that token on every request.
See the example · Lesson 2: Client, API and database · Permalink
MWalk me through what happens when a teacher opens the class list.concept
The browser sends a GET request with the class filter and the login token. The API checks the token and the teacher's permission, runs the C# logic, and queries the database for the enrolled students. SQL Server returns the rows, the API converts them to JSON with status 200, and the browser displays the list.
See the example · Lesson 2: Client, API and database · Permalink
HTTP and REST
FWhat are the parts of an HTTP request and an HTTP response?concept
A request has a method and URL, headers, and an optional body. A response has a status line with a
status code, headers, and an optional body. For example, POST /api/students with a JSON body got
201 Created, a Location header, and the new student as JSON.
See the example · Lesson 65: HTTP and REST basics · Permalink
FWhat is the difference between 400 and 409, and between 401 and 403?concept
400 Bad Request means the request itself is wrong, such as an empty name; sending it again unchanged fails again. 409 Conflict means the request is valid but clashes with the current state, such as issuing a book with no free copy. 401 means "not signed in"; 403 means "signed in, but not allowed".
See the example · Lesson 65: HTTP and REST basics · Permalink
FWhat is a query string, and when do you use it instead of a path segment?concept
The part of a URL after ?, with name=value pairs joined by &. Use the path to identify a
resource, such as /api/students/3, and the query string for options on it, such as paging,
sorting, or filtering: /api/students?page=2&pageSize=20.
See the example · Lesson 65: HTTP and REST basics · Permalink
MWhat do safe and idempotent mean for HTTP methods?concept
Safe methods change nothing on the server, such as GET. Idempotent methods have the same effect
whether sent once or many times: GET, PUT, and DELETE are; POST is not. Deleting student 4
twice returned 204 then 404, but student 4 was gone either way.
See the example · Lesson 65: HTTP and REST basics · Permalink
MWhat is the difference between PUT and PATCH?concept
PUT replaces the whole resource with the body sent, so missing fields are treated as cleared, and
sending it twice gives the same result. PATCH changes only the fields sent; whether it is
idempotent depends on the change, such as "add 1" versus "set to 5".
See the example · Lesson 65: HTTP and REST basics · Permalink
MWhat makes an API RESTful? Give examples of good and bad paths.design
Resources with their own addresses, nouns in paths and verbs in methods, plural collections,
children under parents, statelessness, and status codes that report the result. Good:
GET /api/students/3/fee-invoices. Bad: POST /api/getFeeInvoicesForStudent?id=3.
See the example · Lesson 65: HTTP and REST basics · Permalink
SA mobile app shows success for failed fee payments. The API returns 200 with an error message in the body. What do you change?scenario
Return status codes that match the result: 400 for invalid input, 404 for a missing invoice, 409 for a conflict such as an already-paid invoice, and 2xx only for real success. Put details in a standard error body. Clients that check only the status code then behave correctly without changes.
Explain why status codes are the contract every client and tool reads, map each failure to a code, and mention a standard error format (ProblemDetails, Lesson 74) and versioning if clients depend on the old behaviour.
See the example · Lesson 65: HTTP and REST basics · Permalink
SA parent double-clicks 'Pay'. How do you stop two payments when POST is not idempotent?design
Make the operation idempotent: the client sends a unique idempotency key with the request, and the server stores it with the payment and returns the first result for any repeat with the same key. Disable the button after the first click too, but never rely on the client alone.
Name the problem (non-idempotent POST), describe idempotency keys with a unique constraint in the database, and add the UI and concurrency safeguards as extra layers.
See the example · Lesson 65: HTTP and REST basics · Permalink
AWhy does statelessness matter when an API must serve many schools and users?design
When each request carries everything needed, such as identity in a token and the ids in the URL, any server can handle any request. You can add servers behind a load balancer, restart one without losing sessions, and cache responses. Server-side session state ties users to one machine and limits scaling.
Connect statelessness to horizontal scaling, resilience, and caching, and say where state goes instead: the client, tokens, the database, or a distributed cache.
See the example · Lesson 65: HTTP and REST basics · Permalink
ASP.NET Core project
FWhat is Kestrel?concept
The cross-platform web server built into ASP.NET Core. It runs inside your application, so
dotnet run alone can serve HTTP requests. In production it can face the internet directly or sit
behind a reverse proxy such as IIS, Nginx, or a cloud load balancer.
See the example · Lesson 66: Creating SchoolApp.Api · Permalink
FWhat are the two halves of Program.cs in an ASP.NET Core app?concept
Before builder.Build(), services are registered with builder.Services.Add..., such as
controllers and OpenAPI. After it, the request pipeline is built with app.Use... and app.Map...,
and app.Run() starts the server. Services cannot be added after Build().
See the example · Lesson 66: Creating SchoolApp.Api · Permalink
FWhat is a controller, and what is an action?concept
A controller is a class, usually deriving from ControllerBase, that handles requests for one
resource, with a route such as [Route("api/school")]. An action is a public method in it with an
HTTP attribute such as [HttpGet]; it runs for the matching method and path.
See the example · Lesson 66: Creating SchoolApp.Api · Permalink
MWhat is launchSettings.json for, and is it used in production?concept
It holds launch profiles for running the app on a developer's computer: URLs and ports, and
environment variables such as ASPNETCORE_ENVIRONMENT=Development. It is not used in production;
servers set the environment and URLs through their own configuration.
See the example · Lesson 66: Creating SchoolApp.Api · Permalink
MHow does the hosting environment change an app's behaviour, and what is the default?concept
ASPNETCORE_ENVIRONMENT sets it, and code checks it with app.Environment.IsDevelopment(), to show
tools such as the OpenAPI document only while developing; settings files can differ per environment
too. When it is not set, the environment is Production, so development features stay off by default.
See the example · Lesson 66: Creating SchoolApp.Api · Permalink
MWhy does an app log 'Failed to determine the https port for redirect'?scenario
UseHttpsRedirection wants to send HTTP requests to HTTPS, but the app has no HTTPS endpoint, for
example when started with the http launch profile. It lets the request through and logs the warning.
With an HTTPS endpoint configured, HTTP requests get a 307 redirect instead.
See the example · Lesson 66: Creating SchoolApp.Api · Permalink
SEvery request to a new API returns 404, though the controller and route look correct. What do you check?scenario
That AddControllers() registers controllers and MapControllers() is in the pipeline, that the
controller is public with [ApiController] and a [Route], that the action has the right HTTP
attribute, and that the URL, including the api/ prefix and port, matches. Logging at Information
level for routing shows which endpoint, if any, matched.
Work from the outside in: the right app and port, the pipeline, the registration, then the route template. Name the logging you would switch on to confirm.
See the example · Lesson 66: Creating SchoolApp.Api · Permalink
SHow would you structure a Web API solution so the API project stays thin?design
The API project holds only HTTP concerns: controllers, middleware, configuration, and the composition root. Business rules and entities live in a core project, and data access in a data project, both referenced by the API. Controllers translate HTTP to service calls and results back to HTTP.
Describe the projects and their references, what belongs in each, and why it keeps rules testable without HTTP. Relate it to the layers from the repository pattern lesson.
See the example · Lesson 66: Creating SchoolApp.Api · Permalink
AWhat decides how an ASP.NET Core API is hosted in production, and what must differ from development?design
The hosting model: Kestrel directly, behind a reverse proxy, in a container, or on a managed platform, chosen for TLS termination, scaling, and operations needs. In production the environment is Production, development tools such as OpenAPI UIs are off or protected, HTTPS is enforced, URLs come from server configuration, and secrets come from a secure store, never source files.
Cover hosting options with trade-offs, then a checklist of what changes between environments, and how configuration is supplied per environment.
See the example · Lesson 66: Creating SchoolApp.Api · Permalink
Middleware
FWhat is middleware in ASP.NET Core?concept
A step in the request pipeline. Each middleware receives the HttpContext and the next step; it can
run code before calling next, stop the request, or run code after the rest of the pipeline returns.
Logging, HTTPS redirection, authentication, and error handling are all middleware.
See the example · Lesson 67: Middleware pipeline · Permalink
FWhat is the difference between app.Use and app.Run(delegate)?concept
app.Use adds a middleware that receives the next step and normally calls it. app.Run with a
delegate adds a terminal step that never calls a next one. app.Map starts a separate branch for a
path prefix. (app.Run() with no arguments, at the end of Program.cs, starts the server.)
See the example · Lesson 67: Middleware pipeline · Permalink
FWhat does short-circuiting mean in the middleware pipeline?concept
A middleware writes the response itself and does not call the next step, so the rest of the pipeline, including the controller, never runs. The earlier middleware still run their "after" code. Authentication answering 401, or a stock-taking check answering 503, are examples.
See the example · Lesson 67: Middleware pipeline · Permalink
MHow do you write a custom middleware class?coding
A class whose constructor takes RequestDelegate next (plus singleton services such as a logger) and
which has public async Task InvokeAsync(HttpContext context) that calls await _next(context).
Register it with app.UseMiddleware<T>(), usually through an extension method such as
app.UseRequestTiming().
See the example · Lesson 67: Middleware pipeline · Permalink
MWhy can't a middleware add a header after calling next, and what do you do instead?coding
Once the action starts writing the body, the headers have been sent, and changing them throws
"Headers are read-only, response has already started". Set headers before calling next, or register
context.Response.OnStarting(...), which runs just before the headers go out.
See the example · Lesson 67: Middleware pipeline · Permalink
MWhat is the usual order of the built-in middleware, and why?concept
Exception handling first, so it wraps everything; then HTTPS redirection, static files, routing and CORS, authentication, authorization, and finally the endpoints. Each step needs what the earlier ones did: authorization needs the user that authentication found.
See the example · Lesson 67: Middleware pipeline · Permalink
SAfter a cleanup of Program.cs, every signed-in user gets 401. What would you look at first?scenario
The middleware order: UseAuthorization() placed before UseAuthentication() checks requests before
any token has been read, so every caller looks anonymous. Restore the order, document why each
middleware sits where it does, and add a test that signs in and calls a protected endpoint.
Go from symptom to the pipeline, explain the dependency between the two steps, and add the regression test so the order is protected.
See the example · Lesson 67: Middleware pipeline · Permalink
SIn an app built with WebApplication, does it matter where MapControllers() appears relative to app.Use calls?concept
Not for when the endpoints run: MapControllers() only registers them, and WebApplication runs the
endpoint middleware at the end of the pipeline, after every Use... call. A middleware added after
MapControllers() still runs before the controller. The order of the Use... calls among themselves
still matters.
Explain the implicit routing and endpoint middleware WebApplication adds, describe how you would verify it, and note that keeping Map calls last is still the readable convention.
See the example · Lesson 67: Middleware pipeline · Permalink
AHow do you decide between middleware, filters, and code in the action for a cross-cutting concern?design
Middleware for concerns that apply to every HTTP request, even unmatched ones: timing, error handling, security headers, rate limiting. Filters for concerns tied to actions and their metadata: validation, per-action caching, action-level authorization. The action only for logic specific to that endpoint. Keep business rules out of all three, in the core layer.
Give the scope each mechanism sees, a concrete example for each, and the cost of putting a concern at the wrong level, such as running database work for every 404.
See the example · Lesson 67: Middleware pipeline · Permalink
Controllers and minimal APIs
FWhat is the difference between a controller and a minimal API endpoint?concept
A controller is a class deriving from ControllerBase, with action methods routed by attributes and
services injected through its constructor. A minimal API endpoint is a function mapped in code with
app.MapGet and similar, receiving services as parameters. Both run on the same pipeline.
See the example · Lesson 68: Controllers vs minimal APIs · Permalink
FWhat is a route parameter, and how does it reach the action?concept
A part of the route template in braces, such as {admissionNumber} in
api/students/{admissionNumber}. ASP.NET Core takes that part of the URL and passes it to the action
parameter with the same name, converting the type if needed.
See the example · Lesson 68: Controllers vs minimal APIs · Permalink
FWhat does 405 Method Not Allowed mean, and how is it different from 404?concept
405 means the path exists but not for that HTTP method, such as POST to a GET-only route; the
Allow header lists the methods that work. 404 means no route matches the path at all.
See the example · Lesson 68: Controllers vs minimal APIs · Permalink
MWhat does the [ApiController] attribute do?concept
It makes attribute routing required, returns 400 automatically when model validation fails, applies
binding source rules (complex types from the body), and turns error results such as NotFound() into
ProblemDetails JSON. Without it, a controller behaves like an MVC controller.
See the example · Lesson 68: Controllers vs minimal APIs · Permalink
MWhy return ActionResult<T> instead of IActionResult?coding
ActionResult<T> lets the action return either a T, which becomes 200 OK, or another result such
as NotFound(), with less code. It also keeps the success type visible to tools, so the OpenAPI
document describes the response body. IActionResult loses that type information.
See the example · Lesson 68: Controllers vs minimal APIs · Permalink
MWhy did the controller and the minimal API return different 404 responses for the same missing student?scenario
[ApiController] converts NotFound() into a ProblemDetails body with type, title, status, and
traceId, while the minimal API's Results.NotFound() sends an empty body. To make errors consistent,
register ProblemDetails for the whole app so every error uses the same shape.
See the example · Lesson 68: Controllers vs minimal APIs · Permalink
SWhat belongs in a controller action, and what does not?design
The action reads the request, calls a service, and turns the result into an HTTP response, such as 200 with a DTO or 404. Business rules, SQL, and entity mapping do not belong there: they go in the core services and the data layer, where they can be tested without HTTP.
Describe thin controllers and the reasons: testability, reuse across endpoints and other hosts, and keeping HTTP concerns in one layer. Give a concrete example of a rule that would be duplicated.
See the example · Lesson 68: Controllers vs minimal APIs · Permalink
SA minimal API project's Program.cs has grown to 2,000 lines. How do you restructure it?scenario
Move each resource's endpoints into its own static class with an extension method such as
MapStudentEndpoints(this IEndpointRouteBuilder app), using MapGroup("/api/students") for the shared
prefix and settings. Move handler bodies into named methods so they can be tested. Program.cs then
only calls one Map method per resource.
Give the structure, the grouping feature, and how it improves merges and tests; note that switching to controllers is not required to fix it.
See the example · Lesson 68: Controllers vs minimal APIs · Permalink
AHow would you choose between controllers and minimal APIs for a new product, and how would you keep the choice reversible?design
Weigh the number of endpoints, team experience, the need for filters and conventions, and existing code. Controllers suit large APIs and teams used to them; minimal APIs suit small focused services. Keep endpoints thin and rules in services, use one error format and one validation approach, and the endpoint layer can be rewritten without touching the rest.
Make it a decision with criteria and a written record, and show that a layered design limits the cost of changing it later.
See the example · Lesson 68: Controllers vs minimal APIs · Permalink
Dependency injection
FWhat is dependency injection?concept
A technique where an object receives the services it needs, usually through its constructor, instead
of creating them itself. In ASP.NET Core a built-in container creates them: StudentsController asks
for a StudentService and gets one, with its repository and DbContext built for it.
See the example · Lesson 69: Dependency injection · Permalink
FWhat is the difference between singleton, scoped, and transient lifetimes?concept
Singleton: one object for the whole application. Scoped: one object per request, shared inside it. Transient: a new object every time one is requested. In the demo, the singleton id never changed, the scoped id changed per request, and transient ids differed even within one request.
See the example · Lesson 69: Dependency injection · Permalink
FWhich lifetime should a DbContext have, and why?concept
Scoped, which AddDbContext uses by default. A DbContext tracks the entities of one unit of work and
is not thread-safe, so each request needs its own, shared by the repositories and services handling
that request, and disposed when the request ends.
See the example · Lesson 69: Dependency injection · Permalink
MWhat is a captive dependency, and how does ASP.NET Core help you catch it?concept
A shorter-lived service held by a longer-lived one, such as a singleton whose constructor takes a scoped DbContext: the context then lives forever and is shared by every request. In Development, scope validation stops the app at startup with "Cannot consume scoped service ... from singleton". It is off in Production by default.
See the example · Lesson 69: Dependency injection · Permalink
MWhy is the 'Unable to resolve service' error for a controller found only at request time?scenario
Controllers are not registered in the container; ASP.NET Core creates them when a request needs one, resolving their constructor parameters then. So startup validation does not see that a controller's dependency is missing. Tests that call every endpoint catch it before users do.
See the example · Lesson 69: Dependency injection · Permalink
MWhy prefer constructor injection over resolving services from IServiceProvider inside methods?concept
Constructor injection makes every dependency visible in one place, lets the container validate them,
and lets tests pass fakes directly. Resolving from IServiceProvider or HttpContext.RequestServices
(the service locator pattern) hides dependencies and couples code to the container.
See the example · Lesson 69: Dependency injection · Permalink
SParents occasionally see another parent's data at busy times. You suspect dependency injection. What do you look for?scenario
Singletons that hold per-request state, such as a current-user helper or a cache keyed by nothing, and singletons that capture scoped services. Check registrations for state and lifetimes, make per-request state scoped, keep singletons stateless or thread-safe, and add a concurrency test that runs two users in parallel.
Explain why the bug appears only under concurrency, list the suspicious registration patterns, and give the fix and a way to prove it.
See the example · Lesson 69: Dependency injection · Permalink
SA singleton background service needs to read the database. How do you give it a DbContext safely?coding
Inject IServiceScopeFactory, create a scope for each piece of work with CreateAsyncScope(), resolve
the DbContext or repository from that scope, and dispose the scope when the work ends. Each run then
gets a fresh context, as a request would, instead of one captive context for the life of the app.
Name the captive dependency risk, show the scope-per-unit-of-work pattern, and mention
IDbContextFactory as an alternative when only a context is needed.
See the example · Lesson 69: Dependency injection · Permalink
AHow would you organise service registration in a solution with many projects?design
Each project registers its own types through one extension method, such as AddSchoolData(connection),
so the host's Program.cs composes projects, not individual classes. Register interfaces to
implementations, choose lifetimes deliberately, keep scope validation on in development and CI, and
test that every endpoint resolves.
Cover ownership of registrations, the composition root, how lifetimes are reviewed, and automated checks. Mention keeping Core free of container packages if it should stay framework-neutral.
See the example · Lesson 69: Dependency injection · Permalink
Configuration
FWhere does an ASP.NET Core app read its configuration from, and in what order?concept
By default: appsettings.json, then appsettings.{Environment}.json, then user secrets in
Development, then environment variables, then command-line arguments. A later source overrides an
earlier one for the same key.
FHow do you read a connection string in ASP.NET Core?coding
Put it under ConnectionStrings in configuration, such as ConnectionStrings:SchoolDb, and read it
with builder.Configuration.GetConnectionString("SchoolDb"). Check that it is not null or empty at
startup, and keep passwords out of committed files.
FWhat are user secrets, and when should you use them?concept
A per-user JSON file outside the project folder, linked by the project's UserSecretsId, for local
development secrets such as your own connection string. They never reach Git and load only in the
Development environment. They are not encrypted and not for servers.
MHow do you set a nested configuration key with an environment variable?coding
Replace each : with a double underscore: School__CurrentAcademicYear=2027-28 sets
School:CurrentAcademicYear. The colon is not valid in environment variable names on every operating
system; on the command line, --School:Name=... keeps the colon.
MWhat is the options pattern, and why use it instead of reading IConfiguration directly?concept
It binds a configuration section to a class, such as SchoolOptions, registered with
AddOptions<T>().BindConfiguration("School") and injected as IOptions<T>. It gives typed values,
one place for defaults and validation, and classes that do not depend on configuration keys.
MWhat is the difference between IOptions, IOptionsSnapshot, and IOptionsMonitor?concept
IOptions<T> reads the value once for the app's life. IOptionsSnapshot<T> is scoped and reads a
fresh value each request, so file changes appear without a restart. IOptionsMonitor<T> is a
singleton with the current value and change notifications, for long-lived services.
SA deployment used a wrong page size setting and broke a screen two days later. How do you catch such errors earlier?scenario
Bind settings to options classes with validation attributes, and call ValidateDataAnnotations() and
ValidateOnStart(). The app then refuses to start with a message naming the setting, so the
deployment fails at once and the previous version keeps serving. Add custom Validate rules for
checks attributes cannot express.
Explain fail-fast configuration, how it interacts with deployment health checks, and give an example of the message an operator would see.
SA developer committed a production connection string with a password to Git. What do you do?scenario
Treat it as leaked: change the password immediately, check access logs, and only then remove it from the history if needed. Then fix the cause: production secrets come from environment variables or a secret store, local ones from user secrets, and a secret scanner runs on every push.
Order matters: rotate first, then clean up, then prevent. Mention that deleting the line does not remove it from history or from clones.
AHow would you design configuration and secret management for an API deployed to several environments?design
One build for all environments; safe defaults in appsettings.json; environment differences in environment files or host settings; secrets in a managed secret store, injected at startup with managed identities rather than stored passwords; options classes validated on start; and settings changes reviewed and versioned like code.
Cover the build-once principle, where each kind of value lives, how secrets are accessed without credentials, validation, and auditing changes.
OpenAPI
FWhat is OpenAPI, and how is it related to Swagger?concept
OpenAPI is a standard format, in JSON or YAML, that describes an HTTP API: paths, methods, parameters, bodies, responses, and data shapes. It was originally called the Swagger specification; Swagger is now the name of a family of tools around it, such as Swagger UI.
FWho uses an API's OpenAPI document?concept
Front-end and mobile developers to learn routes and fields, code generators to create typed clients, testers and test tools to know every status code, API gateways to import routes, and the API team itself as a reference and contract.
FHow do you add descriptions to an ASP.NET Core API's OpenAPI document?coding
Write /// XML comments on actions, with <summary>, <param>, and <response>, and set
GenerateDocumentationFile to true in the project file. In .NET 10, the OpenAPI package reads the
generated XML file and adds the text to the document.
MWhat does [ProducesResponseType] do, and what is the common mistake with it?coding
It declares a status code, and optionally a body type, that an action can return, so the OpenAPI document lists it. Once any response is declared, the success response is no longer inferred: declaring only 404 removed the 200. Declare every response, including the success one.
MHow do you change the document's title, or hide an endpoint from it?coding
Register a document transformer in AddOpenApi(options => options.AddDocumentTransformer(...)) and set
document.Info.Title. Hide a controller or action with [ApiExplorerSettings(IgnoreApi = true)];
hiding does not protect it, so secure the endpoint separately.
MWhy does an integer field appear as type integer or string in an ASP.NET Core OpenAPI document?concept
ASP.NET Core's web JSON defaults allow reading numbers from strings, such as "3", so the schema honestly lists both types with a number pattern. Clients normally send numbers; change the JSON number handling if a client generator copes badly with it.
SWhich API changes are breaking for clients, and how do you catch them before release?design
Breaking: removing or renaming a response field, changing a field's type, adding a required request field, removing an endpoint or changing its meaning. Usually safe: adding endpoints or optional fields. Generate the OpenAPI document in the build and compare it with the previous version in every pull request, failing the build on breaking differences.
Give examples on both sides, then describe automated contract comparison and what you do when a breaking change is unavoidable (versioning, deprecation period).
SShould the OpenAPI document and a documentation page be available in production?design
Only as a deliberate decision. Public APIs often publish them, sometimes behind authentication; internal APIs may expose them only inside the network or not at all. Never expose them by accident, and never rely on hiding endpoints from the document as security.
Weigh discoverability for partners against exposing the attack surface, and describe controls: environment checks, authentication, or publishing a static copy of the document instead.
AHow would you use OpenAPI to keep a mobile app and an API in step across several teams?design
Treat the document as the contract: generate it from the API in CI, publish each version, generate the mobile client from it, and compare versions to detect breaking changes. Require complete response declarations and descriptions in review, and version the API when a break is necessary.
Cover generation, publication, client generation, contract checks, and versioning, and the team agreements that make it work, such as reviewing document diffs.
Routing
FWhat is a route template?concept
A pattern for request paths, made of literal segments and parameters in braces, such as
api/students/{admissionNumber}. Routing matches the request's path and method against the templates
and passes parameter values to the action.
FHow do a controller's [Route] and an action's [HttpGet] combine?concept
The controller's route is a prefix and the action's template is added to it. [Route("api/students")]
with [HttpGet("{admissionNumber}")] gives GET api/students/{admissionNumber}; [HttpGet] alone gives
GET api/students.
FWhen should a value go in the path, and when in the query string?design
In the path when it identifies the resource or its place in a hierarchy, such as an admission number
or a class and section. In the query string when it is an option on a list: paging, filtering, or
sorting, such as ?page=2&pageSize=20.
MWhat are route constraints, and what happens when one fails?concept
Rules on a route parameter, such as int, range(1,10), or length(1,2), that decide whether the
route matches. When one fails, the route does not match, and the client gets 404 with no explanation,
before any action runs. They are for matching, not for validation.
MHow do you read and validate optional query string values in a controller?coding
Add parameters with [FromQuery] and default values, such as int page = 1, and put validation
attributes on them, such as [Range(1, 100)]. With [ApiController], an invalid value returns 400
ValidationProblemDetails naming the parameter, and the action never runs.
MWhat happens when two routes could match the same request?concept
The more specific template wins: a literal over a parameter, a constrained parameter over an
unconstrained one, so /api/students/3 chose {id:int} over {admissionNumber}. If two are equally
specific, the request fails with an ambiguous match error (500), which must be fixed in the routes.
SA list endpoint returns all 9,000 students and the mobile app is slow. How do you redesign it?scenario
Add paging with page and pageSize in the query string, a maximum page size, a stable sort ending
with the key, and a response that includes the total count. Add filters such as sectionId so clients
ask only for what the screen shows, and project to DTOs with only the needed fields.
Cover the request shape, the response shape, limits, sorting stability, filtering, and the database side (indexes, projection). Mention keeping old clients working while they move to the paged version.
SHow would you design routes for students, their guardians, and their fee invoices?design
/api/students for the collection and /api/students/{id} for one student; /api/students/{id}/guardians
and /api/students/{id}/fee-invoices for related collections; and /api/fee-invoices/{invoiceNumber} for
an invoice by its own key. Keep nesting to two parent levels, use plural lowercase nouns with hyphens,
and put filters in the query string.
Explain the reasoning for each choice: ownership, unique keys, depth limits, and consistency, and how the routes appear in the OpenAPI document.
AOffset paging with page numbers slows down on very deep pages. What alternative would you consider?design
Keyset (cursor) paging: the client sends the last key it saw, and the query asks for rows after it,
such as WHERE (LastName, FirstName, Id) > (@last...), which an index can seek directly. It stays fast
at any depth and is stable when rows are added, but it cannot jump to page 400; keep offset paging for
screens that need page numbers.
Explain why OFFSET reads and discards rows, describe keyset paging and its index needs, and choose per use case: infinite scroll and exports versus numbered pages.
Model binding and validation
FWhat is model binding in ASP.NET Core?concept
Turning request data into action parameters: route values, query string values, the JSON body,
headers, and services. With [ApiController], a complex type such as CreateStudentRequest is read
from the body, and simple types from the route or query string.
See the example · Lesson 73: Model binding and validation · Permalink
FHow do you add validation rules to a request model?coding
With data annotation attributes such as [Required], [StringLength(50)], [Range], and
[RegularExpression] on its properties or record parameters. With [ApiController], invalid requests
get 400 with ValidationProblemDetails before the action runs.
See the example · Lesson 73: Model binding and validation · Permalink
FWhat does CreatedAtAction return, and why use it?coding
A 201 Created response with the new resource as the body and a Location header built from another
action's route, such as the GET for one student. Building the URL from the route keeps it correct when
routes change, unlike a hand-written string.
See the example · Lesson 73: Model binding and validation · Permalink
MHow do you validate a rule that compares two fields?coding
Implement IValidatableObject on the request type and return ValidationResult objects from
Validate, naming the fields involved, such as an age rule over DateOfBirth and AdmissionDate.
ASP.NET Core calls it only after every attribute has passed.
See the example · Lesson 73: Model binding and validation · Permalink
MWhy might a JSON enum value like "Male" fail to bind, and how do you fix it?concept
System.Text.Json reads and writes enums as numbers by default, so a name cannot be converted and the
request gets 400. Register JsonStringEnumConverter in the JSON options so enums travel as names,
which are clearer and stay stable when enum values are reordered.
See the example · Lesson 73: Model binding and validation · Permalink
MIn what order are binding and validation checks applied, and why does it matter?concept
Content type (415 if not JSON), then binding (400 for values that cannot be converted, stopping at the first), then attributes (every failing field), then IValidatableObject, then the action. Each step runs only if the previous passed, so clients may need two attempts to see all kinds of errors.
See the example · Lesson 73: Model binding and validation · Permalink
SWhere should validation live: the API request model, the domain, or the database?design
In all three, for different purposes. The request model checks shape and format and gives clients clear 400 errors. The domain enforces business rules for every caller, not just the API. Database constraints are the last guard against any client or bug. Duplicated rules are acceptable when each layer has its own reason.
Explain what each layer can see, give SMS examples for each, and how you keep messages consistent, such as mapping domain errors to ProblemDetails.
See the example · Lesson 73: Model binding and validation · Permalink
SA mobile app sends dates like 12/04/2016 and some records are wrong. What do you change?scenario
Accept only an unambiguous format, ISO 8601 (2016-04-12), which DateOnly binds directly, and reject
anything else with a 400 that names the field. Fix the app to send ISO dates, and check existing data
for swapped day and month values.
Identify the ambiguity, enforce a strict contract, communicate it in the OpenAPI document, and plan the data repair.
See the example · Lesson 73: Model binding and validation · Permalink
AHow would you design input validation and error reporting for a large API used by several client teams?design
One request DTO per operation with declarative rules, cross-field rules in IValidatableObject or a validation library, one error format (ValidationProblemDetails with stable keys and messages), rules visible in the OpenAPI document, and tests for each rule. Keep business rules in the domain and map their errors to the same format.
Cover consistency, documentation, localisation of messages if needed, and how clients can rely on error keys. Mention when a library like FluentValidation is worth it.
See the example · Lesson 73: Model binding and validation · Permalink
Responses and errors
FWhat is ProblemDetails?concept
A standard JSON format for HTTP error responses (RFC 9457), with type, title, status, detail,
and instance, plus your own extension fields such as errorNumber. ASP.NET Core can produce it for
every error, so clients parse one shape everywhere.
See the example · Lesson 74: Responses and status codes · Permalink
FWhich status code should a successful POST that creates a resource return?concept
201 Created, with a Location header giving the new resource's address and, usually, the resource in
the body. In a controller, CreatedAtAction builds both from another action's route.
See the example · Lesson 74: Responses and status codes · Permalink
FWhy should an API not return exception details in a 500 response?concept
Stack traces and messages reveal file paths, library versions, SQL, and internal names that help
attackers, and they mean nothing to users. Return a general ProblemDetails with a traceId, and write
the full exception to the server log.
See the example · Lesson 74: Responses and status codes · Permalink
MWhen would you return 400, 409, and 422?concept
400 when the request is malformed or fails field validation. 409 when a valid request conflicts with the current state, such as a duplicate admission number or a record changed by someone else. 422 when a well-formed request breaks a business rule, such as paying more than the balance.
See the example · Lesson 74: Responses and status codes · Permalink
MHow do you handle exceptions globally in ASP.NET Core 8 and later?coding
Register AddProblemDetails() and one or more IExceptionHandler classes with AddExceptionHandler<T>(),
and put app.UseExceptionHandler() first in the pipeline. Each handler's TryHandleAsync returns true
for exceptions it answers; anything unhandled becomes a 500 ProblemDetails.
See the example · Lesson 74: Responses and status codes · Permalink
MWhat does UseStatusCodePages add when ProblemDetails is registered?concept
It fills empty error responses, such as a 404 from a failed route constraint, with a ProblemDetails body. Without it, those responses have no body, so clients see two different error shapes.
See the example · Lesson 74: Responses and status codes · Permalink
SA client retries every failed payment three times. Some failures can never succeed. What do you change in the API?scenario
Return 4xx codes for client-side and rule failures, such as 422 for amount above balance and 409 for
conflicts, so the client knows retrying is pointless, and 5xx only for real server failures, which may
be retried. Include a machine-readable errorNumber, and make payments idempotent so a retried request
cannot charge twice.
Explain status codes as instructions for client behaviour, give the mapping, and mention idempotency keys for safe retries of money operations.
See the example · Lesson 74: Responses and status codes · Permalink
SShould controllers catch domain exceptions with try/catch? What is the alternative?design
Usually not: repeated try/catch blocks drift apart and hide the action's real work. Let domain exceptions
carry a meaning, such as an error number, and translate them in one place: an IExceptionHandler that
maps them to status codes and ProblemDetails. Use controller helpers only for outcomes the controller
decides itself, such as not found.
Contrast the two approaches with an example, and mention testing the mapping once and keeping the domain free of HTTP concepts.
See the example · Lesson 74: Responses and status codes · Permalink
AHow would you design an API's error contract for many client teams over several years?design
One format (ProblemDetails) for every error; a documented catalogue of stable error codes, such as the SMS's 500xx numbers, each with its HTTP status; codes and status listed in the OpenAPI document; human messages that may change and machine codes that never do; a traceId for support; and versioning rules for adding codes without breaking clients.
Separate what clients may depend on (status, code) from what may change (text), show where the catalogue lives, and how it is tested and documented.
See the example · Lesson 74: Responses and status codes · Permalink
FWhat is an API response wrapper (envelope), and why do client teams ask for one?concept
A fixed outer shape for every response body, such as status, message, and data. A client can then read every endpoint with one piece of code: check status, show message, use data. Without it, each endpoint and each kind of error can have its own shape, and every app needs special parsing for each.
See the example · Lesson 80: API response wrapper · Permalink
FIf every response has a status field, why keep using HTTP status codes?concept
Browsers, proxies, load balancers, monitoring, and retry logic read the HTTP status code, never the JSON body. Answering 200 with "status": "error" hides every failure from them. In the SMS, a missing student is still 404 and a broken rule still 422; the wrapper only changes the body.
See the example · Lesson 80: API response wrapper · Permalink
FWhy does the SMS DELETE endpoint answer 200 with a message instead of 204 No Content?concept
A 204 response can never have a body, so it cannot carry the wrapper. With an envelope, every answer has a body, so a successful delete answers 200 with ApiResponse.Ok("Student ADM-2026-10001 was removed.") and no data.
See the example · Lesson 80: API response wrapper · Permalink
MHow can a C# design guarantee that an error response never contains data?coding
Use two classes: a base ApiResponse with status, message, errorCode, errors, and traceId, and
ApiResponse<T> that adds Data. The Fail method returns the base class, so an error object has no Data
property at all, and the JSON cannot contain one even by mistake.
See the example · Lesson 80: API response wrapper · Permalink
MWhich error paths in ASP.NET Core must be changed so that every error uses the wrapper?coding
Four: exceptions you expect (an IExceptionHandler for SchoolRuleException), every other exception (a last IExceptionHandler that answers 500), automatic validation errors (ApiBehaviorOptions. InvalidModelStateResponseFactory), and empty error responses such as unknown routes or 405 (UseStatusCodePages with a handler). Also set SuppressMapClientErrors, or a bare NotFound() still becomes a ProblemDetails.
See the example · Lesson 80: API response wrapper · Permalink
MWhat is a traceId in an error response, and how is it used?concept
The id of the request's trace (Activity.Current.Id in ASP.NET Core). The client gets it in the error response; the server logs the real exception under the same id. Support asks the user for the traceId and finds the exact log entry, without the response ever exposing exception details.
See the example · Lesson 80: API response wrapper · Permalink
SAfter adding an IExceptionHandler for 500s, production crashes no longer appear in the logs. What happened, and how do you fix it?scenario
Since .NET 10, the exception handler middleware does not write its own log entry when an IExceptionHandler returns true. The handler answered the client but nobody logged the exception. Fix it by logging inside the handler with ILogger, including the same traceId the response carries, and add a test or check that a crash produces a log entry.
Start from the symptom, name the framework behaviour change, give the fix (log in the handler with the traceId), and say how you would stop it happening again.
See the example · Lesson 80: API response wrapper · Permalink
SYour team is split: some want a response envelope, others want plain bodies with ProblemDetails (RFC 9457) for errors. How do you decide?design
ProblemDetails is a standard: tools, gateways, and generated clients understand it, and success bodies stay plain. An envelope gives one shape for success and error, which many front-end and mobile teams prefer, but it is a house format that every client must learn. Decide by who consumes the API: public or partner APIs lean to standards; one team's own apps often lean to an envelope. Either way, keep real status codes, never leak exception details, and document it in OpenAPI. The SMS chose an envelope for its own apps and keeps the same status codes.
Compare both on standards, tooling, and client simplicity, tie the choice to the API's consumers, and name what must stay true in both designs.
See the example · Lesson 80: API response wrapper · Permalink
AMany services each have their own response format, and clients struggle. How would you standardise the response contract across them?design
Agree one contract (envelope or ProblemDetails) and publish it as a small shared library with the common Ok and Fail methods, the error handlers, and the validation setup, so services adopt it with a few lines. Define error codes per domain, require a traceId linked to distributed tracing, describe the shapes in each OpenAPI document, and add contract tests that call every endpoint's error paths. Roll it out with API versioning, so existing clients keep the old shape until they move.
Cover the contract, how teams adopt it cheaply, how it is enforced (tests, OpenAPI), observability, and a migration plan that does not break existing clients.
See the example · Lesson 80: API response wrapper · Permalink
Building endpoints
FWhat does CRUD mean, and which HTTP methods does each part use?concept
Create, read, update, and delete: the basic operations on a record. In a REST API, create is POST,
read is GET, update is PUT or PATCH, and delete is DELETE, each on a resource address such as
/api/students/{admissionNumber}.
FWhat is a soft delete, and why use it for students?concept
Marking a record as deleted, such as IsDeleted = 1 with a timestamp, instead of removing the row.
Students have invoices, payments, attendance, and marks; removing the row would break that history or be
refused by foreign keys. A query filter hides soft-deleted rows everywhere.
FWhat status codes should PUT and DELETE return?concept
PUT: 200 with the updated resource, or 204 with no body; 400 for invalid input; 404 when the resource
does not exist. DELETE: 204 when removed; 404 when there is nothing to remove, including a second
delete of the same resource.
MHow would you implement optional search and filter parameters on a list endpoint with EF Core?coding
Start from the IQueryable, add a Where only for each parameter that was given, then count, sort,
and page. EF Core turns it into one parameterised SQL query; StartsWith becomes LIKE 'text%', which
can use an index, and a related-table filter becomes EXISTS.
MWhat is an EF Core SaveChanges interceptor, and what would you use one for?coding
A class EF Core calls during SaveChanges, such as before the SQL is sent. It can inspect and change
tracked entities, for example setting UpdatedAt on every modified entity with audit columns, so no
repository or service has to remember to do it.
MWhy is a uniqueness check in code not enough, even when it runs before every insert?concept
The check cannot see rows hidden by filters, such as soft-deleted records, and two requests can both pass it at the same moment before either inserts. Only the database sees every row at insert time, so keep a UNIQUE constraint and translate its error (2627 or 2601) into a clear 409.
SA school asks to permanently delete a student's data on request. How does that fit with soft delete?scenario
Soft delete covers normal removals. A permanent erasure request is a separate, audited process: check legal retention rules for fee and academic records, anonymise or remove personal fields while keeping financial and statistical records that must stay, record who approved it, and run it in a transaction.
Distinguish routine removal from data-protection erasure, mention retention obligations, anonymisation, auditing, and backups that also hold the data.
SWhen would you add PATCH to an API that already has PUT?design
When clients often change one or two fields of a large resource, when several clients edit different fields of the same record, or when sending every field risks overwriting newer data. Use a clear format, such as JSON Merge Patch or JSON Patch, validate the result after applying it, and combine it with a concurrency check.
Weigh payload size and lost-update risk against complexity, name the patch formats, and explain validating the patched result rather than the patch itself.
AHow would you design search for a system with millions of students across many schools?design
Prefix searches on indexed name and number columns for the common cases, always scoped by school and paged with a stable sort. For "contains", spelling mistakes, or several fields at once, use full-text search or a dedicated search engine fed by changes from the database, and keep the API contract the same whichever engine answers.
Start from the query patterns, show the index each needs, explain where LIKE stops scaling, and how a search engine is kept in sync.
FWhat is master data, and how is it different from transaction data?concept
Master data is reference information that other features depend on, read constantly and changed rarely, such as academic years, classes, sections, and subjects. Transaction data records daily activity, changes constantly, and grows large, such as attendance, marks, and payments.
FWhat does the Cache-Control header with max-age do?concept
It tells clients and caches how long they may reuse a response without asking the server again, such
as Cache-Control: public,max-age=3600 for one hour. In ASP.NET Core, [ResponseCache(Duration = 3600)]
adds it; the server itself stores nothing.
FWhy return classes together with their sections in one response?design
Screens almost always need both. Returning them nested saves the client from making one more request per class, the N+1 problem on the client's side, and EF Core can build the whole tree with one SQL query.
MWhy should error responses never be cached, and what does ASP.NET Core do about it?concept
A cached error, such as a 404 for the current academic year, could be shown long after the problem was
fixed. ASP.NET Core's exception handling adds Cache-Control: no-cache,no-store to the error responses
it writes, so clients and proxies do not keep them.
MHow do you choose a max-age for an endpoint?design
By how long the data may be stale without harm. Subjects and academic years can be an hour or more; classes with live student counts a few minutes during admissions; attendance or balances not at all. When in doubt, start short and lengthen it after watching how the data changes.
MWhat does the EF Core warning about a global query filter on the required end of a relationship mean?concept
A principal entity, such as Student, is filtered, but a dependent that requires it, such as Enrollment, is not. Queries on the dependent then still return rows whose principal is hidden, such as enrollments of removed students being counted. Give the dependent a matching filter through its navigation, or make the relationship optional.
STwo subjects, Maths and Mathematics, exist and marks are split between them. How do you fix it and prevent it?scenario
Merge them: choose one, move every mark, exam paper, and allocation to it in one transaction, then remove the other. Prevent it with unique constraints on both the code and the name, translated into a 409, and restrict master data changes to administrators.
Cover the data repair, its transaction and verification, the constraints, the API's answer, and the permission rule.
SAn app loads the same subject list thousands of times each morning. What can the API do?scenario
Send Cache-Control with a suitable max-age so clients reuse the answer, and support conditional
requests with ETag so clients that do ask get 304 Not Modified. Add server-side caching for the data
itself if the database is still busy, and invalidate it when an administrator changes the list.
Separate client caching, conditional requests, and server caching, and explain invalidation when master data changes.
AHow would you manage master data shared by many schools in a multi-school product?design
Separate global reference data, such as a standard subject catalogue, from school-specific data, such as sections and timetables; scope every school-specific row by school; version changes to shared data; cache per school with clear invalidation; and let administrators change only their own school's data.
Discuss global versus tenant-owned data, isolation, caching keys, change control, and migration of existing schools when the shared catalogue changes. Module 13 builds this.
FWhat is a many-to-many relationship? Give an example from a school system.concept
Each row on one side can relate to many rows on the other, and the other way round. A student has several guardians, and a guardian can have several children at the school. It is stored in a junction table, such as StudentGuardians, with one row per link.
See the example · Lesson 77: Teachers and guardians · Permalink
FWhat is a composite key?concept
A primary key made of more than one column. In StudentGuardians it is (StudentId, GuardianId): the pair
is unique, so a guardian is linked to the same student only once. In EF Core it is declared with
HasKey(sg => new { sg.StudentId, sg.GuardianId }).
See the example · Lesson 77: Teachers and guardians · Permalink
FShould an endpoint return 404 or an empty list when a student has no guardians?design
An empty list with 200: the student exists, and the answer to "which guardians?" is "none". Return 404 only when the parent resource, the student, does not exist, so clients can tell the two cases apart.
See the example · Lesson 77: Teachers and guardians · Permalink
MHow do you map a junction table that has its own columns in EF Core?coding
As an entity of its own, such as StudentGuardian, with a composite key and two one-to-many relationships, one to each side. EF Core's automatic many-to-many mapping hides the junction table and has no place for columns like Relationship or IsPrimaryContact.
See the example · Lesson 77: Teachers and guardians · Permalink
MHow do you enforce 'at most one primary contact per student' in SQL Server?coding
With a filtered unique index on StudentId that includes only rows where IsPrimaryContact = 1. Students may have many guardians, but only one row per student can be in the index, so a second primary contact fails with error 2601.
See the example · Lesson 77: Teachers and guardians · Permalink
MHow can one controller serve routes under two different resources?coding
Leave the controller without a class-level route, or ignore it, and start each action's route with "/",
which makes it absolute, such as [HttpGet("/api/students/{admissionNumber}/guardians")] and
[HttpGet("/api/guardians/{guardianId:int}/students")].
See the example · Lesson 77: Teachers and guardians · Permalink
SSome students show two primary contacts after two years of use. How do you fix the system?scenario
Fix the code so setting a new primary clears the old one in the same save; clean existing data with a script that keeps one primary per student by an agreed rule and reviews unclear cases; then add a filtered unique index so the data can never break the rule again, whatever code or tool writes to it.
Cover the code fix, the data repair with its decision rule and verification, and the database constraint as the lasting guard.
See the example · Lesson 77: Teachers and guardians · Permalink
SWhich personal details should a list endpoint return, and how do you decide?design
Only what the screens using it need: a staff list needs names, codes, and work emails, not personal phones or dates of birth. Put more sensitive details on single-resource endpoints that fewer roles may call, and check every DTO against who will see it.
Mention data minimisation, roles and authorization (Module 12), logging and caching risks of personal data, and reviewing DTOs as part of design.
See the example · Lesson 77: Teachers and guardians · Permalink
AHow would you model people who play several roles, such as a teacher who is also a parent at the school?design
Separate the person from their roles: one person record with shared identity and contact details, and role records such as Teacher and Guardian that reference it, each with role-specific data and permissions. Logins attach to the person, and authorization checks roles, so one sign-in can see both the staff and the parent views.
Compare per-role tables (as in the SMS today) with a person-plus-roles model, discuss duplication, identity, and authorization, and how you would migrate.
See the example · Lesson 77: Teachers and guardians · Permalink
FWhat is a race condition in a web API? Give an example.concept
Two requests run at the same time and the result depends on their timing. Two clerks enrolling into one section both read "highest roll number 0" and both save roll number 1; one of them acted on information that was no longer true.
FWhy put the enrollment rules in a service rather than in the controller?design
The rules (active student, not already enrolled, capacity, next roll number) are business logic that must hold for every caller, and they need several repository calls in one transaction. A service keeps them in one tested place; the controller only translates HTTP to the service call.
FWhich status codes fit 'section is full', 'student not found', and 'student not active'?concept
Section full: 409 Conflict, because the request clashes with the section's current state. Student not found: 404. Student not active: 422, a business rule. Each also carries an SMS error number, such as 50034, 50030, and 50032.
MHow do you make 'check, then insert' safe when two requests act on the same group of rows?coding
Serialize them on something that represents the group: start a transaction and lock its row, such as
SELECT ... FROM Sections WITH (UPDLOCK, ROWLOCK), then check and insert, then commit. Requests for the
same section wait; other sections are unaffected. Keep a unique constraint as a final guard.
MHow do you generate the next number within a group, such as a roll number per section?coding
While the group is locked, read the highest existing number and add one, then insert, all in one transaction; back it with a unique constraint on (group, number). Without the lock, two requests compute the same number. A sequence or identity does not fit, because numbering restarts per group.
MWhy might a soft-delete query filter cause duplicate numbers, and how do you avoid it?concept
Rows hidden by the filter still exist and still hold their numbers in the unique constraint. If the
highest number is read through the filter, a removed student's number can be handed out again and the
insert fails. Read the highest number with IgnoreQueryFilters, while counting capacity with the filter.
SHow do you test that a lock really serializes concurrent requests?design
Make the timing deterministic: in the test, take the lock with one connection and hold it, start the operation on a second connection, check that it has not finished after a delay, release the lock, and check the operation then completes correctly. Sending two requests by hand rarely overlaps reliably.
Explain why naive parallel tests are flaky, describe the hold-the-lock pattern, and mention cleaning up test data when the code under test runs its own transaction.
SWhen would you choose pessimistic locking over optimistic concurrency?design
When conflicts are likely and the work under the lock is short, such as many clerks enrolling into the same sections in admission week: waiting briefly is better than failing and retrying. Choose optimistic concurrency when conflicts are rare or when a user thinks between reading and saving, where holding a lock would block others for minutes.
Compare conflict rate, lock duration, user experience, and deadlock risk, with an example of each from the SMS (enrollment versus fee payment screens).
AAdmission week overloads one popular section with waiting enrollments. How would you scale the design?design
Keep the lock scope minimal and the locked work short; move slow work (notifications, documents) after the commit or to a background queue; consider assigning roll numbers in a later batch step so admission only reserves a place; and use a queue per section if demand truly exceeds what one lock can serve.
Identify the contention point, reduce work inside it, separate reservation from numbering, and discuss asynchronous processing and its effect on the user experience.
FWhy take a whole section's attendance in one request instead of one request per student?design
One request is faster on weak networks, and it can be saved in one transaction, so a register is never half saved. It also lets the API check the register as a whole, such as that every enrolled student is included exactly once.
FWhat does it mean that the attendance PUT is idempotent, and why does it matter?concept
Sending the same register twice leaves the same result as sending it once: the second request created and updated nothing. On school Wi-Fi, apps retry timed-out requests, so retries must be harmless instead of creating duplicate records.
FHow is the attendance percentage calculated in the SMS?concept
(Present + Late) divided by all recorded days, times 100, the same rule as Lesson 18's SQL. Kabir had 2 present days out of 3 recorded in July, which is 66.67%.
MWhat is TimeProvider, and why use it instead of DateTime.Now?coding
.NET's built-in abstraction for the current time. The app registers TimeProvider.System; tests pass a
provider stopped at a fixed moment. Rules such as "no future dates" then give the same result on every
test run, instead of changing as real days pass.
MHow do you implement 'update if it exists, otherwise insert' for a batch with EF Core?coding
Load the existing rows for the batch's key (section and date) as tracked entities, then for each item update the matching entity if its values differ, or add a new one. One SaveChangesAsync writes the updates and inserts in one transaction, with a unique constraint as the final guard against duplicates.
MWhy should attendance reports be aggregated in SQL rather than in C#?concept
A day has about 10,000 records. GROUP BY in SQL Server sends back ten rows of counts; loading the records into the API to count them would move all 10,000 rows for the same ten numbers. With an index on the date, the SMS's daily summary took about 120 ms over 600,000 rows.
SReports show some students above 100% attendance. What likely happened, and how do you prevent it?scenario
Duplicate records for the same student and day, usually from retried requests that inserted again. Repair the duplicates, add a unique constraint on (enrollment, date) if missing, and make the save idempotent: one request per register, updating existing records instead of inserting blindly.
Work from symptom to cause (duplicates), name the missing guards (constraint, idempotency), and cover the data repair with verification.
SShould the attendance endpoint accept a partial register? Discuss the trade-off.design
Requiring the complete register prevents a forgotten student from looking present or unrecorded, and keeps every day's data comparable. Accepting partial registers suits workflows such as marking late arrivals later; then the API needs explicit "not marked" handling in reports. The SMS requires complete registers and allows corrections by sending the register again.
Weigh data quality against flexibility, describe what reports must do in each design, and say how corrections work.
AAttendance grows by 10,000 rows a day across many schools. How would you keep it fast and manageable for years?design
Index for the main access paths (date; enrollment and date), keep reports aggregated in SQL, pre-compute daily and monthly summaries in a summary table updated after each register, archive or partition old years by date, and serve dashboards from the summaries instead of the raw rows.
Estimate the growth, then cover indexing, summary tables, partitioning (SQL Extras), archiving, and how reports stay correct when summaries are updated.
FHow would you design REST routes for entering exam marks in a school system?design
Give each paper (one subject's exam for one class) its own address and treat a section's marks sheet as one resource: GET and PUT /api/exam-papers/{paperId}/marks?sectionId=19. Exams and their papers are read with GET /api/exams and /api/exams/{examId}/papers?classId=10, and a report card with GET /api/students/{admissionNumber}/report-cards/{examId}.
See the example · Lesson 82: Exams and report cards · Permalink
FWhy should an absent student not simply get 0 marks?concept
Absent and a score of 0 mean different things, and reports must show the difference. The SMS stores IsAbsent = true with null marks; the report card shows "Absent", and a line must have marks or absent, never both (error 50053).
See the example · Lesson 82: Exams and report cards · Permalink
FWhen does the SMS report card show a percentage and a final result?concept
Only when every paper of the exam has been entered. Until then the result is "Incomplete" and the percentage is null, because a missing paper would make the percentage look higher. A complete card is "Pass" only if every paper reached its pass marks.
See the example · Lesson 82: Exams and report cards · Permalink
MWhen should a validation check move into a shared class?design
When a second feature needs exactly the same rule. In the SMS, the register check (every student once, nobody else) was written for attendance; marks sheets needed it too, so it moved to RegisterCheck, and both services call it. Sharing earlier, with only one user, often guesses the wrong shape.
See the example · Lesson 82: Exams and report cards · Permalink
MWhy keep report card rules in a service instead of in the SQL query?design
The query only returns what is stored: each paper and the student's mark if there is one. The service decides Pass, Fail, Absent, grades, and the result. Rules change between schools and boards, and in C# they can be unit tested without a database, as the SMS report card tests do.
See the example · Lesson 82: Exams and report cards · Permalink
MA database trigger raises THROW 50001 for invalid marks. How should the API handle it?coding
Catch the SqlException with Number 50001 in the repository and throw the same SchoolRuleException 50001 that the service raises for the same rule. The exception handler then answers 422 with errorCode 50001, whether the C# check or the database caught the problem. Without this, the client gets a 500.
See the example · Lesson 82: Exams and report cards · Permalink
SPrinted report cards showed percentages that were too high for some students. How do you investigate and prevent it?scenario
Compare a wrong card with its marks: the usual cause is a percentage calculated from the papers entered so far while some papers were still missing. Fix the rule (no percentage, result "Incomplete", until every paper is entered), add tests for incomplete cards, and give the office a list of missing papers before printing. Reprint the affected cards.
Reproduce with real data, name the rule that was wrong, fix it with a test, and add a process check so incomplete data cannot reach print again.
See the example · Lesson 82: Exams and report cards · Permalink
SShould the 'marks within the maximum' rule live in the API, the database, or both?design
Both. The API checks first and gives a clear message for the user. The database trigger is the final guard for every program that writes marks, such as imports or scripts. Both use error number 50001, and the repository translates the database's error, so clients see one consistent error.
Explain defence in depth, who writes to the database besides the API, and how to keep both checks consistent for clients.
See the example · Lesson 82: Exams and report cards · Permalink
AEach school in a group uses different grade scales and pass rules. How would you design the report card logic?design
Keep the data query the same and make the rules configurable per school: grade scales as data or as strategies (the IGradeScale idea from Lesson 56), pass marks per paper, and result rules per board. Version the rules so old report cards can be reproduced exactly, cache them per school, and cover each school's rules with tests built from real past results.
Separate stored data from rules, show how rules vary per school, and cover versioning, reproducibility, and testing.
See the example · Lesson 82: Exams and report cards · Permalink
FWhat is set-based SQL, and why use it to create invoices for a whole class?concept
Set-based SQL handles many rows in one statement, such as INSERT ... SELECT, instead of one statement per row in a loop. The SMS bills a class of 1,000 students with two inserts and a summary in one transaction and one round trip, in about 0.3 seconds, instead of about 4,000 separate statements.
FWhat is a SQL Server sequence, and why use one for invoice numbers?concept
A sequence is a database object that hands out the next number each time NEXT VALUE FOR is called, safely across many users at once. MAX(number) + 1 can give two requests the same number; a sequence never does. The SMS formats the value as INV-2026-NNNNNN.
FWhy are there gaps in invoice numbers, and is that a problem?concept
A sequence never takes a number back, even when the transaction that used it rolls back or the row is later deleted. In the SMS, test runs used 2,000 numbers, so the next real invoice was INV-2026-002013. Gaps are normal; what must never happen is a duplicate, which the unique constraint on InvoiceNumber prevents.
MHow do you make a batch endpoint, such as 'bill this class', safe to run twice?design
Let the batch skip what is already done and report it. The SMS insert uses NOT EXISTS to skip students who already have an active invoice; the second run reported 0 created and 1,006 already invoiced. A unique index (one active invoice per enrollment) is the final guard.
MHow do you add an item row for each new invoice created by an INSERT ... SELECT?coding
Capture the new rows with OUTPUT INSERTED.Id INTO a table variable, then insert the items with INSERT ... SELECT from that table variable joined to the fee lines (a CROSS JOIN, since every invoice gets every line). This works because FeeInvoices has no trigger; on a table with triggers, OUTPUT must use INTO, as here.
MHow do you build a paged report that also shows totals for everything, in one round trip?coding
Send two statements with the same FROM and WHERE: one returns COUNT and SUM over the whole filter, the other returns one page with ORDER BY and OFFSET/FETCH. Read both with Dapper's QueryMultiple. Totals computed from the page alone would change with the page size.
SA server restart halfway through billing left half a class with invoices; a retry then billed the first half twice. How do you fix it?scenario
Repair first: find enrollments with two active invoices and cancel the duplicates, with an audit note. Then fix the design: bill the class in one set-based transaction, so a failure leaves nothing half-done; skip students already billed, so a retry is harmless; and add a filtered unique index so the database refuses a second active invoice.
Separate the data repair from the design fix, and explain how atomicity, idempotency, and a database constraint each prevent a part of the problem.
STwo clerks click 'Generate invoices' for the same class at the same moment. What happens in your design?scenario
Each run first takes an application lock for the class (sp_getapplock), so the second clerk waits until the first commits, then its NOT EXISTS check finds every student already billed and creates nothing. If anything slipped past, the filtered unique index would refuse the duplicate and the API would answer 409 (50017). The first SMS version used UPDLOCK, HOLDLOCK on the check instead, and under load two runs could deadlock (error 1205); the application lock removed that.
Walk through both transactions step by step, name the lock and the constraint, and say what the second clerk sees.
ABilling must run for 1,000 schools at the start of each term. How would you design it?design
Run billing as background jobs per school and class, not inside web requests. Each job is set-based, one transaction, and idempotent, so failed jobs can simply be retried. Record each run (who, when, counts) for audit, spread the load over time, give each school its own number series, and notify the office when its run is done. Monitor failures and totals per school.
Cover moving work off the request path, idempotent retries, data partitioning per school, numbering, auditing, and monitoring.
FWhy build a dashboard endpoint instead of letting the screen call the existing endpoints?design
One request is quicker than six, especially on phones and slow networks, and it is less work for the server. The endpoint is shaped by the screen, not by the tables. The SMS keeps the detailed endpoints for their own screens and adds GET /api/dashboard/summary for the first screen.
FWhat does Dapper's QueryMultiple do, and what must you be careful about?coding
It sends several SQL statements in one round trip and returns a reader for their results. You read each result in the same order as the statements; reading out of order maps the wrong rows. The SMS dashboard reads five results this way.
FWhat is a covering index?concept
An index that holds every column a query needs, so SQL Server reads only the index and never the table. INCLUDE adds columns to the index without making them part of the key. In the SMS, adding EnrollmentId to the attendance date index cut one day's count from 1,606 pages read to 44.
MHow do you find which statement in a SQL batch is slow?coding
Run it with SET STATISTICS TIME ON to get the CPU and elapsed time of each statement, and SET STATISTICS IO ON to see how many pages each one reads from each table. The SMS dashboard's attendance statement took 126 ms and read 1,606 pages, which pointed straight at a missing index column.
MAn index includes Status, yet a query on that table still reads the table. Why?scenario
The query needs another column the index does not hold. In the SMS, the dashboard joins each attendance record to its enrollment, so it needs EnrollmentId as well as Status. Rebuilding the index with INCLUDE (EnrollmentId, Status) and DROP_EXISTING = ON made it cover the query.
MWhat does [ResponseCache(Duration = 60)] do on an ASP.NET Core action?concept
It adds the header Cache-Control: public,max-age=60, so browsers and proxies may reuse the response for a minute. It does not cache anything on the server by itself. It suits numbers such as a dashboard that may safely be a little old.
SYou added an index to speed up a query, but the timings did not change at all. What do you check?scenario
Check that the change was really made: in the SMS, the script's re-run check looked for a column that was already included, so it skipped the rebuild. Then read the index definition from sys.index_columns, and compare pages read with STATISTICS IO before and after. Only trust a fix once the numbers move.
Start from verifying the change itself, then measure with the right tools, and explain how you would prevent a silent no-op in future scripts.
SA dashboard test fails sometimes when the whole suite runs, but never alone. What is likely wrong?scenario
It depends on data that other tests change at the same time: test classes run in parallel. In the SMS, fee totals change while FeeBillingTests creates invoices, so the dashboard test checks only numbers that no other test changes. Alternatives are separate test data, a collection that runs tests one after another, or a database per test run.
Explain parallel test execution, shared data, and the options to isolate tests, with their costs.
AThe dashboard is opened by thousands of users across many schools. How would you keep it fast?design
Keep one round trip per dashboard and covering indexes for its queries. Cache the result per school for a short time on the server (memory or a distributed cache), refreshed in the background. Pre-compute heavy numbers such as daily attendance into summary tables. Send Cache-Control so clients reuse answers, and watch query times per school in monitoring.
Layer the answer: query efficiency, server-side caching with clear freshness rules, pre-computation, and measurement in production.
Data access in the API
FHow can you switch an API's data access from EF Core to Dapper without changing the controllers?concept
Keep data access behind interfaces such as IStudentRepository, and let one registration method decide the classes. Controllers and services only ask for the interfaces. In the SMS, AddSchoolData changed from the EfCore classes to the Dapper classes, and no controller changed.
See the example · Lesson 81: Switching to Dapper · Permalink
FEF Core had a soft-delete query filter. What must you do instead when you use Dapper?concept
Write the condition yourself, such as IsDeleted = 0, in every query that reads the table, including joins and counts. Dapper runs exactly the SQL you write. A test with one removed student catches a forgotten condition.
See the example · Lesson 81: Switching to Dapper · Permalink
FWhy might a team choose Dapper for an API instead of EF Core?concept
Dapper runs SQL that the team writes and can read, so queries are exact and easy to review with a database team, and its overhead is very small. EF Core gives change tracking, LINQ, migrations, and less code for simple writes. Many teams use EF Core for writes and Dapper for heavy reads; the SMS chose Dapper for the whole API.
See the example · Lesson 81: Switching to Dapper · Permalink
MDapper threw SqlException 547 when saving an attendance status. What went wrong?scenario
The enum AttendanceStatus was passed as it is, and Dapper sends an enum's number (Late is 2). The
Status column holds names, and CK_AttendanceRecords_Status refused the number. Pass Status.ToString().
EF Core did this conversion for us with HasConversion<string>().
See the example · Lesson 81: Switching to Dapper · Permalink
MHow do you save several related changes together with Dapper?coding
Open the connection, start a SqlTransaction, pass the transaction to every Dapper call, and commit at the end. If anything throws, disposing the transaction without committing rolls everything back. In the SMS, the invoice update and the new payment, and the attendance updates and inserts, each use one transaction.
See the example · Lesson 81: Switching to Dapper · Permalink
MHow do you implement optimistic concurrency with a RowVersion column in Dapper?coding
Read the RowVersion with the row. Update with WHERE Id = @Id AND RowVersion = @RowVersion, and read the new value with OUTPUT INSERTED.RowVersion. If no row comes back, someone else saved first: roll back and report a conflict. The SMS reports it as error 50014, just as the EF Core version did.
See the example · Lesson 81: Switching to Dapper · Permalink
SAfter switching to Dapper, a payment is saved but the API answers 500 with a NullReferenceException. How do you find and fix it?scenario
Use the traceId to find the log entry and its stack trace. In the SMS it pointed to the mapping code, which read payment.FeeInvoice. EF Core's relationship fix-up had always filled that link; Dapper does not. The right fix is in the entity: RecordPayment sets FeeInvoice = this, so the object model is correct whichever tool loads it. Then add a test that checks the link.
Go from symptom to log to cause, explain the hidden ORM behaviour, and say why the fix belongs in the domain model rather than the repository, with a test to keep it fixed.
See the example · Lesson 81: Switching to Dapper · Permalink
SHow would you prove that replacing a data-access layer did not change the API's behaviour?design
Run the same tests against both implementations (the SMS enrollment tests run once per tool, including the lock test). Call the same requests on the old and new builds and diff the responses; the SMS compared 20 GET answers and found no difference. Exercise every write path, then compare timings. Keep the old implementation until all of this passes.
Cover automated tests, side-by-side comparison of real responses, write paths, performance, and a safe rollout with the old code still available.
See the example · Lesson 81: Switching to Dapper · Permalink
AYour company's APIs use EF Core, and one team wants Dapper. How would you set a data-access standard across many teams?design
Agree what must be true whatever the tool: repositories behind interfaces, one transaction per business change, concurrency checks, soft-delete rules, parameterised SQL, and integration tests against a real database. Allow EF Core for most CRUD and Dapper where SQL control or speed matters, decided per service and recorded. Provide shared helpers (connection factory, transaction helper, row-to-DTO patterns) and review checklists for the things EF Core did implicitly. Measure before switching for speed.
Separate the non-negotiable rules from the tool choice, give criteria for choosing, and explain how you support teams and enforce the rules.
See the example · Lesson 81: Switching to Dapper · Permalink
Filters
FWhat is a filter in ASP.NET Core MVC?concept
Code that runs before and after controller actions, inside MVC, for jobs many actions share. Filters can read the action, its attributes, and its bound arguments, and can answer the request themselves. The SMS uses a filter for read-only mode and another for checking date ranges.
FIn what order do the filter types run?concept
Authorization filters, then resource filters, then model binding and validation, then action filters around the action, then exception filters if it throws, then result filters around writing the result. Resource filters also run again at the very end, on the way back.
FWhen would you use middleware instead of a filter?concept
When the job applies to every request, whatever happens next, including requests for files or unknown routes, which never reach a filter. Use a filter when the job depends on which action runs or on its bound arguments. In the SMS, request timing is middleware; read-only mode and date checks are filters.
MHow does a filter stop a request from reaching the action?coding
It sets context.Result (or, in an async filter, does not call next). That result becomes the response, and the action and the later filters never run. The SMS's ReadOnlyModeFilter sets a 503 ApiResponse this way.
MWhy might a filter answer 503 to a request whose body is invalid, instead of 400?scenario
It is a resource filter, which runs before model binding and validation. In the SMS read-only mode, an empty admission request got 503 because the filter refused it before validation ever read the body. An action filter would run after validation, and the client would get 400 first.
MAn attribute filter needs a service from dependency injection. How do you supply it?coding
Attribute filters are created by .NET, not by dependency injection, so their constructors cannot ask for services. Use [ServiceFilter(typeof(MyFilter))] (the filter is registered and taken from DI) or [TypeFilter(typeof(MyFilter))] (created with services filled in), or register the filter globally with options.Filters.Add of the type, as the SMS does for ReadOnlyModeFilter.
SThe school must stop all changes for two days at year end, but parents must still read report cards. How do you build it?design
A global resource filter that refuses every non-GET request with 503 and Retry-After while a setting is on, with an attribute to exempt actions such as sign-in. Read the setting with IOptionsMonitor, so the office can switch it without a restart or deployment. Keep the message clear, and test both modes.
Choose the mechanism (a resource filter), explain why it runs before binding, how the switch changes at run time, which actions are exempt, and what status and headers clients get.
SYou changed a setting in appsettings.json while the API was running, but a filter still uses the old value. Why?scenario
The filter reads the setting through IOptions, which binds the value once. IOptionsMonitor.CurrentValue (or IOptionsSnapshot per request) sees reloaded configuration. Also check the file is the one the app actually reads (the content root) and that reloading is on, which is the default for appsettings.json.
Name the three options interfaces and when each sees changes, and how you would verify the reload.
APayments are sometimes recorded twice when a phone app retries after a timeout. How would you design idempotency for the API?design
Clients send an Idempotency-Key header with each payment. A resource filter (or middleware) stores the key with the response in a shared store (database or distributed cache) for a limited time, and when the same key arrives again it replays the stored response instead of running the action. Store keys per user, handle two identical requests arriving at once with a unique constraint or lock, and reject a reused key with a different body.
Cover the client contract, where keys and responses are stored, concurrent duplicates, expiry, and how it works across several servers.
Logging
FWhat are log levels, and when would you use Warning instead of Error?concept
Levels rank how important an entry is: Trace, Debug, Information, Warning, Error, Critical. Warning is for something unusual that was handled, such as a school rule refusing a request or a slow request; Error is for a failure the user saw, such as an unhandled exception. Settings choose the minimum level per category.
See the example · Lesson 90: Logging and trace ids · Permalink
FWhat is structured logging?concept
Writing log entries from a message template with named holes, such as "Payment {ReceiptNumber} recorded", so each value is kept as a separate property. Log systems can then search and count by property (ReceiptNumber = RCP-2026-000069) instead of parsing text.
See the example · Lesson 90: Logging and trace ids · Permalink
FWhat should never be written to logs?concept
Passwords, access tokens, signing keys, and personal data such as names, birthdays, phone numbers, and addresses, and whole request or response bodies that may contain them. Logs are read by more people and kept in more places than the database. The SMS logs ids only.
See the example · Lesson 90: Logging and trace ids · Permalink
MWhy is logger.LogInformation($"Payment {receipt}") a bad habit?coding
String interpolation turns the value into plain text, so it cannot be searched as a property, and the string is built even when the level is turned off. Use a message template, LogInformation("Payment {ReceiptNumber}", receipt), or a [LoggerMessage] method.
See the example · Lesson 90: Logging and trace ids · Permalink
MWhat does the [LoggerMessage] attribute give you?coding
The compiler generates the body of a static partial logging method: fast (no boxing, the template parsed once), typed (a missing argument is a build error), with a fixed event id and level. The SMS keeps every deliberate message in one SchoolLog class this way.
See the example · Lesson 90: Logging and trace ids · Permalink
MHow do you add the current user to every log entry of a request?coding
Open a logging scope after authentication, for example in a small middleware: logger.BeginScope("UserId:{UserId}", userId). Every entry written inside the scope carries UserId, and the JSON formatter stores it as a property. Code that runs outside the scope, such as the exception handler, must add the user to its own message.
See the example · Lesson 90: Logging and trace ids · Permalink
SA parent sends a screenshot of an error with a trace id. How do you find what happened?scenario
Search the logs for the trace id. In ASP.NET Core it is the middle part of the response's traceId
(00-<TraceId>-<SpanId>-00), and every log entry of that request carries it as a scope. The matching
entries show the request path, the user, the rule or exception, and the timing.
Explain how the trace id links the response to the logs, what the entries contain, and why logs must include scopes and be searchable for this to work.
See the example · Lesson 90: Logging and trace ids · Permalink
SThe team finds parents' phone numbers and passwords in the API logs. What do you do?scenario
Stop the leak first: remove request-body logging and any log line that includes personal data, and deploy. Then treat the stored logs as an incident: restrict access, delete or redact them according to policy, and ask affected users to change passwords. Prevent it returning with deliberate log messages that take ids only, and tests that check sensitive text never appears in logs.
Order the response: contain, clean up existing data, notify, then prevent with design and tests.
See the example · Lesson 90: Logging and trace ids · Permalink
AHow would you design observability for an API that runs on many servers for many schools?design
Structured JSON logs with fixed event ids, sent to a central store; distributed traces with the same trace ids across services (OpenTelemetry); and metrics such as request rates, error rates, and response times per endpoint and school. Add the school and user ids as scopes, alert on symptoms (error rate, slow requests), keep personal data out, and set retention and access rules for each kind of data.
Cover the three signals (logs, traces, metrics), how they are linked, what you alert on, and the privacy and cost limits.
See the example · Lesson 90: Logging and trace ids · Permalink
Caching
FWhat is a cache, and what data is worth caching?concept
A cache keeps a copy of an answer close by so the next request does not ask the source again. Cache data that is read far more often than it changes and that may safely be a little old, such as the current academic year or the list of subjects. Never cache data that must be exact, such as a fee balance.
See the example · Lesson 91: Server-side caching · Permalink
FWhat is the difference between [ResponseCache] and a server-side cache?concept
[ResponseCache] sends a Cache-Control header so each browser or proxy may keep its own copy; the server cannot take it back early. A server-side cache such as HybridCache keeps the copy in the API's memory, serves every client from it, and can remove entries when data changes. They work well together.
See the example · Lesson 91: Server-side caching · Permalink
FWhat makes a good cache key?concept
It names everything the answer depends on, and nothing else, so two different questions never share an answer. The SMS dashboard key is dashboard:{yearId}:{date}; leaving out the date would return one day's numbers for another. Per-user answers need the user in the key, or should not be cached.
See the example · Lesson 91: Server-side caching · Permalink
MHow does HybridCache.GetOrCreateAsync behave, and what is stampede protection?concept
It returns the cached value for a key, or runs the given function once, stores its result, and returns it. Stampede protection means that when many requests ask for the same missing key at once, the function runs only once and all of them share the result, instead of all hitting the database together.
See the example · Lesson 91: Server-side caching · Permalink
MHow do you add caching without changing the code that uses a repository?design
Write a decorator: a class that implements the same interface, holds the real repository, answers reads from the cache, and passes everything else through. Register it in place of the real one. In the SMS, CachedMasterDataRepository wraps the Dapper repository, and no controller or service changed.
See the example · Lesson 91: Server-side caching · Permalink
MHow do you remove cached answers when the data changes?coding
Give related entries a tag when you store them, and remove the tag in the same code path that changes the data. In the SMS, every subject entry carries the tag "subjects", and AddSubjectAsync calls RemoveByTagAsync("subjects"), so the new subject appears at once instead of after an hour.
See the example · Lesson 91: Server-side caching · Permalink
SParents see an old fee balance in the app after paying. What went wrong, and what do you change?scenario
The balance was cached, so the app showed the copy from before the payment. Money and other exact, per-person data should not be cached; if a cache is truly needed, remove the student's entry in the payment code path and keep expiry very short. Review every cached item with the question: what harm does the old version cause?
Identify the stale cache as the cause, decide whether the data should be cached at all, and describe invalidation and review rules.
See the example · Lesson 91: Server-side caching · Permalink
SThe API runs on three servers with in-memory caches. A subject is added on one of them. What happens?scenario
Only that server removes its cached subjects; the other two keep their copies until they expire, so users may see different lists for up to an hour. Options: shorter expiry, a shared distributed cache (such as Redis as HybridCache's second level), or broadcasting removals to every server.
Explain per-server memory caches, the visible effect, and compare the fixes by freshness, cost, and complexity.
See the example · Lesson 91: Server-side caching · Permalink
AHow would you design caching for an API that serves 1,000 schools?design
Put the school in every key (and tag by school), so no school ever sees another's data. Use a two-level cache: memory on each server for speed, plus a shared distributed cache for consistency and warm restarts. Cache master data and computed dashboards with expiry matched to how often each changes, remove tags in the code that changes data, protect against stampedes, and watch hit rates and memory per school.
Cover isolation between tenants, the cache levels, what is cached and for how long, invalidation, and monitoring.
See the example · Lesson 91: Server-side caching · Permalink
Versioning
FWhat is a breaking change in an API? Give examples.concept
A change that makes existing clients fail or misread data: removing or renaming a response field, changing a field's type or meaning, making an optional request field required, or changing a status code. Adding a field, an endpoint, or an optional request field is not breaking, because old clients ignore it.
FWhat are the common ways to put a version in an API request?concept
In the URL path (/api/v2/students/...), in a header (X-Api-Version: 2), or in the query string (?api-version=2.0). URL versions are visible and easy to test and cache; headers keep URLs clean but are invisible in links and logs. The SMS uses the URL segment, and the key is to use one style everywhere.
FHow do existing apps keep working when you add versioning to an API that had none?concept
Make unversioned requests mean version 1. In Asp.Versioning, set DefaultApiVersion to 1.0 and AssumeDefaultVersionWhenUnspecified to true. In the SMS, /api/students/... still answers exactly as before, and /api/v1/... is the same endpoint with the version named.
MHow do you serve two versions of the same endpoint in ASP.NET Core controllers?coding
Add Asp.Versioning.Mvc, mark the existing controller [ApiVersion("1.0")], and add a new controller marked [ApiVersion("2.0")] with a route such as api/v{version:apiVersion}/students. Give both the same [ControllerName] so the package groups them and reports both versions.
MWhy might api-supported-versions show only one version on each of two versioned controllers?scenario
Asp.Versioning groups versions by controller name. A StudentsController (1.0) and a StudentsV2Controller (2.0) look like two different APIs, so each reports only its own version. [ControllerName("Students")] on the version 2 class makes both report 1.0, 2.0.
MWhen should you add a new API version instead of changing the existing one?design
Only for breaking changes. If the change can be additive, such as adding firstName and lastName next to fullName, do that: old clients keep working and there is one version to maintain. Add a version when a field must be removed, renamed, or change meaning, or a response must be reshaped.
SA field was renamed in the API and every installed mobile app now shows blank names. What do you do?scenario
Restore the old field at once (or serve the old shape again) so installed apps work. Move the rename into a new version that new app releases use. Add a rule and a review step: no field is removed or renamed without a new version and a deprecation date, and add tests that pin the version 1 response shape.
Put users first (restore), then do the change properly (versioning), then prevent it (rules and contract tests).
SHow do you retire an old API version safely?design
Mark it deprecated (so responses list it in api-deprecated-versions) and publish a date; log which version each request uses and contact the clients still on it; after the date, remove it and answer its routes with a clear error. For a school, one full academic year is a sensible notice period.
Cover announcing, measuring usage, communicating, and the final removal, with a realistic timeline.
AYour platform has many APIs used by internal and partner apps. How would you set a versioning policy?design
One style for all APIs, semantic rules for what counts as breaking, and a preference for additive changes. Breaking changes get a new major version with a published support period (for example, at least 12 months), deprecation headers, per-version documentation, and usage dashboards per client. Contract tests guard each supported version, and an API gateway can route versions to different services if needed.
Cover the style, the definition of breaking, support periods, communication, documentation, enforcement with tests, and how clients are tracked.
Background jobs
FWhat is a hosted service in ASP.NET Core, and what is BackgroundService?concept
A hosted service is a class the app starts when it starts and stops when it stops (IHostedService, registered with AddHostedService). BackgroundService is a base class for one: you override ExecuteAsync, which runs in the background for the life of the app, and its stoppingToken is cancelled at shutdown.
FWhich work should move out of a request into the background?concept
Work the user's next step does not depend on, especially slow work or work that calls another service: sending messages, emails, and reports. In the SMS, saving a payment stays in the request; the receipt message is queued. Moving it also stops a slow or failing provider from failing the payment itself.
MHow do you pass work from a controller to a background worker in ASP.NET Core?coding
Register a queue as a singleton, for example a bounded Channel of messages. The controller writes a message and returns at once; a BackgroundService reads the channel with ReadAllAsync and processes each message. Bounded with FullMode.Wait means a flood of work waits for room instead of using up memory.
MYour BackgroundService needs a scoped repository. How do you use it?coding
Not through the constructor: a hosted service is a singleton, and the app refuses to start with "Cannot consume scoped service ... from singleton IHostedService". Inject IServiceScopeFactory, create a scope for each piece of work (CreateAsyncScope), and resolve the repository from that scope; disposing the scope cleans it up.
MWhat happens if ExecuteAsync throws an unhandled exception, and how do you guard against it?scenario
Since .NET 6 the default BackgroundServiceExceptionBehavior is StopHost: the whole app shuts down, every endpoint included. Catch exceptions inside the loop (except cancellation at shutdown), log them, and carry on with the next message or the next scheduled run.
MHow would you write and test a job that must run every day at 18:00?coding
A BackgroundService loop: get the time from TimeProvider, work out the next 18:00, await Task.Delay(next - now, timeProvider, stoppingToken), run, repeat. Because the clock is injected, a test uses FakeTimeProvider: set it to 17:59, start the job, Advance one minute, and check the job ran, without waiting.
SThe API runs on two servers and both run the evening reminder job. How do you stop duplicate reminders?scenario
Let the database decide. Record each reminder in a table with a unique key on (invoice, date) before sending, and send only if the insert succeeded; the second server gets a duplicate-key error and skips that invoice. Alternatives are a distributed lock (such as sp_getapplock) or a scheduler that runs each job on one server.
Name the problem (every instance runs every hosted service), then give an idempotent design and say why it needs no coordination between servers.
SWhy is an in-memory queue risky, and what is the outbox pattern?design
Messages waiting in memory are lost when the app stops or crashes. With an outbox, the message is saved in a table in the same transaction as the business change (such as the payment); a worker reads unsent rows, sends them, and marks them sent. Nothing is lost on restart, but sending is at least once, so receivers must handle duplicates.
Explain what is lost and when, describe the outbox steps, and mention the at-least-once trade-off.
AHow would you design background processing for a school platform that sends millions of messages a day?design
Separate the API from the workers: the API writes messages to an outbox table or a durable broker (Azure Service Bus, RabbitMQ), and a scalable pool of worker services sends them. Add retries with backoff, a dead-letter queue for messages that keep failing, idempotency keys so retries never double-send, rate limits per provider, and dashboards for queue length and failures. Schedules run in one place (Hangfire, Quartz.NET, or a cloud scheduler) so each job runs once.
Cover durability, scaling the workers, retries and dead letters, idempotency, scheduling, and monitoring.
FWhen should an endpoint answer 202 Accepted instead of 200 OK?concept
When it has accepted the work but not finished it, because the work continues in the background. 200 says "done, here is the result"; 202 says "accepted, it will be done". In the SMS, POST /api/fee-reminders answers 202 with how many reminders were queued; the worker sends them afterwards.
Health checks
FWhat is a health check endpoint, and who calls it?concept
An address, such as /health/ready, where the API reports whether it is working. Load balancers call it every few seconds to decide which servers get traffic, container platforms use it to restart or pause instances, and monitoring tools use it to alert the team.
FWhat is the difference between a liveness check and a readiness check?concept
Liveness asks "is the process running?" and failing it usually means restart the process. Readiness asks "can it do its work now?" (database reachable, workers running) and failing it means send no traffic until it recovers. A database problem belongs in readiness, because restarting the API does not fix the database.
FWhat do Healthy, Degraded, and Unhealthy mean, and which HTTP codes do they return?concept
Healthy: everything works (200). Degraded: it works but needs attention, such as a growing queue (200, so traffic keeps flowing). Unhealthy: it cannot do its job, such as the database being unreachable (503).
MHow do you write a custom health check for SQL Server in ASP.NET Core?coding
Implement IHealthCheck: open a connection, run SELECT 1, return HealthCheckResult.Healthy; catch exceptions (except cancellation) and return Unhealthy with the exception, so it is logged. Register it with AddHealthChecks().AddCheck("database", tags: ["ready"], timeout: ...) and map it with MapHealthChecks.
MYour health endpoint returns 401 behind the load balancer and every server is marked down. Why, and what do you change?scenario
A fallback authorization policy requires a token on every endpoint, and the load balancer sends none. Mark the health endpoints AllowAnonymous, keep their body free of secrets and exception details, and also exclude them from rate limiting, because a 429 looks just as dead.
MHow can a health check tell that a BackgroundService has stopped?coding
BackgroundService.ExecuteTask is the running ExecuteAsync. Hosted services are singletons, so the check can ask for IEnumerable of IHostedService, find the worker, and report Unhealthy if ExecuteTask is completed while the app is running. Queue length and the last run's result can add Degraded warnings.
SThe database failed over for 30 seconds and every API instance restarted, causing a longer outage. What went wrong?scenario
The database check was in the liveness probe, so the platform restarted every instance, and they were still starting when the database returned. Keep liveness trivial (the process answers) and put the database in readiness, which only removes instances from traffic. Add timeouts so a hanging check fails fast.
Separate the two probe types, explain what each one triggers, and show why restarts cannot fix a dependency.
SShould the readiness check include every external service the API uses?design
No. Include what the instance itself needs and can be fixed per instance, such as its database connection. A shared external service (an SMS provider, a payment gateway) failing would mark every instance unready at once and turn a partial problem into a full outage. Monitor those separately and degrade gracefully.
Classify dependencies as critical per instance versus shared, and explain the blast radius of each choice.
ADesign health checking and monitoring for an API running many instances across regions.design
Per instance: a trivial liveness probe and a readiness probe with fast, time-limited checks of its own dependencies, answering only a status publicly and details on an internal address. Per service: synthetic checks from outside that exercise real flows, dashboards of error rates and latency, and alerts with clear owners. Health results can also be pushed (IHealthCheckPublisher) to the monitoring system, and shared dependencies are watched centrally rather than in every probe.
Cover probe design, timeouts, security of details, external synthetic checks, metrics and alerting, and shared-dependency handling.
Testing the API
FWhat is the difference between unit tests, integration tests, and API tests?concept
Unit tests check one class with fakes instead of a database, and run in milliseconds. Integration tests check code together with a real dependency, such as SQL that runs against SchoolDb. API tests run the whole application in memory (WebApplicationFactory) and send real HTTP requests through routing, security, validation, and the database.
See the example · Lesson 96: Tests for the whole API · Permalink
FWhat is the test pyramid?concept
A guideline for the shape of a test suite: many fast unit tests at the bottom, fewer integration tests in the middle, and fewer still slow end-to-end or API tests at the top. Fast tests give quick feedback and pinpoint failures; the slower ones prove the parts work together.
See the example · Lesson 96: Tests for the whole API · Permalink
FWhat does code coverage measure, and what does it not tell you?concept
The share of code lines (or branches) that ran during the tests. It does not say the lines were checked: a test without asserts still covers code. Use it to find code no test touches, not as proof of quality.
See the example · Lesson 96: Tests for the whole API · Permalink
MHow can a test catch a missing dependency injection registration for any controller?coding
Find every controller type in the API assembly and create each one in a DI scope with ActivatorUtilities.CreateInstance, the way MVC does. Collect the exceptions and fail with their messages. It catches the mistake even though the app starts normally, because controllers are only created when a request reaches them.
See the example · Lesson 96: Tests for the whole API · Permalink
MHow would you test that no endpoint is accidentally open to anonymous users?coding
Read the endpoints from EndpointDataSource in a WebApplicationFactory, select those with IAllowAnonymous metadata, and compare them with an approved list in the test. With a fallback policy that requires sign-in, every other endpoint is locked; a newly opened one fails the test until someone approves it.
See the example · Lesson 96: Tests for the whole API · Permalink
MYour coverage jumped from 60% to 77% after a settings change, with no new tests. What do you check?scenario
The number of lines counted. Excluding CompilerGeneratedAttribute, for example, removes every async method's generated state machine, so most async code disappears from the report. Exclude only truly generated files (such as obj/), and treat any change in coverage without a change in tests as suspicious.
See the example · Lesson 96: Tests for the whole API · Permalink
SWhat is approval testing, and how can it protect a public API?design
The test produces output (here, the sorted list of routes with versions) and compares it with an approved file in source control. Any difference fails the test and writes a received file; if the change is intended, the developer replaces the approved file and the change appears in code review. It stops routes being removed or renamed by accident, which would break installed clients.
Define the technique, give a concrete use, and explain how it turns accidental changes into reviewed ones.
See the example · Lesson 96: Tests for the whole API · Permalink
SAPI tests share one real database. How do you keep them reliable?design
Each test creates its own data and removes it in finally, or works on data no other test changes; tests that touch the same rows share an xUnit collection so they never run at the same time. Avoid depending on today's date (inject TimeProvider) and on counts that other tests change. Alternatives are a transaction rolled back per test, a tool that resets tables (such as Respawn), or a fresh database container per run.
Cover isolation, cleanup, parallel runs, time, and the main reset strategies with their trade-offs.
See the example · Lesson 96: Tests for the whole API · Permalink
ADesign a testing strategy for a product team of 40 developers working on one large API.design
A pyramid with clear ownership: fast unit tests on every save, integration and API tests in CI on every pull request against containerised dependencies, and contract tests (approved routes, OpenAPI diffs, consumer contracts) to protect clients. Add whole-API safety nets for security and DI, coverage reports per pull request to spot untested changes, flaky-test tracking with quarantine, time budgets for the suite, and smoke tests after each deployment.
Cover layers and speed, CI gates, contracts with clients, security nets, coverage use, flaky tests, and post-deployment checks.
See the example · Lesson 96: Tests for the whole API · Permalink
CORS
FWhat is an origin, and when do two addresses have the same origin?concept
An origin is the scheme, host, and port of an address, such as http://localhost:5173. Two addresses have
the same origin only when all three match; the path does not count. A page on port 5173 calling an API on
port 5080 is a cross-origin call.
FWhat is CORS, and who enforces it?concept
Cross-Origin Resource Sharing. Browsers stop a page from reading answers from another origin unless the
server allows it with the Access-Control-Allow-Origin header. The browser enforces it; curl, Postman, and
other servers ignore it, so the API must still check tokens and permissions itself.
FA page shows 'Failed to fetch' but the same request works in Postman. How do you find out why?scenario
Open the browser's console and Network tab. A CORS refusal is explained in the console, for example "No 'Access-Control-Allow-Origin' header is present". The page's code only gets "Failed to fetch", on purpose, so the console is where the reason is.
MWhat is a preflight request, and when does the browser send one?concept
An OPTIONS request the browser sends before the real one, carrying Origin,
Access-Control-Request-Method, and Access-Control-Request-Headers. It is sent for requests that are not
simple: an Authorization header, a JSON body, or methods like PUT and DELETE. Without permission in the
answer, the real request is never sent.
MWhere does UseCors go in the ASP.NET Core pipeline, and what breaks if it is too late?coding
Before UseAuthentication and UseAuthorization. The preflight carries no token, so if authorization runs
first, the preflight gets 401 with no CORS headers and every browser call fails. In the SMS, moving it after
UseAuthorization turned the preflight's 204 into a 401.
MAn unknown website's preflight gets 204 from your API. Is the API open to it?scenario
No. ASP.NET Core answers the preflight with 204 either way. What matters is the missing
Access-Control-Allow-Origin header: without it the browser refuses, and the real request is never sent.
Test for the header, not the status code.
SHow would you configure CORS for an API used by several websites across environments?design
One named policy with exact origins read from configuration: empty by default, the local front end in Development, the real websites per server. Validate the origins at startup (no path, no trailing slash), allow only the methods and headers the front ends use, expose only the response headers they must read, set a preflight max age, and cover it with tests that send preflights from allowed and unknown origins.
Cover exact origins from configuration, startup validation, least privilege for methods and headers, exposed headers, caching, and tests.
SWhy is AllowAnyOrigin risky, and when is it acceptable?concept
It lets any website read the API's answers inside your users' browsers, which matters most when the browser sends credentials automatically, such as cookies; ASP.NET Core refuses to combine it with AllowCredentials. It is acceptable for truly public, read-only data with no credentials, such as a public book catalogue, in a separate policy on those endpoints only.
Explain who is protected, the credentials rule, and a narrow per-endpoint policy for public data.
AMany teams' front ends call your organisation's APIs. How do you keep CORS correct and safe at scale?design
Avoid cross-origin calls where you can: serve the front end and API under one origin through a gateway or reverse proxy, or use a backend-for-frontend. Where CORS is needed, manage the allowed origins centrally per environment, validate them in CI and at startup, keep policies identical across environments except for origins, and test preflights in the pipeline. Never treat CORS as authentication; tokens and authorization stay on every API.
Cover same-origin options, central origin management, validation, environment parity, tests, and the limits of CORS as a security control.
Calling other services
FWhy should you not create a new HttpClient for every request?concept
Each new HttpClient opens new network connections, and closed ones linger for a while, so a busy server can
run out of them. One HttpClient kept forever has the opposite problem: it never notices a changed DNS address.
IHttpClientFactory reuses connections and renews them every two minutes by default.
See the example · Extra 8: Calling other services · Permalink
FWhich HTTP failures are worth retrying, and which are not?concept
Retry errors that can pass: 5xx answers, 408, 429, timeouts, and broken connections. Do not retry 400, 401, 403, 404, 409, or 422: the request itself is wrong and will fail the same way again.
See the example · Extra 8: Calling other services · Permalink
FWhat is a typed client?coding
A class that takes an HttpClient in its constructor and knows one service's API, registered with
AddHttpClient<TInterface, TImplementation>(). The factory creates its HttpClient with the address, time
limits, and retries set in one place, and the rest of the app only sees the interface.
See the example · Extra 8: Calling other services · Permalink
MWhat are backoff and jitter, and why use both?concept
Backoff makes each wait longer than the last (about 1, 2, 4 seconds), so a struggling service gets time to recover. Jitter adds a random part to each wait, so many clients that failed together do not all retry at the same moment and overload the service again.
See the example · Extra 8: Calling other services · Permalink
MWhat is an idempotency key, and when do you need one?concept
A unique id for one operation, sent in a header such as Idempotency-Key, the same on every attempt. If the
service already handled that key, it returns the first result instead of doing the work again. You need it
whenever you retry a request that changes something, such as a POST that sends a text or takes a payment,
because a lost answer would otherwise cause a duplicate.
See the example · Extra 8: Calling other services · Permalink
MExplain the three states of a circuit breaker.concept
Closed: calls go through and failures are counted. Open: after too many failures, every call fails at once without touching the service, for a break period. Half-open: after the break, one trial call goes through; success closes the circuit, failure opens it again.
See the example · Extra 8: Calling other services · Permalink
SHow would you make an API's calls to a payment or messaging provider reliable?design
A typed client from IHttpClientFactory behind an interface; a time limit per attempt and a total limit;
retries only for transient errors, with backoff and jitter; an idempotency key on every changing request; a
circuit breaker; logs and metrics for retries and circuit events. Work that must not be lost goes into an
outbox table first and is sent by a background job that can retry later. Tests replace the network with a
scripted handler.
Cover the client, time limits, retry rules, idempotency, the circuit breaker, durable storage, observability, and testing.
See the example · Extra 8: Calling other services · Permalink
SYour retries work, but during a one-hour provider outage messages were lost. What do you change?scenario
Retries cover seconds, not hours. Store each message durably before sending, in an outbox table with a status, in the same transaction as the business change. A background job sends pending rows, marks them sent, and retries failures later with growing delays, keeping the idempotency key, and alerts after too many failures.
Separate short retries from durable delivery, then describe the outbox, its job, and monitoring.
See the example · Extra 8: Calling other services · Permalink
AMany services in your company call each other. How do you keep failures from spreading?design
Give every call a time budget shorter than the caller's own, and pass deadlines along. Retry only at one layer, so retries do not multiply through the chain, and cap them with retry budgets. Use circuit breakers and bulkheads (separate limits per dependency), idempotency keys for changing calls, and asynchronous messaging with durable queues where an immediate answer is not needed. Keep shared dependencies out of readiness checks, and watch retry and circuit metrics on dashboards.
Cover time budgets, where to retry, retry budgets, circuit breakers and bulkheads, idempotency, async messaging, health checks, and monitoring.
See the example · Extra 8: Calling other services · Permalink
Files
FHow does an ASP.NET Core controller receive an uploaded file?coding
The client sends a multipart form, and the file part arrives as an IFormFile parameter whose name matches
the form field. IFormFile gives the length, the claimed file name and content type, and OpenReadStream()
to read the bytes.
FWhich parts of an uploaded file can you trust?concept
Only what actually arrives: its size, and its content after you check it. The file name, extension, and content type are chosen by the user's computer, so never use the name to save the file, and check the content itself, for example the header row of a CSV or the first bytes of a picture.
FHow do you make the browser download an API's answer as a file?coding
Return the bytes with the right content type and a Content-Disposition: attachment; filename=... header.
In ASP.NET Core, File(...), PhysicalFile(...), or Results.Stream(..., contentType, fileDownloadName) set
it from the file name you pass.
MHow do you limit the size of uploads, and what does the client get when a file is too big?coding
[RequestSizeLimit] on the action (or Kestrel's MaxRequestBodySize) stops reading beyond the limit, and form
options limit multipart bodies. In an MVC action, the failed form read becomes a 400 "Request body too large"
before the action runs. Check IFormFile.Length in code as well, since a test server may not enforce the limit.
MWhy might Validator.TryValidateObject miss the rules of a request record?concept
For a positional record, ASP.NET Core requires validation attributes on the constructor parameters, and
throws if they are on the properties. Validator reads only properties, so it does not see them. Outside MVC
you must read the parameters' attributes yourself, as the SMS's RecordValidator does, or validate differently.
MHow do you stream a large download instead of building it in memory?coding
Read rows one at a time (for example Dapper's QueryUnbufferedAsync as an IAsyncEnumerable) and write each
to the response stream as it arrives, for example inside Results.Stream. The response goes out chunked,
memory stays flat, and the client starts receiving data at once.
SDesign a CSV import of thousands of rows that must be safe to repeat and must never save half a file.design
Check the file (size, header), then every row with the same rules as the single-record endpoint, collecting errors by row. Bulk-copy valid rows into a staging table, check them against existing data, and apply one MERGE in a single transaction; any problem rolls everything back and returns every error. Matching on a natural key makes a repeated upload change nothing. Return counts of added, updated, and unchanged rows.
Cover the two stages of checks, error reporting by row, staging plus bulk copy, one transaction, the natural key for idempotency, and the summary.
SWhat security risks do file uploads and CSV exports bring, and how do you handle them?concept
Uploads: huge files (limit size), path traversal through file names (never use them; generate names), lying
types (check content and signatures), malware (scan files that are stored or shared), and public folders
(store outside them and serve through an endpoint that checks permissions). Exports: CSV injection, where a
value starting with =, +, -, or @ runs as a formula; neutralise such values. Both need authorization.
Split upload and export risks, and give a defence for each.
AUsers upload large files (videos, scans) to your platform. How would you design it?design
Do not stream large files through the API servers. The API authorises the upload and returns a short-lived signed URL to object storage; the client uploads directly, in resumable chunks. A storage event triggers background processing: virus scan, type check by content, thumbnails or conversion, then the metadata row is marked ready. Downloads use signed URLs or a CDN. Set size and type limits, quotas per tenant, retention rules, and encryption at rest.
Cover direct-to-storage uploads with signed URLs, resumable chunks, asynchronous processing and scanning, metadata, downloads through a CDN, limits and quotas, and data protection.
Security
Users and passwords
MWhy do we store a password hash instead of the password, and how is a hash different from encryption?concept
A hash is a one-way function: easy to compute from the password, practically impossible to reverse. At login, the app hashes the typed password and compares hashes, so the real password is never stored. Encryption can be reversed with a key, so a stolen key exposes every password.
See the example · Lesson 14: Fees, users, audit, soft delete · Permalink
AHow would you design roles and permissions so they can grow with the system?design
Start with users, roles, and a user-role junction, as in the SMS. When permissions get finer, add a permissions table and a role-permission junction, and check permissions rather than role names in code. Data rules, such as "a parent sees only their children", are checked separately from roles.
Describe role-based access control and its limits, then the step to permission-based checks. Use SMS examples: Teacher and Accountant roles, and the parent-sees-own-child rule as a data-level check. Mention the trade-off: more flexibility means more admin screens and more testing of who can do what.
See the example · Lesson 14: Fees, users, audit, soft delete · Permalink
Authentication
FWhat is the difference between authentication and authorization, and between 401 and 403?concept
Authentication answers "who are you?"; authorization answers "are you allowed to do this?". A request without a valid token fails authentication and gets 401 Unauthorized. A signed-in user who may not do something fails authorization and gets 403 Forbidden.
See the example · Lesson 85: Signing in with JWT · Permalink
FWhat are the three parts of a JWT, and can anyone read its contents?concept
A header (the algorithm, such as HS256), a payload of claims (such as sub, name, role, exp), and a signature, separated by dots. The header and payload are only base64url encoded, so anyone can read them; the signature only stops anyone changing them. Never put passwords or secrets in claims.
See the example · Lesson 85: Signing in with JWT · Permalink
FHow should an API store passwords?concept
As a slow, salted hash, never as text or with a fast hash alone. The SMS uses ASP.NET Core Identity's PasswordHasher: PBKDF2 with SHA-512, a random salt for each password, and 100,000 rounds. To sign in, the typed password is hashed the same way and compared.
See the example · Lesson 85: Signing in with JWT · Permalink
MWhat does a fallback authorization policy do, and why use one?concept
It applies to every endpoint that has no authorization attributes of its own. Setting it to RequireAuthenticatedUser makes the API secure by default: a new endpoint that someone forgets to protect still needs a signed-in user. Public endpoints opt out with [AllowAnonymous], such as login.
See the example · Lesson 85: Signing in with JWT · Permalink
MWhich token settings does the API check on every request, and why set ClockSkew?coding
The signature (with the signing key), the issuer, the audience, and the expiry time (exp and nbf). ClockSkew is a grace period for clocks that differ between servers; its default is 5 minutes, so an expired token would still work for 5 more minutes. The SMS sets it to 30 seconds.
See the example · Lesson 85: Signing in with JWT · Permalink
MWhere should a JWT signing key be kept, and what happens if it leaks?concept
Outside source control: user secrets while developing, and environment variables or a key vault in production. Anyone with the key can create a valid token for any user, including an admin. If it leaks, replace it at once; every token signed with the old key then stops working. The SMS also refuses to start without a key of at least 32 characters.
See the example · Lesson 85: Signing in with JWT · Permalink
SA security review finds that your login says 'unknown user' for one mistake and 'wrong password' for another. Why is that a problem, and how do you fix it?scenario
It tells an attacker which user names exist, so they can focus password guessing on real accounts. Use one message for every failure ("User name or password is wrong."), and make both paths do the same work: the SMS still verifies against a dummy hash when the user does not exist, so the response time does not give it away either. Add rate limiting and lockout against guessing.
Explain user enumeration through messages and through timing, then the fix for each, and the extra defences against password guessing.
See the example · Lesson 85: Signing in with JWT · Permalink
SA parent reports that a report-card link works without signing in. How did it happen, and how do you stop it happening again?scenario
That endpoint was protected one by one with [Authorize], and someone forgot it. Fix it now, then change the design: a fallback policy that requires a signed-in user everywhere, with [AllowAnonymous] only where needed. Add an automated test that calls every endpoint without a token and expects 401, and check the logs for anyone who used the open link.
Cover the immediate fix, the design change that makes the mistake impossible, the test that guards it, and the investigation of possible data exposure.
See the example · Lesson 85: Signing in with JWT · Permalink
AAccess tokens last 60 minutes. How would you let users stay signed in longer, and sign them out at once when needed?design
Keep access tokens short and add refresh tokens: long-lived, random, stored hashed on the server with the user and device, and exchanged for a new access token (rotating the refresh token each time). Signing out, or blocking a user, revokes their refresh tokens; for instant cut-off, keep a short deny list of token ids (jti) until they expire, or shorten access token lifetimes. Larger systems often move sign-in to an identity provider using OpenID Connect.
Separate access and refresh tokens, explain storage and rotation, cover revocation and its delay, and say when a dedicated identity provider is worth it.
See the example · Lesson 85: Signing in with JWT · Permalink
Authorization
FWhat is role-based access control?concept
Users belong to roles (in the SMS: Admin, Principal, Teacher, Accountant, Parent), and permissions are given to roles instead of to each user. The API checks the user's roles, sent as role claims in the token, before running an action.
FA signed-in user calls an endpoint their role may not use. What status should the API return?concept
403 Forbidden: the API knows who they are, but they are not allowed. 401 is only for a missing, invalid, or expired token. The app treats them differently: 401 means "sign in", 403 means "not allowed".
FWhy is hiding a button in the app not enough to protect a feature?concept
Anyone can call the API directly, with a copied token and a tool such as curl or the browser's developer tools. Every permission must be checked on the server with [Authorize]; hiding buttons is only for a tidy screen.
MWhat is the advantage of named policies over [Authorize(Roles = "...")] lists?design
The controller says what kind of user it needs (such as Teaching), and the roles for each policy are defined in one place. Adding a role or changing a permission is one line, instead of a search through every controller where one forgotten copy becomes a security hole.
MA controller has [Authorize(Policy = "Staff")] and one action has [Authorize(Policy = "ManageSchool")]. Who may call the action?coding
Only users who pass both policies: every authorization attribute on the controller and on the action must pass. In the SMS, that is Admin and Principal, who are in both Staff and ManageSchool.
MIn what order does ASP.NET Core run authorization and model validation, and why does it matter?concept
Authorization runs first, then model binding and validation. A refused user gets 403 even with an invalid body, so they never learn what a valid request looks like. In the SMS, a teacher posting an empty admission got 403, while the principal got 400 with the field errors.
SThe office changes a teacher into an accountant, but for a while she can still take attendance and cannot open fees. Why, and what are the options?scenario
Her roles are inside her access token, and the API does not look them up again on each request, so the change only arrives with her next token (up to 60 minutes in the SMS). Options: accept the delay and ask her to sign in again; shorten token lifetimes; or check a revocation list or the user's current roles on each request, at the cost of a database or cache lookup.
Explain claims in self-contained tokens, then weigh the options by how fast changes must apply and what each costs.
SHow do you make sure no role can do more than it should, as an API grows?design
Secure by default with a fallback policy, named policies in one file, and a data-driven test table with one row per role and sensitive endpoint, including the refusals. In the SMS, each row is a role, a request, and the expected status, chosen so no row changes data. Review new write endpoints for their policy before merging.
Combine design (defaults and named policies), automated proof (the permission table), and process (reviews), and explain why refusals need tests as much as successes.
ASchools want to define their own roles and permissions without a new release. How would you design it?design
Move from fixed roles to permissions: store Permissions, RolePermissions, and UserRoles per school, and make endpoints require permissions (such as Attendance.Take) instead of roles. A custom authorization policy provider can create a policy per permission name, and a handler checks the user's permissions, loaded at sign-in into the token or cached per user. Keep a few built-in roles as templates, audit every change to permissions, and decide how fast changes apply.
Explain the move from roles to permissions, how ASP.NET Core can check them dynamically, where the data lives per school, and how changes propagate and are audited.
FWhat is IDOR (insecure direct object reference)?concept
A bug where an API checks that a user may use an endpoint but not whether the record in the request belongs to them. Changing an id in the URL then shows someone else's data, such as a parent opening another child's report card by changing the admission number.
FWhat is the difference between a role rule and a record rule?concept
A role rule needs only the token: "is this user a teacher?". A record rule also needs the record being reached: "is she the class teacher of section 20?" or "is this parent linked to ADM-2017-0003?". Record rules usually read the route and ask the database.
FShould an API answer 404 or 403 when a parent asks for a student who is not theirs?concept
403, and the same 403 when the student does not exist at all. If real records gave 403 and missing ones 404, the answers would reveal which ids exist. The SMS answers 403 for both, and only users allowed to see a record ever learn whether it exists.
MWhat are an authorization requirement and an authorization handler in ASP.NET Core?coding
A requirement (IAuthorizationRequirement) is a small class that states a rule and holds its settings,
such as which staff roles count. A handler (AuthorizationHandler<TRequirement>) checks it and calls
context.Succeed when the rule holds. A policy lists requirements, so controllers only name the policy.
MHow can an authorization handler know which record a request is about?coding
For endpoints, context.Resource is the HttpContext, so the handler can read route values (such as admissionNumber or sectionId) and the query string. The SMS handlers read them, then ask the database whether the user is linked to that record. For rules that need a loaded object, you can instead call IAuthorizationService.AuthorizeAsync(User, resource, policy) inside the action.
MWhy should a handler take the user's own ids from the token and not from the request?concept
Anything in the request (body, query, headers other than the token) can be changed by the user. The token is signed, so its claims, such as guardian_id or teacher_id, cannot be changed without breaking the signature. The SMS compares the route's record with the ids from the token.
SA parent reports seeing another child's marks by changing a number in the app's address. What do you do?scenario
Confirm and fix the endpoint first: add a record rule that checks the parent is linked to the student in the route. Then search every endpoint with an id in its route for the same gap, add permission tests that try other families' ids, and check logs for who reached records they should not have. Report the exposure as the school's policy requires.
Cover the immediate fix, the systematic search for the same bug, tests that prevent it returning, and the investigation and reporting of the leak.
SRecord checks add a database query to many requests. How do you keep them fast and correct?design
Keep each check a tiny EXISTS query on indexed links (StudentGuardians, ClassTeachers, SubjectAllocations). Put small, stable facts in the token at sign-in, such as the user's teacher or guardian id, but not lists that change often. Cache answers per user for a short time if needed, and invalidate when links change. Measure: in the SMS each check reads only a few rows.
Balance speed against freshness: what goes in the token, what is queried, what is cached, and how changes reach the checks.
AList endpoints (such as all invoices) must also show each user only their records. How would you design that across a large API?design
Record rules on single-record endpoints are not enough for lists: the query itself must filter. Build the filter from the user (for example, only students linked to this guardian) in one shared place, such as a data-access layer that always adds it, or database row-level security with SESSION_CONTEXT set per request. Test lists with users who own nothing, one record, and many records.
Separate single-record checks from list filtering, compare doing it in the data layer with database row-level security, and explain how to make it impossible to forget.
Auditing
FWhat are audit columns, and why are they useful?concept
Columns such as CreatedAt, CreatedBy, UpdatedAt, and UpdatedBy that record when and by whom a row was created and last changed. They answer "who changed this?" for disputed data such as marks and payments, and they protect staff who made correct changes.
FWhy must the 'changed by' user come from the token and not from the request body?concept
The client decides what is in the body, so anyone could write someone else's id. The signed token cannot be changed, and the server carries the user from it into the database without the client touching it.
FWhat is SESSION_CONTEXT in SQL Server?concept
A set of key-value pairs that belongs to one connection, set with sp_set_session_context and read with SESSION_CONTEXT(N'key'). Statements, triggers, and DEFAULT constraints on that connection can read it, which lets the API tell the database who the user is.
MA column has a DEFAULT, but new rows still get NULL. Why?scenario
The INSERT names the column and passes NULL, and an explicit value, even NULL, wins over the default. In the SMS, the Dapper inserts leave CreatedBy out, so DF_..._CreatedBy fills it from the session's user.
MHow can a singleton service know the current request's user in ASP.NET Core?coding
Inject IHttpContextAccessor (registered with AddHttpContextAccessor). It finds the current request's HttpContext from anywhere, so a singleton such as the SMS's HttpCurrentUser can read the user's "sub" claim each time it is asked, even though the user changes with every request.
MWhy set the session user in the connection factory instead of in each repository?design
Every connection comes from the factory, so one hook covers every repository, Dapper call, and transaction, and nobody can forget it. The SMS adds a StateChange handler that runs sp_set_session_context with @read_only = 1 as soon as a connection opens.
SWith connection pooling, could one user's id stay on a connection and be used by the next request?scenario
Not with SESSION_CONTEXT: SQL Server resets a pooled connection's session state before it is reused, so the next user starts with NULL. The SMS proves it with a test that opens a connection with user 42, then opens another from the same pool without a user and reads NULL. @read_only = 1 also stops code on the same connection from changing the id.
Explain pooling and the reset, show how you would prove it with a test, and mention read-only values.
SMarks were changed after results were published, and the audit log has old and new values but no user. How do you fix this for the future?scenario
The application connects with one shared login, and nothing tells the database who the person is. Pass the signed-in user to every connection (SESSION_CONTEXT), let the triggers and defaults read it, and stamp UpdatedBy in updates. Add a test that a change through the API records the user, and consider blocking changes to published results without a recorded reason.
Explain why the user was missing, describe the end-to-end fix, how to verify it, and a process rule for sensitive changes.
AHow would you design auditing for every important table in a large system?design
Decide what must be audited (who, when, what changed, from where), then choose a mechanism: triggers that write before-and-after values (as tr_Marks_Audit does), SQL Server temporal tables for full history, or change data capture feeding a separate audit store. Pass the user and request id on every connection, protect the audit data from changes (separate permissions, append-only), set retention rules, and give the office a way to search it.
Compare the mechanisms by detail, cost, and tamper resistance, and cover identity propagation, retention, and how people will use the audit data.
Rate limiting
FWhat is rate limiting, and what does it protect against?concept
Capping how many requests a caller may make in a period. It slows password guessing on sign-in, stops one client (or a buggy app in a loop) from taking everyone's share, and limits the damage of floods. The SMS allows 5 sign-ins per minute per address and 300 requests per minute per user.
FWhat should an API return when a request is rate limited?concept
429 Too Many Requests, with a Retry-After header saying how many seconds to wait when the limiter knows it, and a body in the API's usual error shape. Well-behaved clients wait instead of retrying at once.
FCompare the fixed window, sliding window, and token bucket limiters.concept
Fixed window: N requests per window, reset when it ends; simple, but bursts can cluster at the edge. Sliding window: N requests in any window, counted in segments; smoother. Token bucket: a bucket of N tokens refilled at a steady rate; allows short bursts with a steady average. The SMS uses a fixed window for sign-in and a token bucket for general traffic.
MWhat is a partition in ASP.NET Core rate limiting, and how do you choose the key?design
A partition is whose count a request uses, chosen by a key computed from the request. Before sign-in the only key is the client address; once signed in, the user id is fairer, because many users can share one address. The SMS uses "user:{sub}" when signed in and "ip:{address}" otherwise.
MWhere must app.UseRateLimiter() go in the pipeline, and why?coding
After UseAuthentication (and after routing, which minimal hosting adds early), so the limiter can see the signed-in user for per-user limits and the endpoint's [EnableRateLimiting] policy. Placed before authentication, every request would count as anonymous.
MWhy are rate-limit tests often flaky, and how do you avoid it?scenario
In tests every request comes from the same address and the suite makes many sign-ins, so a real limit is hit at random. Raise the limits in the shared test setup, and test the limiter on purpose with a separate server configured with small limits, as the SMS's RateLimitTests does.
SShould you protect sign-in with account lockout or with rate limiting?design
Lockout blocks the account after N failures, which also lets an attacker lock a real user out on purpose. A rate limit per address slows the attacker's address without touching the user, but a guesser with many addresses gets N guesses each. Combine them: a per-address limit first, a softer per-account protection (delays or a long lockout only after many failures), and alerts on repeated failures.
Compare who each defence slows down, how each can be abused, and propose a layered design.
SAt 8:00 every teacher signs in from the school's one network address and many get 429. What do you change?scenario
The sign-in limit is per address, and the whole staff room shares one. Raise the per-address sign-in limit for that school's deployment (it is configuration), and keep guessing slow with a per-account protection, such as a short lockout or growing delays per user name, plus alerts. Check the rejection logs to confirm the cause first.
Diagnose from the logs, explain why the partition key causes it, and give a fix that keeps the security goal.
AThe API runs on several servers behind a load balancer. How do you make rate limits correct across all of them?design
The built-in limiter counts in each server's memory, so N servers allow roughly N times the limit. Options: a gateway or reverse proxy that limits in one place before the servers; a shared store (such as Redis) that all servers count in; or dividing each server's limit by the number of servers as an approximation. Keep per-user limits in the API, where the user is known, and coarse per-address limits at the edge.
Explain why in-memory counts multiply, compare edge limiting, shared counters, and approximations, and say which limits belong where.
System Design
Non-functional requirements
AWhich non-functional requirements change when the SMS goes from one school to 1,000 schools?design
Data isolation becomes critical: one school must never see another's data. Load peaks multiply, for example every school taking attendance between 8 and 9 AM. Availability, backups and restore per school, deployments without downtime, security reviews, and the cost per school all become explicit requirements with numbers.
List the categories (isolation, scalability, availability, recoverability, security, cost) and give one SMS number for each, such as "attendance save under 1 second at 8 AM for 1,000 schools". Then name the design consequence: a tenant column or database per school, caching, and horizontal scaling, and the trade-off between shared and separate databases (cost against isolation).
Reporting
AHow would you separate day-to-day school operations from heavy reporting when the SMS serves 1,000 schools?design
Keep the normalized transactional database for daily work such as attendance and fees. Send reporting to a separate copy built for reading: a read replica for simple reports, or a data warehouse with denormalized tables refreshed on a schedule. Heavy reports then never slow down a teacher saving attendance.
Name the two workloads (many small writes vs few large reads) and why one design cannot serve both well. Describe the options from cheapest to most powerful: indexed views, read replicas, then a warehouse. State the trade-off: reports become slightly delayed (eventual consistency) in exchange for fast, safe daily operations.
See the example · Lesson 7: Normalization 1NF-3NF · Permalink
Multi-tenant SaaS
FWhat is a SaaS product, and what is a tenant?concept
SaaS (software as a service) means one company runs one system and many customers use it over the internet, usually for a monthly fee. Each customer is a tenant: it shares the system with the others but must only ever see its own data. In the SMS, each school is a tenant.
See the example · Lesson 97: From one school to 1,000 · Permalink
FWhat is the difference between functional and non-functional requirements? Give an example of each.concept
Functional requirements say what the system does, such as "a school admin can add a teacher". Non-functional requirements say how well it does it, such as "95% of requests answer in under 300 ms" or "99.9% availability". Non-functional ones need numbers, or nobody can test them.
See the example · Lesson 97: From one school to 1,000 · Permalink
FWhat is an architecture decision record (ADR)?concept
A short document, kept next to the code, that records one important decision: the context, the options considered, the choice, and its consequences. It explains later why the system looks the way it does, so the same debate does not have to be repeated.
See the example · Lesson 97: From one school to 1,000 · Permalink
MCompare shared database, schema per tenant, and database per tenant.concept
Shared database with a tenant column: one schema, cheapest, simplest upgrades, but a missed filter leaks data and one busy tenant can slow others. Schema per tenant: better separation, but every table is copied per tenant. Database per tenant: strongest isolation and easy per-tenant restore, but many databases to upgrade, back up, monitor and pay for, and cross-tenant reports are hard.
See the example · Lesson 97: From one school to 1,000 · Permalink
MHow would you estimate the database size of a system before it is built?scenario
Measure bytes per row on real or realistic sample data (data plus every index, for example from sys.allocation_units or sys.dm_db_index_physical_stats), then multiply by rows per year from written assumptions: tenants, users, events per user. Keep the assumptions in one place so the estimate can be rerun. For the SMS: about 100 bytes per attendance row, 176 million rows a year, about 19 GB.
See the example · Lesson 97: From one school to 1,000 · Permalink
MWhy design for the peak hour instead of the average load?concept
Users judge the system at its busiest moment. In the SMS, morning attendance is about 11 requests a second but results day is about 444, 40 times more; a system sized for the average would fail exactly when parents need it. Size compute, connection pools and caches for the peak, and use the quiet hours for batch work.
See the example · Lesson 97: From one school to 1,000 · Permalink
SYour company sold its app to each new customer by copying the code and database. What problems follow, and how do you fix them?scenario
Each copy drifts: fixes must be repeated per customer, some copies miss security fixes, and upgrades become projects. Move to one multi-tenant system: one code base, one schema with a tenant id, per-tenant settings instead of per-tenant code, and a migration plan that moves customers one by one with their data verified.
Name the operational and security costs of copies, then describe the target design and a safe migration path.
See the example · Lesson 97: From one school to 1,000 · Permalink
SYou chose a shared database with a SchoolId column. What risks does that bring, and how do you control them?design
Data leaks from a missing filter, and noisy neighbours. Control leaks with two layers: tenant filtering in every repository and database-level row-level security, plus tests that try to read another tenant's data. Control noisy neighbours with indexes that start with the tenant id, per-tenant rate limits, and background or serialized heavy jobs. Keep the option to move a very large tenant to its own database later.
Name each risk with a concrete failure, then a preventive control, a detective control, and an escape hatch.
See the example · Lesson 97: From one school to 1,000 · Permalink
ADesign the tenancy model for an education platform expected to grow from 50 to 10,000 schools.design
Start with a shared database and a tenant id for cost and simple operations, with isolation enforced in the application and the database. Design for movement: tenant id in every key and index, a tenant catalogue that maps each tenant to a database, and tooling to move one tenant. As it grows, shard tenants across several databases, and give the largest or most regulated tenants their own database, all behind the same code.
Show how the model evolves with scale and cost, covering isolation, routing through a tenant catalogue, sharding, per-tenant exceptions, and operations such as upgrades and backups.
See the example · Lesson 97: From one school to 1,000 · Permalink
Leadership
Project discussion
FTell me about a project you have built.behavioral
Use a clear structure: the problem, who uses it, your role, the technology, one hard problem you solved, and the result. Keep it to about two minutes. For example: "I built a School Management System with SQL Server, C#, and ASP.NET Core. The hardest part was making sure attendance could not be saved twice for the same student and day."
MHow do you explain your project's architecture in an interview?behavioral
Go layer by layer from the user to the data: the client, the API, the business logic, and the database, naming the technology for each. Then follow one real request through all of them, such as a teacher saving attendance. Finish with one design decision and why you made it.
Planning
SThe school wants attendance, fees, exams, transport and hostel modules live in three months with two developers. What do you build first?scenario
Order the work by dependency and value. Masters, students, and enrollment come first because every other module needs them. Then attendance (used every day) and fees (money and legal records), then exams. Transport and hostel move to a later phase, and the agreed scope is written down and delivered in small increments.
Name the principle (dependencies first, then business value and risk). Walk through the SMS module order with the reason for each. Then state the trade-off honestly: cutting scope is acceptable, cutting data integrity or security is not, and the client must agree in writing to what moves to phase two.
SHow do you decide what is in scope and what is out of scope?behavioral
Rank features by business value, dependencies, risk, and the deadline, for example with Must, Should, Could, and Won't. Must-haves and their dependencies go in; the rest goes on a written out-of-scope list. The client agrees to that list, so new requests become planned changes instead of silent extra work.
Explain the ranking method, then show the SMS example: payment gateways, transport, and timetables are out of Part 1 because the core school records come first. Close with the trade-off: a smaller scope delivered well beats a large one delivered late, and scope is revisited every release.
Requirements
MWhat do you do when a requirement is unclear?behavioral
Ask specific questions with examples instead of guessing, and where possible watch how the work is done today. Write down your assumptions and get them confirmed by the person who owns the requirement. A five-minute question is cheaper than a week of rework.
SAfter the database is built, the principal says attendance must be taken per period, not per day. How do you handle it?scenario
First assess the impact: tables, API endpoints, screens, reports, and existing data that must be migrated. Then give an estimate and discuss priority and timeline with the client as a change request. Once agreed, update the requirements, the design, and the tests before changing code.
Show a calm process: impact analysis, estimate, agreement, then change. Use the SMS detail: AttendanceRecords needs a period column and a new unique rule, and old daily records need a migration decision. Mention the trade-off between a quick patch and a clean redesign, and how asking "show me how you do it today" earlier would have caught it.