I ferri del mestiere 🛠️

Il mio dizionario personale di C# e .NET. Dalla sintassi base ai pattern architetturali, spiegati analizzando il codice reale dei miei progetti in produzione.

Non la solita teoria accademica, ma una vera e propria Knowledge Base operativa. Ogni termine è accompagnato da snippet di codice estratti direttamente dalle mie applicazioni (come MotoLogPro o Wallet Wise), per farti vedere come i concetti si traducono in soluzioni reali.

Dai un'occhiata al mio profilo GitHub per esplorare il codice sorgente completo: github.com/Mugen85

Tutti Struttura C# Basi OOP OOP: Pilastro Design / Pattern MAUI EF Core / DB Flusso / Asincrono

Fondamenta OOP e Struttura

Un "contenitore logico" che organizza e raggruppa classi affini, evitando conflitti di nomi tra parti diverse del programma o librerie esterne.

namespace MotoLogPro.Domain.Entities; public class Motorcycle { // ... }

Il caso d'uso in MotoLogPro

Usando il C# moderno (file-scoped namespace) evito di indentare tutto il file. Raggruppo tutte le entità del database sotto MotoLogPro.Domain.Entities per mantenere l'architettura Clean e ordinata.

Parole chiave che definiscono la visibilità (chi può usare) di una classe, un metodo o una proprietà. Sono il primo strumento di Incapsulamento.

public // Visibile a tutti (interno + esterno all'assembly) private // Visibile solo alla classe stessa protected // Visibile alla classe e alle sue figlie (Ereditarietà) internal // Visibile solo all'interno dello stesso progetto (.dll) public class AuthService { private readonly HttpClient _httpClient; // solo per questa classe public Task LoginAsync() { ... } // accessibile dall'esterno }

La regola d'oro

Principio del minimo privilegio: esponi sempre il meno possibile. Inizia con private, e allarga la visibilità solo quando strettamente necessario. Rende il codice più sicuro e manutenibile.

Il "progetto" o lo "stampo" da cui vengono creati gli oggetti in memoria. Raggruppa i dati (stato) e i metodi (comportamento). È un tipo riferimento: l'oggetto vive nell'heap e viene gestito dal Garbage Collector.

public class Motorcycle { public int Id { get; set; } public string Brand { get; set; } = string.Empty; }

Il caso d'uso in MotoLogPro

Uso questa classe come entità fondamentale del dominio. Rappresenta la mappa nel codice di quello che diventerà una vera e propria tabella nel database relazionale.

Simile alla classe, ma è un tipo valore: vive nello stack (più veloce) e viene copiato per valore ad ogni assegnazione. Ideale per dati piccoli, immutabili e ad alta frequenza di utilizzo.

// Esempio: Point è struct in .NET (copia il valore, non il riferimento) public readonly struct Money { public decimal Amount { get; } public string Currency { get; } public Money(decimal amount, string currency) => (Amount, Currency) = (amount, currency); } Money a = new(100m, "EUR"); Money b = a; // b è una COPIA indipendente di a

Class vs Struct: quando scegliere?

Usa struct per dati piccoli (2-3 campi), immutabili e usati spesso (coordinate, colori, intervalli di tempo). Usa class per tutto il resto. In WalletWise potrei usare una struct Money per incapsulare importo + valuta senza overhead di allocazione.

Un tipo speciale che definisce un insieme di costanti con nome. Trasforma "numeri magici" o stringhe fragili in valori leggibili, tipizzati e sicuri a compile-time.

public enum FuelType { Benzina = 0, Diesel = 1, Elettrico = 2, Ibrido = 3 } // Utilizzo nella classe Motorcycle public FuelType Fuel { get; set; } = FuelType.Benzina; // Pattern matching su enum → codice leggibilissimo var label = vehicle.Fuel switch { FuelType.Elettrico => "⚡ Zero emissioni", FuelType.Ibrido => "🌿 Ibrido", _ => "⛽ Carburante" };

Il caso d'uso in MotoLogPro

Invece di salvare nel DB la stringa "benzina" (con tutti i rischi di typo), salvo il valore intero 0 e mappo l'enum via Entity Framework. Il codice è autodescrittivo e il compilatore mi avvisa subito se uso un valore inesistente.

La capacità di una classe (figlia) di assorbire e riutilizzare tutti i campi, le proprietà e i metodi di un'altra classe (genitore).

public class ApplicationUser : IdentityUser { public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; }

Il caso d'uso in MotoLogPro

Sfrutto la potenza del framework ASP.NET: ereditando da IdentityUser, la mia classe riceve gratis Email, Password, e sicurezza. Io aggiungo solo i campi extra che mi servono (Nome e Cognome).

Permette a una classe figlia di "sovrascrivere" il comportamento di un metodo ereditato dal genitore (marcato virtual) per personalizzarne il funzionamento.

protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); // Aggiungo le mie regole personalizzate per le tabelle }

