C#: atestat și pregătire ONTI

C#: proiect bibliotecă cu împrumut și salvare XML

Construiește un proiect C# de bibliotecă, cu împrumut, returnare și salvare XML. Verifică identitatea cărților și persistența prin închidere și redeschidere.

Acest proiect unește modelul, lista vizuală și persistarea. Avem două exemplare identificate prin ID și o stare Disponibila. Utilizatorul selectează o carte, o împrumută sau o returnează, apoi salvează catalogul. Proiectul este local și monoutilizator; nu promite rezervări concurente sau evidență completă a cititorilor.

Cum lucrezi cu exemplul

Exemplul complet se compilează ca Windows Forms App pe .NET Framework 4.8, cu C# 7.3. Deschide Lesson.csproj din arhivă în Visual Studio pe Windows. Rulează scenariile descrise mai jos; verificarea compilării nu înlocuiește testarea ferestrei și a dispozitivelor folosite.

Descarcă sursele și instrucțiunile.

Ideea și pașii de implementare

BindingList păstrează modelul. ListBox afișează textul furnizat de obiect, dar operațiile folosesc obiectul selectat, nu textul. După schimbarea stării reîmprospătăm legarea, fiindcă modelul simplificat nu implementează notificarea proprietăților.

Fișierul se află în LocalApplicationData/Algo25Biblioteca, un director al utilizatorului. La pornire, dacă există un catalog, îl validăm într-o listă temporară: rădăcină corectă, ID pozitiv unic și titlu nevid. Numai după succes înlocuim setul inițial. O eroare de citire dezactivează salvarea pentru a nu suprascrie datele necitite. Copia de backup este păstrată la înlocuire.

Exemplu și rezultat așteptat

Împrumută Algoritmi, salvează, închide și redeschide. Cartea trebuie să apară ocupată. Returneaz-o și salvează din nou: starea devine disponibilă. A doua apăsare Împrumut pe o carte ocupată nu schimbă nimic. Titlurile și ID-urile sunt păstrate în XML.

Schemă pentru lecția 41: ID 1: disponibil; Împrumut → ocupat; Salvare catalog.xml; Repornire → tot ocupat.ID 1: disponibil → Împrumut → ocupat → Salvare catalog.xml → Repornire → tot ocupat.Mărește schema ↗

Codul complet

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new LessonForm());
    }
}
class Book {
    public int Id {get;set;} public string Title {get;set;} public bool Available {get;set;}
    public override string ToString() {return Id + ": " + Title + (Available ? " [disponibila]" : " [ocupata]");}
}
class LessonForm : Form
{
readonly BindingList<Book> books=new BindingList<Book>();
    readonly BindingSource source=new BindingSource();
    readonly ListBox list=new ListBox {Dock=DockStyle.Fill};
    readonly string folder=Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"Algo25Biblioteca");
    bool loadOk=true;
    public LessonForm() {
        Text="Biblioteca locala";ClientSize=new Size(600,320);
        books.Add(new Book {Id=1,Title="Algoritmi",Available=true});books.Add(new Book {Id=2,Title="Grafuri",Available=true});
        try {
            string file=Path.Combine(folder,"catalog.xml");
            if(File.Exists(file)) {
                var doc=System.Xml.Linq.XDocument.Load(file);var next=new List<Book>();var ids=new HashSet<int>();
                if(doc.Root.Name!="catalog")throw new FormatException("Radacina invalida");
                foreach(var x in doc.Root.Elements("book")) {
                    int id=(int)x.Attribute("id");string title=(string)x.Attribute("title");bool available=(bool)x.Attribute("available");
                    if(id<=0 || !ids.Add(id) || string.IsNullOrWhiteSpace(title))throw new FormatException("Carte invalida");
                    next.Add(new Book {Id=id,Title=title,Available=available});
                }
                books.Clear();foreach(var book in next)books.Add(book);
            }
        } catch(Exception ex) when(ex is IOException || ex is System.Xml.XmlException || ex is FormatException ||
            ex is ArgumentException || ex is InvalidOperationException || ex is UnauthorizedAccessException) {
            loadOk=false;MessageBox.Show("Catalogul nu a putut fi incarcat. Salvarea este blocata.");
        }
        source.DataSource=books;list.DataSource=source;
        var bar=new FlowLayoutPanel {Dock=DockStyle.Top,Height=42};
        var borrow=new Button {Text="Imprumut"};var giveBack=new Button {Text="Returnare"};var save=new Button {Text="Salvare",Enabled=loadOk};
        borrow.Click += (s,e) => Change(false);giveBack.Click += (s,e) => Change(true);save.Click += Save;
        bar.Controls.AddRange(new Control[] {borrow,giveBack,save});Controls.Add(list);Controls.Add(bar);
    }
    void Change(bool available) {
        var book=list.SelectedItem as Book;if(book==null || book.Available==available)return;
        book.Available=available;source.ResetBindings(false);
    }
    void Save(object sender,EventArgs e) {
        if(!loadOk)return;
        try {
            Directory.CreateDirectory(folder);string file=Path.Combine(folder,"catalog.xml"),temp=file+".tmp";
            var doc=new System.Xml.Linq.XDocument(new System.Xml.Linq.XElement("catalog",books.Select(b=>
                new System.Xml.Linq.XElement("book",new System.Xml.Linq.XAttribute("id",b.Id),
                    new System.Xml.Linq.XAttribute("title",b.Title),new System.Xml.Linq.XAttribute("available",b.Available)))));
            doc.Save(temp);if(File.Exists(file))File.Replace(temp,file,file+".bak");else File.Move(temp,file);
            MessageBox.Show(this,"Salvat.");
        } catch(IOException) {MessageBox.Show(this,"Salvarea a esuat.");}
          catch(UnauthorizedAccessException) {MessageBox.Show(this,"Acces refuzat.");}
    }
    protected override void Dispose(bool disposing) {if(disposing)source.Dispose();base.Dispose(disposing);}
}

Greșeli și limite

Nu testa persistarea numai prin existența fișierului: verifică starea după o pornire nouă. Nu transforma un fișier corupt într-un catalog gol salvat automat. În acest exemplu salvarea este explicită; închiderea fără Salvare pierde schimbările din memorie. Pentru utilizare reală poți adăuga avertizarea de modificări nesalvate.

Exersează

Parcurge succesiv împrumut, refuz repetat, returnare și repornire. Creează o copie a XML-ului și introdu un ID duplicat: încărcarea trebuie refuzată, iar salvarea blocată. Extinde apoi modelul cu cititorul și data, păstrând regulile în operații separate. Documentează limitarea la un singur utilizator.

Continuă parcursul

Parcursul complet de 50 de lecții

Bazele C#

Lecția precedentă: C#: export CSV cu citare și protecție pentru foi de calcul

Lecția următoare: C#: proiect gestiune cu stoc și operații verificabile.

Referință: documentația Microsoft.

Întrebări frecvente

De ce salvez în LocalApplicationData?

Este un loc destinat datelor utilizatorului, fără dependență de folderul personal al autorului proiectului.

Ce se întâmplă la un XML invalid?

Încărcarea este refuzată și salvarea este blocată pentru a proteja datele necitite.

Este un sistem de bibliotecă complet?

Este un proiect didactic funcțional pentru stări și persistare, care poate fi extins cu cititori și istoric.

Un pas mai departe

Autentifică-te pentru a păstra progresul în cont.

Exersează cu o problemă →