My Library

No we can create something usefull

My Library project

Objective

Create a library of my books or books I want to read...

Properties of a book

Let's start...

#01 Make a new class "MyLibrary" in the "Exercises" folder

Alt text

#02 Adjust Program.cs file accordingly

Alt text

We need a class to store data

#03 Create a new Folder "Models" under the "Exercises" folder and put a BookModel.cs class into it

Alt text

#04 Add properties to the model class

Alt text

namespace AbcItNetFramework.Exercises.Models
{
            internal class BookModel
    {
            public string Name { get; set; }

            public string Author { get; set; }

            public string Genre { get; set; }

            public string Price { get; set; }

            public string ImgUrl { get; set; }

            public string About { get; set; }
    }
}

#05 Now we need a way how to enter data - we have console app so we have to put properties one by one

using System;
using AbcItNetFramework.Exercises.Models;

namespace AbcItNetFramework.Exercises
{
            internal class MyLibrary
    {
            public void Run()
        {
            var book = new BookModel();

            Console.WriteLine("Enter the name of the Book:");
            book.Name = Console.ReadLine();

            Console.WriteLine("Enter the author:");
            book.Author = Console.ReadLine();

            Console.WriteLine("Enter the genre:");
            book.Genre = Console.ReadLine();

            Console.WriteLine("Enter the price:");
            book.Price = Console.ReadLine();

            Console.WriteLine("Enter the imgage:");
            book.ImgUrl = Console.ReadLine();

            Console.WriteLine("Enter the description:");
            book.About = Console.ReadLine();

            Console.WriteLine(book);
            Console.ReadKey(true);
        }
    }
}

If we run the application and fill all the entries...

Alt text

#06 Just a small improvement

Alt text

namespace AbcItNetFramework.Exercises.Models
{
            internal class BookModel
    {
            public string Name { get; set; }

            public string Author { get; set; }

            public string Genre { get; set; }

            public string Price { get; set; }

            public string ImgUrl { get; set; }

            public string About { get; set; }

            public override string ToString()
        {
            return $"Name: {Name}, Author: {Author}, Genre: {Genre}, Price: { Price}, Image: {ImgUrl}, About: {About}";
        }
    }
}

Now if we run the app again:

Alt text

#07 OK - but we need to enter many books - not just one

Alt text

using System;
using System.Collections.Generic;
using AbcItNetFramework.Exercises.Models;

namespace AbcItNetFramework.Exercises
{
            internal class MyLibrary
    {
            public void Run()
        {
            var books = new List<BookModel>();
            var stopEnteringBooks = false;

            while (!stopEnteringBooks)
            {
            var book = new BookModel();

                Console.WriteLine("Enter the name of the Book:");
                book.Name = Console.ReadLine();

                Console.WriteLine("Enter the author:");
                book.Author = Console.ReadLine();

                Console.WriteLine("Enter the genre:");
                book.Genre = Console.ReadLine();

                Console.WriteLine("Enter the price:");
                book.Price = Console.ReadLine();

                Console.WriteLine("Enter the imgage:");
                book.ImgUrl = Console.ReadLine();

                Console.WriteLine("Enter the description:");
                book.About = Console.ReadLine();

                books.Add(book);

                Console.WriteLine("Continue entering books? Y/N");
            var continueEntering = Console.ReadLine();
                stopEnteringBooks = continueEntering == "N";

                Console.WriteLine("--------------");
            }

            Console.WriteLine("My Library:");

            foreach (var book in books)
            {
                Console.WriteLine();
                Console.WriteLine(book);
            }

            Console.ReadKey(true);
        }
    }
}

#08 Not very useful - every time we close the app all the data disappeares

What ar the opptions how to save data permanently

#09 Serialization

Create a new method for saving

Alt text

#10 Use external package

Most of the common probles are already solved by someone else - don't invent a wheel 🥱

Alt text

Alt text

Add using(s)

Alt text

#11 Finally save the data

Alt text

#11 Run the app, fill some data and explore the .json file:

