Inno Generic Flashcards
Tracking
In Entity Framework (EF) Core, tracking refers to the process where the framework keeps track of changes made to entities retrieved from the database. When an entity is retrieved from the database, EF Core tracks its state (Added, Modified, Deleted, Unchanged) so it can automatically generate the necessary SQL to persist changes back to the database. This is called “Change Tracking.”
Transactions
EF Core supports database transactions, allowing you to group multiple operations into a single unit of work. If one operation fails, all previous operations can be rolled back to maintain data consistency.
You can use the DbContext.Database.BeginTransaction() method to start a transaction and manage it manually or rely on automatic transactions when calling SaveChanges().
Loading Types:
Eager Loading: Using Include() to load related entities alongside the main entity in one query.
Lazy Loading: Related entities are loaded automatically when accessed for the first time, using proxies (enabled by default with a virtual property).
with virtual properties
UseLazyLoadingProxies() in model builder
Explicit Loading: Manually loading related data via methods like Load() when needed.
Fluent API:
Pros: More flexible, allows complex configurations, and can be used for every aspect of your models.
Cons: Requires more lines of code and can be less readable if overused.
Data Annotations:
Pros: Simpler, more concise, and useful for basic configurations.
Cons: Less flexible compared to Fluent API, and some configurations (like complex relationships) cannot be easily done with annotations.
Code-First
Code-First: You start by defining your domain models in code, and EF Core generates the database schema based on them.
Pros: Ideal for greenfield projects, version control over migrations.
Cons: Less flexibility when working with an existing database.
Database-First
You start with an existing database, and EF Core generates classes that map to the database schema.
Pros: Ideal for working with legacy databases.
Cons: May require additional work for custom configurations and might not be as flexible.
Repository Pattern:
The Repository Pattern abstracts the data access logic from the business logic by providing a layer that acts as a mediator between the two.
It simplifies the interaction with the database by encapsulating queries and operations.
Pros: Decouples data access code from business logic, making the application easier to test and maintain.
Unit of Work
The Unit of Work pattern maintains a list of changes to be applied to the data store, ensuring that all changes within a single transaction are committed together.
Pros: Simplifies transaction management by grouping multiple operations.
Cons: It may add unnecessary complexity if not used properly.
SOLID Principles:
S: Single Responsibility Principle – A class should have only one reason to change.
O: Open/Closed Principle – Software entities should be open for extension but closed for modification.
L: Liskov Substitution Principle – Subtypes must be substitutable for their base types.
I: Interface Segregation Principle – Clients should not be forced to depend on interfaces they do not use.
D: Dependency Inversion Principle – High-level modules should not depend on low-level modules, both should depend on abstractions.
DRY i KISS
DRY (Don’t Repeat Yourself):
A principle aimed at reducing duplication in code, ensuring that each piece of knowledge or logic has a single, unambiguous representation.
KISS (Keep It Simple, Stupid):
The principle that systems should be as simple as possible, avoiding unnecessary complexity.
REST
Representational State Transfer (REST) is an architectural style for designing networked applications. It relies on stateless communication, standard HTTP methods (GET, POST, PUT, DELETE), and resources that can be represented as URLs.
Mapping
Mapping refers to converting one representation of data to another. Common mappings include mapping database models to DTOs (Data Transfer Objects) or converting between different types in different layers of an application.
Classes, Structures, Records
Classes: Reference types, allow inheritance, and can have methods, properties, and fields.
Structures: Value types, do not support inheritance (except for interfaces), and are more lightweight than classes.
Records: Reference types with a focus on immutability and value equality. They are ideal for DTOs or data containers.
Reference vs. Value Types
Reference Types: Store a reference to the memory location of the object, allowing multiple variables to point to the same object in memory. Examples: classes, arrays, strings.
Value Types: Contain the actual data, so when assigned to another variable, a copy of the data is created. Examples: structs, primitives.
Constructors
Parameterized Constructor:
A constructor that accepts parameters to initialize the object with specific values at the time of creation.
Parameterless Constructor:
A constructor that does not require any parameters. It is typically used when you need to instantiate an object but don’t have initial values to pass.
Primary Constructor:
In C# 9.0+, a primary constructor allows you to define properties directly in the class declaration, simplifying the initialization of class members.
Generics
Generics: Allow the creation of reusable and type-safe classes, methods, and structures. It enables the use of placeholders for data types, allowing for flexible and strongly-typed code without redundancy.
Example: List<T> where T is a placeholder for any data type.</T>
Middlewares and Request Pipeline
Middlewares: Components in the request pipeline that handle HTTP requests and responses. Each middleware can perform some logic, such as authentication, logging, or modifying the request or response.
Request Pipeline: The sequence of middlewares that the HTTP request passes through before reaching the application’s endpoint.
Swagger and OpenAPI
Swagger: A framework for documenting and testing REST APIs. It automatically generates interactive documentation for your API.
OpenAPI: The specification behind Swagger, providing a standard way to define APIs. OpenAPI allows for describing APIs in a machine-readable format (YAML or JSON) that can be used to generate documentation, client libraries, and server stubs.
ACID
ACID is a set of properties that ensure that database transactions are processed reliably. These properties guarantee the integrity of the database in case of failures.
Atomicity: Ensures that a transaction is fully completed or not executed at all. If one part of the transaction fails, the entire transaction is rolled back.
Example: If you transfer money from one bank account to another, both debit and credit operations are treated as a single unit. If either fails, neither is applied.
Consistency: Guarantees that the database will always be in a valid state after a transaction. It ensures that any transaction will move the database from one valid state to another.
Example: If the database rule is that the balance of an account should never be negative, the transaction will be rejected if it tries to break that rule.
Isolation: Ensures that the operations of one transaction are isolated from others. Even if multiple transactions are happening concurrently, the results of one transaction should not interfere with others.
Example: Two users trying to withdraw money from the same account will not be able to do so in a way that creates an inconsistent state.
Durability: Once a transaction is committed, it will remain so, even in the case of a system crash or failure. The data will be permanently saved.
Example: After a successful money transfer, the changes are written to disk, ensuring that the transfer is not lost in case of a failure.
CAP Theorem
The CAP theorem (also known as Brewer’s theorem) states that a distributed database system can provide only two out of the following three guarantees at the same time:
Consistency: Every read operation returns the most recent write (or an error).
Example: After a successful write to the database, all clients will see the updated value.
Availability: Every request (read or write) will receive a response, but it may not contain the most recent data.
Example: Even if a system has partitioned into two parts, it will still respond to requests, even if one part has outdated information.
Partition Tolerance: The system will continue to operate, even if network partitions prevent some nodes from communicating with others.
Example: If a network failure occurs between two database nodes, the system should still function on the remaining nodes, possibly returning outdated or inconsistent data.
3 states of CAP theorem
The CAP theorem implies that in a distributed system, you have to choose between these three properties:
CA (Consistency + Availability): Systems that sacrifice partition tolerance but ensure consistency and availability (e.g., traditional SQL databases).
CP (Consistency + Partition Tolerance): Systems that ensure consistency and partition tolerance but might sacrifice availability (e.g., HBase).
AP (Availability + Partition Tolerance): Systems that sacrifice consistency but ensure availability and partition tolerance (e.g., Cassandra).
Get Familiar with NoSQL Databases and Their Types
NoSQL Databases are designed to handle large volumes of unstructured, semi-structured, or structured data. Here are the main types:
Document Stores (e.g., MongoDB, CouchDB):
Data is stored in documents (usually JSON, BSON).
Each document can have a different structure.
Good for hierarchical data.
Key-Value Stores (e.g., Redis, Riak):
Store data as key-value pairs, similar to a dictionary.
Great for caching, session management, or simple lookups.
Column-Family Stores (e.g., Cassandra, HBase):
Data is stored in columns rather than rows.
Ideal for high-throughput, wide-column scenarios.
Graph Databases (e.g., Neo4j, ArangoDB):
Designed to store and process relationships between data points.
Useful for social networks, recommendation engines, and network analysis.
Time-Series Databases (e.g., InfluxDB, TimescaleDB):
Optimized for storing time-stamped data, such as logs or metrics.
Circuit Breaker:
The Circuit Breaker pattern is used to detect failures and prevent a system from making repeated requests to a failing service, potentially causing more harm. The pattern operates like an electrical circuit breaker: it monitors the calls to a remote service, and if failures reach a certain threshold, it “trips” the breaker and stops the calls for a period of time, allowing the service to recover.
The circuit breaker typically has three states:
Closed: The circuit is functioning normally, and requests flow through as usual.
Open: The circuit breaker has tripped, and requests are no longer allowed to pass through to the service.
Half-Open: After a cooling-off period, a limited number of requests are allowed to pass through to see if the service has recovered. If they succeed, the circuit closes again; otherwise, it opens.