Il caso d'uso in MotoLogPro

Nel ApplicationDbContext sovrascrivo questo metodo nativo di Entity Framework. Richiamo prima le logiche base del genitore con base.OnModelCreating, poi ci aggancio le mie configurazioni custom per le tabelle.

Una classe incompleta che non può essere istanziata direttamente (no new), ma funge da base concettuale obbligando altre classi a derivare da essa.

[ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { // Eredita metodi come Ok(), BadRequest() da ControllerBase }

Il caso d'uso in MotoLogPro

Nelle mie API utilizzo ControllerBase, che è una classe astratta nativa di ASP.NET. Fornisce fondamenta robuste e metodi pronti all'uso per gestire le risposte HTTP.

L'opposto dell'abstract: una classe marcata sealed non può essere ereditata da nessuna altra classe. È la versione finale, chiusa ad ogni estensione.

public sealed class TokenService : ITokenService { // Nessuno potrà mai fare: class HackedTokenService : TokenService public string GenerateToken(ApplicationUser user) { ... } }

Perché usarla?

Sicurezza e performance. Il compilatore e il runtime sanno che non esiste alcuna classe derivata, quindi possono ottimizzare le chiamate ai metodi (no dispatch virtuale). Perfetta per servizi critici come la generazione di token JWT, dove l'ereditarietà incontrollata sarebbe un rischio.

Un contratto rigoroso. Definisce quali metodi e proprietà una classe deve possedere, ma non dichiara mai come devono essere implementati.

public interface IVehicleService { Task<IEnumerable<VehicleDto>> GetMyVehiclesAsync(); Task<bool> AddVehicleAsync(VehicleDto vehicle); }

Il caso d'uso in MotoLogPro

Separa il "cosa" dal "come". L'app sa che esiste un servizio per recuperare i veicoli (l'interfaccia), ma non le importa se questo li legge da un'API remota, da un database locale SQLite o da un file di testo (l'implementazione).

Un membro static appartiene alla classe stessa, non a una sua istanza. Esiste uno e un solo esemplare di quel membro, condiviso da tutti gli oggetti e accessibile senza fare new.

// Classe statica: non istanziabile, solo metodi di utilità public static class DateTimeHelper { public static string ToItalianFormat(DateTime dt) => dt.ToString("dd/MM/yyyy"); } // Uso diretto senza new: var label = DateTimeHelper.ToItalianFormat(DateTime.Now);

Quando usarlo (e quando no)

Perfetto per classi di utilità pure (helper, costanti, extension methods). Da evitare per la logica di business: le classi statiche non sono iniettabili via DI, non sono mockabili nei test e creano dipendenze globali difficili da gestire.

Permettono di "aggiungere" nuovi metodi a un tipo esistente (anche a quelli del framework come string o IEnumerable) senza modificarne il codice sorgente.

// Definizione: primo parametro con "this" = tipo da estendere public static class StringExtensions { public static bool IsValidEmail(this string email) => email.Contains('@') && email.Contains('.'); } // Utilizzo: sembra un metodo nativo di string! bool ok = "marco@test.it".IsValidEmail(); // → true

Il caso d'uso in MotoLogPro

Li utilizzo per registrare i servizi in MauiProgram.cs in modo ordinato. Invece di avere un file unico con 200 righe di builder.Services.Add..., creo metodi di estensione come services.AddMotoLogProServices() che raggruppano registrazioni correlate in file separati.

Permette di dividere la definizione di una singola classe su più file fisici. Il compilatore unirà tutti i frammenti al momento della compilazione.

public partial class LoginViewModel : ObservableObject { // Io scrivo qui le mie proprietà e comandi // Il CommunityToolkit genera il resto in background! }

Il caso d'uso in MotoLogPro

Fondamentale in MAUI (MVVM). Grazie a partial, i Source Generators analizzano il mio codice e autogenerano centinaia di righe di boilerplate in un file nascosto, unendolo alla mia classe per magia.

Tipo di dato reference (come la classe) pensato principalmente per incapsulare dati "immutabili" (che non cambiano dopo la creazione) in modo ultracompatto.

public record LoginRequestDto( string Email, string Password ); // Uguaglianza strutturale gratis! Due record sono uguali // se hanno gli stessi dati (non lo stesso riferimento in memoria)

Il caso d'uso in MotoLogPro

Li utilizzo massicciamente per i DTO (Data Transfer Objects). Mi permettono di definire "pacchetti postali" usa-e-getta che viaggiano tra il client MAUI e le Web API con un'unica riga di codice pulitissima.

Una variabile dichiarata internamente alla classe. Per rispettare il principio di "Incapsulamento", si imposta come private (preceduto spesso da underscore _).

public class AuthService : IAuthService { private readonly HttpClient _httpClient; }

Il caso d'uso in MotoLogPro

Serve per mantenere uno stato interno accessibile solo alla classe stessa. Aggiungendo readonly mi assicuro che la connessione HTTP, una volta assegnata, non possa più essere alterata accidentalmente.

Due modi per rendere un valore non modificabile, con una differenza cruciale: const è risolto a compile-time (valore noto subito), readonly è risolto a runtime (valore assegnato nel costruttore).

public class ApiConfig { // const: valore fisso, noto e immutabile per sempre public const string ApiVersion = "v1"; // readonly: assegnato una volta sola nel costruttore public readonly Uri BaseUrl; public ApiConfig(string host) { BaseUrl = new Uri(host); // può dipendere da parametri! } }

La regola pratica

Usa const per valori primitivi davvero fissi (stringhe di versione, limiti numerici). Usa readonly per qualsiasi dipendenza o oggetto iniettato via costruttore — è il pattern che usi in ogni AuthService o ViewModel.

Funge da "portinaio" per l'accesso ai dati. Permette di esporre un valore pubblico, controllandone la lettura (get) e la scrittura (set).

[ObservableProperty] private string _email = string.Empty; // Tradotto (da MAUI e MVVM) equivarrebbe a: public string Email { get => _email; set { _email = value; OnPropertyChanged(); } }

Il caso d'uso in MotoLogPro

Grazie al Toolkit MAUI uso l'attributo sul campo privato, che genera in automatico la Proprietà Pubblica. Il magico blocco set avvisa in tempo reale l'interfaccia grafica quando l'utente digita qualcosa.

Un blocco di codice che racchiude una logica specifica o un'azione. Definisce il comportamento e "cosa sa fare" l'oggetto.

[HttpGet("GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { // Codice che elabora e ritorna dati return forecastList; }

Il caso d'uso in MotoLogPro

Nelle Web API un metodo pubblico agganciato a un attributo [HttpGet] diventa un Endpoint: un "pulsante virtuale" che l'app mobile preme tramite internet per farsi inviare informazioni (es. la lista delle moto).

Un delegato è un tipo che rappresenta un riferimento a un metodo (come un "puntatore a funzione" tipizzato). Action e Func sono delegati generici predefiniti in .NET che evitano di definire delegati custom.

// Action: metodo che non ritorna nulla (void) Action<string> showAlert = (msg) => Console.WriteLine(msg); showAlert("Login riuscito!"); // Func: metodo che ritorna un valore (ultimo T = tipo ritorno) Func<decimal, decimal, decimal> addIva = (price, rate) => price * (1 + rate); var total = addIva(100m, 0.22m); // → 122 // Uso tipico: callback e pipeline di elaborazione dati public void ProcessVehicles(Func<Vehicle, bool> filter) { var result = _vehicles.Where(filter); }

La connessione con LINQ e Lambda

Sono il cuore di LINQ: ogni metodo come .Where(), .Select(), .OrderBy() accetta in realtà un Func come parametro. Quando scrivi una lambda x => x.Brand == "Yamaha", stai passando una Func<Vehicle, bool> in modo compatto.

Una sintassi compatta per definire funzioni anonime (senza nome) inline. Il simbolo => si legge "tale che" o "va in". Sono il modo moderno e conciso per passare logica come parametro.

// Sintassi base: (parametri) => espressione Func<int, int> square = x => x * x; // In LINQ per filtrare, trasformare, ordinare var yamahas = vehicles .Where(v => v.Brand == "Yamaha") .OrderBy(v => v.Year) .Select(v => new { v.Model, v.Year }); // Metodi con corpo multi-riga Func<int, string> classify = km => { if (km < 10000) return "Nuova"; if (km < 50000) return "Usata"; return "Alta percorrenza"; };

Il caso d'uso in MotoLogPro

Ovunque ci sia LINQ, ci sono le lambda. Le uso per filtrare le moto per utente, per ordinare i log di manutenzione per data, o per trasformare le entità del DB nei DTO da inviare all'app mobile — tutto in poche righe leggibili e senza classi intermedie.

Parola chiave che lascia al compilatore il compito di dedurre automaticamente il tipo di una variabile dal valore assegnato. Non è "tipizzazione dinamica": il tipo è fisso e sicuro, determinato a compile-time.

// Con var: il compilatore deduce il tipo corretto var vehicles = await _context.Motorcycles.ToListAsync(); // → tipo effettivo: List<Motorcycle> var response = await _httpClient.PostAsJsonAsync("/login", dto); // → tipo effettivo: HttpResponseMessage // NON usare var quando il tipo non è ovvio dal contesto var x = GetData(); // ❌ poco chiaro List<Vehicle> x = GetData(); // ✅ esplicito

La regola d'oro

Usa var quando il tipo è ovvio dalla parte destra dell'espressione (es. risultati di LINQ, new, metodi con nome descrittivo). Evitalo quando il tipo non è immediatamente chiaro per chi legge il codice dopo di te.

Consentono di definire classi, metodi o interfacce "jolly", ritardando la specifica del tipo di dato (Type) finché non viene effettivamente utilizzato.

// Iniezione di una dipendenza (Metodo Generico) builder.Services.AddHttpClient<IAuthService, AuthService>(...); // Ritorno tipizzato asincrono (Classe Generica) public Task<IEnumerable<VehicleDto>> GetMyVehiclesAsync(); // Repository generico: un solo metodo, qualsiasi entità public async Task<T?> GetByIdAsync<T>(int id) where T : BaseEntity => await _context.Set<T>().FindAsync(id);

Il caso d'uso in MotoLogPro

C# è fortemente tipizzato (Type-Safe). Invece di avere un Task che restituisce "roba indefinita", i generics mi permettono di dire esattamente cosa ritornerà un'operazione asincrona, prevenendo errori già in fase di scrittura del codice.

Inizializzazione e flussi d'esecuzione

Un metodo speciale (avente lo stesso nome della classe) eseguito una sola volta, l'istante in cui l'oggetto prende vita (new). Prepara lo stato iniziale.

public class AuthService : IAuthService { private readonly HttpClient _httpClient; // Questo è il costruttore "classico" public AuthService(HttpClient httpClient) { _httpClient = httpClient; } }

La logica Enterprise

Riceve in ingresso le "dipendenze" (come la connessione al web) passate dal sistema e le salva nei campi interni. Era lo standard assoluto fino a C# 11.

L'evoluzione "leggera". Invece di scrivere il metodo del costruttore all'interno della classe, le dipendenze si dichiarano direttamente affianco al nome della classe.

public partial class LoginViewModel(IAuthService authService) { private readonly IAuthService _authService = authService; }

Il caso d'uso in MotoLogPro

Abbatte vertiginosamente le righe di codice (boilerplate) da scrivere quando devo iniettare servizi ripetitivi nei miei ViewModel.

Lo statement using garantisce che un oggetto che gestisce risorse esterne (file, connessioni DB, stream) venga sempre rilasciato correttamente al termine del blocco, anche in caso di eccezione.

// Sintassi classica: Dispose() chiamato in automatico alla fine del blocco using (var stream = new FileStream(path, FileMode.Open)) { // leggo il file... } // ← stream.Dispose() chiamato qui, sempre // Sintassi moderna (C# 8+): scope della variabile = fine del metodo using var connection = new SqlConnection(connectionString); await connection.OpenAsync(); // connection.Dispose() chiamato automaticamente quando il metodo termina

La connessione con IDisposable

Qualsiasi classe che implementa l'interfaccia IDisposable espone un metodo Dispose() con la logica di pulizia. Lo statement using è semplicemente zucchero sintattico che chiama quel metodo in modo garantito — è l'alternativa sicura al ricordarsi di farlo a mano in un finally.

C# distingue tra tipi che possono essere null (marcati con ?) e tipi che non lo possono essere. Con Nullable Reference Types abilitati, il compilatore avvisa quando rischi una NullReferenceException prima ancora di eseguire il codice.

// int non può essere null. int? sì. int km = null; // ❌ errore di compilazione int? kmOpt = null; // ✅ OK // Null-conditional operator ?. — cortocircuita se null string? name = user?.FirstName; // null se user è null, nessuna eccezione // Null-coalescing operator ?? — valore di fallback string display = user?.FullName ?? "Ospite"; // Null-forgiving operator ! — "so quello che faccio, fidati" var token = SecureStorage.GetAsync("token").Result!;

Il caso d'uso in MotoLogPro

Con <Nullable>enable</Nullable> nel .csproj, il compilatore mi avvisa se provo ad accedere a una proprietà di un oggetto che potrebbe essere null. Ho eliminato decine di potenziali crash in produzione ancora prima di avviare l'app, solo seguendo i warning del compilatore.

Una sintassi potente per testare un'espressione non solo contro un valore, ma anche contro un tipo, una struttura o una condizione, estraendo direttamente le informazioni in un'unica operazione.

// is-pattern: controlla il tipo E assegna la variabile in un colpo if (shape is Circle c) Console.WriteLine($"Raggio: {c.Radius}"); // switch expression (C# 8+): compatto, senza break, ritorna un valore var message = statusCode switch { 200 => "✅ OK", 401 => "🔒 Non autorizzato", 404 => "❓ Non trovato", >= 500 => "💥 Errore server", _ => "⚠️ Sconosciuto" }; // Property pattern: match su proprietà dell'oggetto var label = vehicle switch { { Fuel: FuelType.Elettrico, Year: > 2022 } => "⚡ EV Recente", { Km: > 100_000 } => "🔧 Alta percorrenza", _ => "🏍️ Standard" };

Il caso d'uso in MotoLogPro

Lo uso per mappare i codici di risposta HTTP delle Web API in messaggi leggibili, e per categorizzare i veicoli in base a più proprietà combinate senza if/else a cascata. Il codice risultante è più corto, privo di bug di break dimenticati e immediatamente leggibile.

Un sistema di query integrato nel linguaggio che permette di filtrare, trasformare e aggregare qualsiasi collezione (liste, array, risultati DB) con una sintassi uniforme e leggibile, senza scrivere cicli for a mano.

// Method syntax (la più usata in produzione) var result = vehicles .Where(v => v.UserId == currentUserId) .OrderByDescending(v => v.Year) .Select(v => new VehicleDto { Id = v.Id, Brand = v.Brand, Model = v.Model }) .ToListAsync(); // eseguito UNA SOLA VOLTA sul DB (deferred execution) // Aggregazioni immediate int count = vehicles.Count(v => v.Fuel == FuelType.Elettrico); decimal avgKm = vehicles.Average(v => v.Km); bool hasOld = vehicles.Any(v => v.Year < 2000);

Il caso d'uso in MotoLogPro

Con Entity Framework Core, LINQ non gira in memoria: viene tradotto in SQL ottimizzato e inviato al database. Scrivo codice C# tipizzato e leggibile, il framework si occupa di generare la query SQL corretta. Nessun SQL scritto a mano, zero rischi di SQL injection.

Un blocco di codice progettato per avviare compiti "lenti" (API, DB, I/O) mettendo in attesa solo se stesso (tramite await), senza paralizzare il dispositivo dell'utente.

public async Task<bool> LoginAsync(string email, string pwd) { var response = await _httpClient.PostAsJsonAsync("/login", dto); return response.IsSuccessStatusCode; }

Il caso d'uso in MotoLogPro

Nelle app mobile è obbligatorio: permette al "thread della UI" di continuare a far girare l'animazione di caricamento mentre il sistema dialoga pazientemente col server web nel background.

Un meccanismo cooperativo per segnalare a un'operazione asincrona che dovrebbe interrompersi. Non la ferma con la forza: il codice deve controllare il token e decidere quando fermarsi.

// Lato Controller: il framework inietta il token automaticamente [HttpGet] public async Task<IActionResult> GetVehiclesAsync(CancellationToken ct) { var vehicles = await _context.Motorcycles .ToListAsync(ct); // si interrompe se il client chiude la connessione return Ok(vehicles); } // Lato client: timeout manuale dopo 10 secondi using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); var data = await _service.GetVehiclesAsync(cts.Token);

Perché è importante

Senza CancellationToken, se un utente naviga via da una pagina mentre una query è in esecuzione, il server continua a lavorare inutilmente sprecando risorse. Passando il token di ASP.NET Core alla query EF Core, la chiamata al DB viene annullata automaticamente se il client si disconnette.

Permette di costruire sequenze "pigre" (lazy): gli elementi vengono generati uno alla volta solo quando richiesti, senza creare una lista completa in memoria. Il metodo si "mette in pausa" ad ogni yield e riprende alla chiamata successiva.

// Senza yield: crea TUTTA la lista in memoria prima di restituirla IEnumerable<int> GetEvenNumbers(int max) { var list = new List<int>(); for (int i = 0; i <= max; i += 2) list.Add(i); return list; } // Con yield: produce UN elemento alla volta, solo se serve IEnumerable<int> GetEvenNumbers(int max) { for (int i = 0; i <= max; i += 2) yield return i; // pausa qui, riprende alla prossima iterazione } // Se ci fermo al 3° elemento, gli altri NON vengono mai calcolati var first3 = GetEvenNumbers(1_000_000).Take(3); // 0, 2, 4

Quando usarlo

Ideale per pipeline di dati, validazioni con più regole (restituisci gli errori uno alla volta), o quando lavori con sequenze potenzialmente infinite o molto grandi dove caricare tutto in memoria sarebbe costoso. In LINQ, molti operatori come .Where() e .Select() usano yield internamente — è per questo che LINQ è lazy per default.

Il giubbotto antiproiettile per il codice. Tenta un'esecuzione (try), cattura eventuali esplosioni o errori inaspettati (catch), e assicura una pulizia finale (finally).

try { IsBusy = true; await _authService.LoginAsync(Email, Pwd); } catch (HttpRequestException ex) when (ex.StatusCode == 401) { ErrorMessage = "Credenziali non valide."; } catch (Exception ex) { await Shell.Current.DisplayAlert("Errore", ex.Message, "OK"); } finally { IsBusy = false; }

Il caso d'uso in MotoLogPro

Nota il catch when: permette di filtrare le eccezioni non solo per tipo ma anche per condizione, gestendo il 401 separatamente dagli altri errori di rete. Il finally spegne a prescindere l'indicatore di caricamento — anche se il catch stesso lancia un'eccezione.

Pattern architetturali e dati

Un paradigma in cui una classe non "costruisce da sola" i suoi attrezzi (con new), ma li richiede in ingresso nel suo costruttore.

// 1. All'avvio dell'app registro il servizio (MauiProgram.cs) builder.Services.AddHttpClient<IAuthService, AuthService>(); // 2. Il ViewModel riceve il servizio in "Iniezione" (LoginViewModel.cs) public LoginViewModel(IAuthService authService) { ... }

Il caso d'uso in MotoLogPro

È il cuore di .NET. Gestendo tutto il "magazzino attrezzi" (IoC Container) da un unico file, posso cambiare il motore del software senza toccare minimamente le pagine o le classi.

"Etichette" speciali da applicare alle proprietà. Servono sia per la validazione automatica (Frontend) che per mappare lo schema del Database (Backend).

[Required] [MaxLength(17)] public string Vin { get; set; } = string.Empty; // Telaio [ForeignKey(nameof(UserId))] public virtual ApplicationUser? User { get; set; }

Il caso d'uso in MotoLogPro

Le uso nelle Entità del database WebAPI per dire con precisione ad Entity Framework Core che il telaio moto è obbligatorio e lungo max 17 caratteri, configurando contemporaneamente la chiave esterna per la tabella utente.

Tooling specifico e .NET MAUI

Un attributo del CommunityToolkit.Mvvm che converte in automatico un normale metodo C# in un Comando eseguibile direttamente al click di un bottone dall'interfaccia UI.

[RelayCommand] private async Task LoginAsync() { if (IsBusy) return; // Logica del login... }

Il caso d'uso in MotoLogPro

Nel passato bisognava scrivere decine di righe extra creando una classe intera chiamata ICommand. Oggi basta questa riga e nello XAML della pagina ti basta agganciare Command="{Binding LoginCommand}"!

Un sistema di routing intuitivo, basato su stringhe URL, usato da MAUI per spostarsi tra le schermate in modo fluido gestendo lo storico (Back Stack).

// I doppi slash "//" indicano un instradamento ASSOLUTO await Shell.Current.GoToAsync("//DashboardPage");

Il caso d'uso in MotoLogPro

Dopo un login andato a buon fine, uso i doppi //. Questo "resetta" la storia di navigazione: se l'utente preme il tasto indietro, l'app viene minimizzata invece di riportarlo alla schermata di Login.

API nativa multi-piattaforma del pacchetto "Essentials" per salvare piccoli pezzi di testo cifrati usando gli standard di massima sicurezza del sistema ospite.

if (!string.IsNullOrEmpty(result.AccessToken)) { await SecureStorage.SetAsync("auth_token", result.AccessToken); }

Il caso d'uso in MotoLogPro

Lo sfrutto in AuthService.cs per salvare il token generato al login. Utilizza internamente la crittografia basata sul Keystore (Android) e sul Keychain (iOS). I dati salvati non sono visibili in nessun log o navigatore di file del dispositivo.

Nessun termine trovato

Prova con un termine diverso o rimuovi il filtro per tag.