Marcin Jahn | Dev Notebook
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects

Entity Framework Core Tips

A class inheriting DbContext is needed.

Seeding

One way is to override OnModelCreating method of DbContext:

protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Value>()
.HasData(
new Value { Id = 1, Name = "Value 101" },
new Value { Id = 2, Name = "Value 102" },
new Value { Id = 3, Name = "Value 103" }
);
}

Downside of this is that we need to manually type the ids. Another way is to create seperate class for seeding:

public class Seed
{
public static void SeedData(DataContext dbContext)
{
if (!dbContext.Activities.Any())
{
//Create some data...
dbContext.Values.AddRange(values);
dbContext.SaveChanges();
}
}
}

Then, we can run it from Program of our application:

public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<DataContext>();
context.Database.Migrate(); //auto-migration running on every startup to make sure DB is in sync
Seed.SeedData(context); //auto-seeding the DB with exemplary data if there is no data in DB
}
catch (Exception e)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(e, "An error occured during migration");
}
}
host.Run();
}

Ids are generated automatically. Example above also shows how to run migrations on every application startup.

SQLite

It’s good idea to use Sqlite in a prototype. It’s files based DB. Registering DbContext:

services.AddDbContext<DataContext>(options =>
{
options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"));
});

The GetConnectionStrings method is just looking for ConnectionStrings section in appsettings.json. Exemplary appsettings.json:

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "Data source=reactivities.db"
}
}

reactivities.db is the name of SQLite DB file.

Async

It’s a good idea to get data using async methods, i.e.:

public async Task<ActionResult<IEnumerable<Value>>> Get()
{
var values = await _dbContext.Values.ToListAsync();
return Ok(values);
}

Adding records

DbSet has an asynchronous AddAsync method. However, it should be used only in cases where value generators are used. It’s adviced to use Add (synchronous method) in other cases.

DB modification success

It’s a good idea to use the following strategy for finding out if saving changes in DB was successful:

_dbContext.Activities.Add(activity);
var success = await _dbContext.SaveChangesAsync() > 0;
if (success) return Unit.Value;
else throw new Exception("Problem saving changes");

SaveChangesAsync returns a number of changes done in DB.

Global Filters

Global query filters might be useful when using soft-deletes to filter out the deleted entities by default.

ASP.NET Core

Responses

It’s not the best idea to directly return entities of our DbSets. We should define DTO classes (records) that will contain responses of our API actions.

Returning DB entities directly exposes the whole database structure, which might bring too many information to the client. Additionally, when Includeing relations, we might encounter cyclic references issue when serializing data.

←  Testing
© 2023 Marcin Jahn | Dev Notebook | All Rights Reserved. | Built with Astro.