[
            {
            "Name": "Armagedon",
            "Author": "Henry O'Neil",
            "Genre": "Fantasy",
            "Price": "$73",
            "About": "Life after the end of civilization"
            },
            {
            "Name": "Paradise",
            "Author": "Marvin O'Harra",
            "Genre": "Romance",
            "Price": "$15",
            "About": "The book about perfect relationship"
            },
            {
            "Name": "ABC IT",
            "Author": "ACTUM company",
            "Genre": "Education",
            "Price": "$120",
            "About": "Teach yourself how to create applications"
            }
]

#12 Load the saved data - create a method

use explicit type (instad of var)

Alt text

create a new method

Alt text

implement the logic

Alt text

        private List<BookModel> LoadBooks()
        {
            if (!File.Exists("MyLibrary.json"))
            {
            return new List<BookModel>();
            }

            var booksJson = File.ReadAllText("MyLibrary.json");
            var books = JsonConvert.DeserializeObject<List<BookModel>>(booksJson);

            return books;
        }

#13 Code improvements

Repeated json file address - move it to constant

Alt text

Create constant for all occurrences - name it SerializationPath

Alt text

result

Alt text

using System;
using System.Collections.Generic;
using System.IO;
using AbcItNetFramework.Exercises.Models;
using Newtonsoft.Json;

namespace AbcItNetFramework.Exercises
{
            internal class MyLibrary
    {
            private const string SerializationPath = "MyLibrary.json";

            public void Run()
        {
            List<BookModel> books = LoadBooks();
            var stopEnteringBooks = false;

            while (!stopEnteringBooks)
            {
            var book = new BookModel();

                Console.WriteLine("Enter the name of the Book:");
                book.Name = Console.ReadLine();

                Console.WriteLine("Enter the author:");
                book.Author = Console.ReadLine();

                Console.WriteLine("Enter the genre:");
                book.Genre = Console.ReadLine();

                Console.WriteLine("Enter the price:");
                book.Price = Console.ReadLine();

                Console.WriteLine("Enter the description:");
                book.About = Console.ReadLine();

                books.Add(book);

                Console.WriteLine("Continue entering books? Y/N");
            var continueEntering = Console.ReadLine();
                stopEnteringBooks = continueEntering == "N";

                Console.WriteLine("--------------");
            }

            Console.WriteLine("My Library:");

            foreach (var book in books)
            {
                Console.WriteLine();
                Console.WriteLine(book);
            }

            SaveBooks(books);

            Console.ReadKey(true);
        }

            private List<BookModel> LoadBooks()
        {
            if (!File.Exists(SerializationPath))
            {
            return new List<BookModel>();
            }

            var booksJson = File.ReadAllText(SerializationPath);
            var books = JsonConvert.DeserializeObject<List<BookModel>>(booksJson);

            return books;
        }

            private void SaveBooks(List<BookModel> books)
        {
            var result = JsonConvert.SerializeObject(books, Formatting.Indented);
            File.WriteAllText(SerializationPath, result);

        }
    }
}

#14 Create a speparate method for creating a new book

Alt text

#15 Create a separate method for listing the books

Alt text

#16 Use the new method at the beginning of code flow

Alt text

using System;
using System.Collections.Generic;
using System.IO;
using AbcItNetFramework.Exercises.Models;
using Newtonsoft.Json;

namespace AbcItNetFramework.Exercises
{
            internal class MyLibrary
    {
            private const string SerializationPath = "MyLibrary.json";

            public void Run()
        {
            List<BookModel> books = LoadBooks();

            ListAllBooks(books);

            var stopEnteringBooks = false;

            while (!stopEnteringBooks)
            {
                CreateNewBook(books);

                Console.WriteLine("Continue entering books? Y/N");
            var continueEntering = Console.ReadLine();
                stopEnteringBooks = continueEntering == "N";

                Console.WriteLine("--------------");
            }

            ListAllBooks(books);

            SaveBooks(books);

            Console.ReadKey(true);
        }

            private static void ListAllBooks(List<BookModel> books)
        {
            Console.WriteLine("My Library:");

            foreach (var book in books)
            {
                Console.WriteLine();
                Console.WriteLine(book);
            }

            Console.WriteLine();
        }

