03open 25 min

Data and state modeling: records, classes, generics, collections, and LINQ

Move from Python's flexible data structures into explicit C#/.NET models using records, classes, typed collections, generics, and LINQ.

by the end of this lesson you can

  • Uses a typed model for the user shape
  • Uses LINQ or a loop intentionally rather than as a novelty
  • Makes the result collection clear to the caller

Overview

Python backend code often gets far with dictionaries, dataclasses, lists, and comprehensions. C#/.NET asks you to be more specific. You choose whether a value is a class or record, whether a collection is a List or IEnumerable, and whether a query reads best as a loop or as LINQ.

In Python, you often

shape data with dicts, dataclasses, and list comprehensions, letting runtime behavior and tests enforce many of the assumptions.

In C#/.NET, the common pattern is

to model data with named types and typed collections, then use LINQ and generics to express transformations without losing the contract.

why this difference matters

This is where many Python developers either start liking .NET or fighting it. Stronger modeling feels heavier at first, but it makes service and API code much easier to reason about once the domain has real names and explicit collection types.

Python

active_emails = [user["email"] for user in users if user["active"]]

C#/.NET

var activeEmails = users
    .Where(user => user.IsActive)
    .Select(user => user.Email)
    .ToList();

Deeper comparison

Python version

payload = {"id": 1, "email": "ana@example.com", "roles": ["admin"]}

C#/.NET version

public record UserDto(int Id, string Email, List<string> Roles);

var payload = new UserDto(1, "ana@example.com", new List<string> { "admin" });

Reflect

Why can explicit modeling with records, typed collections, and LINQ be safer than keeping backend payloads flexible the way many Python services do?

what a strong answer notices

A strong answer mentions named domain concepts, compiler-checked transformations, fewer hidden key assumptions, and easier reasoning about what each service actually returns.

Rewrite

Rewrite this Python collection code into C#/.NET and choose data structures that make the returned shape explicit.

Rewrite this Python

admins = [user for user in users if "admin" in user["roles"]]

what good looks like

  • Uses a typed model for the user shape
  • Uses LINQ or a loop intentionally rather than as a novelty
  • Makes the result collection clear to the caller

Practice

Design the models and query code for a C#/.NET service that returns active customer summaries from a repository result set.

success criteria

  • Uses records or classes intentionally
  • Keeps collection transformations readable for someone coming from Python comprehensions
  • Explains why the chosen generic or collection types fit the service boundary

Common mistakes

  • Treating LINQ like magical punctuation instead of as a readable query tool over typed collections.
  • Keeping Python-style dictionary modeling too long instead of naming the real domain shapes.
  • Using broad collection types everywhere without thinking about what the API actually promises to callers.

takeaways

  • This is where many Python developers either start liking .NET or fighting it. Stronger modeling feels heavier at first, but it makes service and API code much easier to reason about once the domain has real names and explicit collection types.
  • A strong answer mentions named domain concepts, compiler-checked transformations, fewer hidden key assumptions, and easier reasoning about what each service actually returns.
  • Uses records or classes intentionally