            private static void CreateNewBook(List<BookModel> books)
        {
            var book = new BookModel();

            Console.WriteLine("Enter the name of the Book:");
            book.Name = Console.ReadLine();

            Console.WriteLine("Enter the author:");
            book.Author = Console.ReadLine();

            Console.WriteLine("Enter the genre:");
            book.Genre = Console.ReadLine();

            Console.WriteLine("Enter the price:");
            book.Price = Console.ReadLine();

            Console.WriteLine("Enter the description:");
            book.About = Console.ReadLine();

            books.Add(book);
        }

            private List<BookModel> LoadBooks()
        {
            if (!File.Exists(SerializationPath))
            {
            return new List<BookModel>();
            }

            var booksJson = File.ReadAllText(SerializationPath);
            var books = JsonConvert.DeserializeObject<List<BookModel>>(booksJson);

            return books;
        }

            private void SaveBooks(List<BookModel> books)
        {
            var result = JsonConvert.SerializeObject(books, Formatting.Indented);
            File.WriteAllText(SerializationPath, result);

        }
    }
}

Organize code better

All the exercises' classes have public method void Run() - we call it interface

But now skip interfaces and focus on classes and inheritance

#01 Create the Base class and move the very fist (now commented out) code there

notice the virtual keyword

Alt text

using System;

namespace AbcItNetFramework.Exercises
{
            internal class ExerciseBase
    {
            public virtual void Run()
        {
            Console.WriteLine("Hello ABC IT!");
            Console.ReadKey(true);
        }
    }
}

#02 Now inherit this class in all other exercise classes like:

Alt text

do it for all 3 exercise classes

#03 Now we have to change Program class


using AbcItNetFramework.Exercises;

namespace AbcItNetFramework
{
            internal class Program
    {

            static void Main()
        {
            ExerciseBase exercise;

            exercise = new ExerciseBase();

            //exercise = new TimeCounter();

            //exercise = new MathChallange();

            //exercise = new MyLibrary();

            exercise.Run();
        }
    }
}

Implement DB storage (replace serialization)

We will use SQLite

#01 Get the necessary nuget

Alt text

#02 Create a new folder for DB called Data

Alt text

#03 Open the Db Browser for SQLite and create new DB MyLibrary and add table Books and properties from the BookModel

Notice: We added one more property ID - don't forget to check all the checkboxes

Alt text

#04 Add Id into the Model class

Alt text

#05 Now we want to replace serialization with database

but we want to keep the old implementation - not delete it (we can switch between these two ways how to store data)

extract the current logic into a separate class called JsonStorage under the new folder Storage

Alt text

#06 Move the serialization logic into the new class

We have to make the methods public (to be visible outside)

Alt text

using System.Collections.Generic;
using System.IO;
using AbcItNetFramework.Exercises.Models;
using Newtonsoft.Json;

namespace AbcItNetFramework.Exercises.Storage
{
            internal class JsonStorage
    {
            private const string SerializationPath = "MyLibrary.json";

            public List<BookModel> LoadBooks()
        {
            if (!File.Exists(SerializationPath))
            {
            return new List<BookModel>();
            }

            var booksJson = File.ReadAllText(SerializationPath);
            var books = JsonConvert.DeserializeObject<List<BookModel>>(booksJson);

            return books;
        }

            public void SaveBooks(List<BookModel> books)
        {
            var result = JsonConvert.SerializeObject(books, Formatting.Indented);
            File.WriteAllText(SerializationPath, result);
        }
    }
}

#07 Now as we moved the methods to other class our code in MyLibrary.cs is broken

Alt text

Fix it - for now this way - later we'll make it better

Alt text

using System;
using System.Collections.Generic;
using AbcItNetFramework.Exercises.Models;
using AbcItNetFramework.Exercises.Storage;

namespace AbcItNetFramework.Exercises
{
            internal class MyLibrary : ExerciseBase
    {
            public override void Run()
        {
            var storage = new JsonStorage();

            List<BookModel> books = storage.LoadBooks();

            ListAllBooks(books);

            var stopEnteringBooks = false;

            while (!stopEnteringBooks)
            {
                CreateNewBook(books);

                Console.WriteLine("Continue entering books? Y/N");
            var continueEntering = Console.ReadLine();
                stopEnteringBooks = continueEntering == "N";

                Console.WriteLine("--------------");
            }

            ListAllBooks(books);

            storage.SaveBooks(books);

            Console.ReadKey(true);
        }

            private static void ListAllBooks(List<BookModel> books)
        {
            Console.WriteLine("My Library:");

            foreach (var book in books)
            {
                Console.WriteLine();
                Console.WriteLine(book);
            }

            Console.WriteLine();
        }

            private static void CreateNewBook(List<BookModel> books)
        {
            var book = new BookModel();

            Console.WriteLine("Enter the name of the Book:");
            book.Name = Console.ReadLine();

            Console.WriteLine("Enter the author:");
            book.Author = Console.ReadLine();

            Console.WriteLine("Enter the genre:");
            book.Genre = Console.ReadLine();

            Console.WriteLine("Enter the price:");
            book.Price = Console.ReadLine();

            Console.WriteLine("Enter the description:");
            book.About = Console.ReadLine();

            books.Add(book);
        }

    }
}

#8 The new DbStorage should behave just the same way as the current JsonStorage from outside - meaning both storages have to have the same interface - so extract it.

Alt text

Call it IStorage

Alt text

(We can see that inteface is similar to class - but it has methods without implementation)

#09 Now we create a new DbStorage class

Alt text

#10 Implement the inteface

Alt text

Visual Studio will prepare the methods for us

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AbcItNetFramework.Exercises.Models;

namespace AbcItNetFramework.Exercises.Storage
{
            internal class DbStorage : IStorage
    {
            public List<BookModel> LoadBooks()
        {
            throw new NotImplementedException();
        }

            public void SaveBooks(List<BookModel> books)
        {
            throw new NotImplementedException();
        }
    }
}

#11 Implement the methods

using System;
using System.Collections.Generic;
using System.Data.SQLite;
using AbcItNetFramework.Exercises.Models;

namespace AbcItNetFramework.Exercises.Storage
{
            internal class DbStorage : IStorage
    {
            const string dbPath = @"..\..\Data\MyLibrary.db";
            readonly string connectionString = $@"Data Source={dbPath};Version=3;";

            public List<BookModel> LoadBooks()
        {
            var result = new List<BookModel>();
            try
            {
            using (var connection = new SQLiteConnection(connectionString))
                {
                    connection.Open();
            var command = new SQLiteCommand("SELECT id, Name, Author, Genre, Price, About FROM Books", connection);

            using (var reader = command.ExecuteReader())
                    {
            while (reader.Read())
                        {
            var id = (int)reader["id"];
            var name = reader["Name"].ToString();
            var author = reader["Author"].ToString();
            var genre = reader["Genre"].ToString();
            var price = reader["Price"].ToString();
            var about = reader["About"].ToString();

                            result.Add(
            new BookModel
                                {
                                    Id = id,
                                    Name = name,
                                    Author = author,
                                    Genre = genre,
                                    Price = price,
                                    About = about
                                });
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            return result;
        }

            public void SaveBooks(List<BookModel> books)
        {
            try
            {
            using (var connection = new SQLiteConnection(connectionString))
                {
                    connection.Open();
            var query = "INSERT INTO Books (Name, Author, Genre, Price, About) VALUES (@Name, @Author, @Genre, @Price, @About)";
            foreach (var book in books)
                    {
            if (book.Id != 0)
                        {
            continue;
                        }

            using (var command = new SQLiteCommand(query, connection))
                        {
            // Use parameters to prevent SQL injection
                            command.Parameters.AddWithValue("@Name", book.Name);
                            command.Parameters.AddWithValue("@Author", book.Author);
                            command.Parameters.AddWithValue("@Genre", book.Genre);
                            command.Parameters.AddWithValue("@Price", book.Price);
                            command.Parameters.AddWithValue("@About", book.About);

            // Execute the command
                            command.ExecuteNonQuery();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

#12 Now we switch the usage of storages

Alt text

#13 If we now run the app we don't get any books as they are not in db yet. Let Enter some and look back to the DB

Alt text

#14 Lets improve the code a bit using constructor

Alt text

#15 Fix bug - Id should be long (two places)

Alt text

Alt text

Now you can switch between sorages by creating different instances in constructor. You can create as many as you want of other implementations of the IStorage