Hoàn thiện (2): xóa, lọc, tìm kiếm, xử lý file

    0

    Trong bài học này chúng ta tiếp tục hoàn thiện các chức năng chính như đã phân tích, bao gồm: bổ sung chức năng xóa dữ liệu, lọc dữ liệu, tự động tìm sách trong thư mục, mở file pdf từ chương trình, đánh dấu (bookmark) các cuốn sách đang đọc.

    NỘI DUNG CỦA BÀI Ẩn

    Thực hành 1: thêm chức năng xóa dữ liệu

    Chức năng xóa dữ liệu chưa được thực hiện ở các bài trước đây. Ở chức năng này chúng ta không xây dựng một lớp view riêng mà tận dụng khả năng của lớp MessageView (đã xây dựng ở bài này).

    Bước 1. Tạo phương thức Delete

    Xây dựng phương thức Delete trong lớp BookController như sau:

    public void Delete(int id, bool process = false)
    {
        if (process == false)
        {
            var b = Repository.Select(id);
            Confirm($"Do you want to delete this book ({b.Title})? ", $"do delete?id={b.Id}");
        }
        else
        {
            Repository.Delete(id);
            Success("Book deleted!");
        }
    }
    

    Trong phương thức Delete chúng ta vận dụng lớp MessageMessageView đã xây dựng ở bài trước để đưa ra các thông báo ngắn cho người dùng mà không cần xây dựng một lớp view riêng cho phương thức Delete.

    Bước 2. Bổ sung thêm route

    r.Register(route: "delete",
        action: p => controller.Delete(p["id"].ToInt()),
        help: "[delete ? id = <value>");
    r.Register(route: "do delete",
        action: p => controller.Delete(p["id"].ToInt(), true),
        help: "this route should be used only in code");

    Bước 3. Dịch và chạy thử với lệnh delete

    Kết quả thực hiện lệnh delete
    Kết quả thực hiện lệnh delete

    Thực hành 2: thêm chức năng lọc dữ liệu

    Ở phần thực hành này chúng ta bổ sung thêm một tính năng mới: lọc dữ liệu. Ở chức năng này, người dùng cung cấp một từ khóa bất kỳ. Chương trình sẽ tìm trong danh sách dữ liệu tất cả những cuốn sách mà tiêu đề, tác giả, nhà xuất bản, tag và mô tả có chứa từ khóa này.

    Bước 1. Thêm phương thức Filter vào lớp BookController

    public void Filter(string key)
    {
        var model = Repository.Select(key);
        if (model.Length == 0)
            Inform("No matched book found!");
        else
            Render(new BookListView(model));
    }

    Bước 2. Bổ sung thêm route

    r.Register(route: "filter",
         action: p => controller.Filter(p["key"]),
         help: "[filter ? key = <value>]rntìm sách theo từ khóa");

    Bước 3. Dịch và chạy thử chương trình với lệnh filter

    Kết quả thực hiện lệnh filter
    Kết quả thực hiện lệnh filter

    Khi lọc dữ liệu, bạn cũng có thể muốn sắp xếp dữ liệu theo tiêu chí nào đó, ví dụ theo tựa sách, theo tác giả, theo năm xuất bản. Bạn có thể đọc thêm về các thuật toán sắp xếp để tự thực hiện.

    Thực hành 3: thêm chức năng tìm sách trong thư mục

    Như đã mô tả ở bài 1, đây là chức năng giúp xây dựng dữ liệu sách tự động. Người dùng cung cấp một đường dẫn tới thư mục chứa các file sách, chương trình sẽ phát hiện tất cả các file pdf có trong đó và sử dụng tên và đường dẫn của các file này để tạo ra dữ liệu cơ bản về kho sách.

    Trong phần thực hành này, chúng ta sẽ tạo ra thêm một lớp điều khiển mới để thực hiện các chức năng liên quan tới file, thư mục.

    Bước 1. Xây dựng lớp ShellController

    Tạo mới file ShellController.cs trong thư mục Controllersdành cho lớp ShellController và viết code cho lớp ShellController như sau:

    using System.Diagnostics;
    using System.IO;
    namespace BookMan.ConsoleApp.Controllers
    {
        using DataServices;
        using Models;
        using Views;
        using Framework;
    
        internal class ShellController : ControllerBase
        {
            protected Repository Repository;
            public ShellController(SimpleDataAccess context)
            {
                Repository = new Repository(context);
            }
    
            public void Shell(string folder, string ext = "*.pdf")
            {
                if (!Directory.Exists(folder))
                {
                    Error("Folder not found!");
                    return;
                }
                var files = Directory.GetFiles(folder, ext ?? "*.pdf", SearchOption.AllDirectories);
                foreach (var f in files)
                {
                    Repository.Insert(new Book { Title = Path.GetFileNameWithoutExtension(f), File = f });
                }
                if (files.Length > 0)
                {
                    //Render(new BookListView(Repository.Select()));
                    Success($"{files.Length} item(s) found!");
                    return;
                }
                Inform("No item found!", "Sorry!");
            }          
        }
    }

    Ở bước này chúng ta xây dựng lớp ShellController kế thừa từ lớp ControllerBase đã xây dựng ở các bài trước.

    Bước 2. Bổ sung code cho ConfigRouter

    BookController controller = new BookController(context);
    ShellController shell = new ShellController(context);

    Bước này chúng ta chỉ bổ sung khai báo và khởi tạo một object của ShellController. Object này dùng để thực hiện các chức năng mới liên quan đến file và thư mục.

    Bước 3. Bổ sung route mới cho ConfigRouter

    r.Register(route: "add shell",
        action: p => shell.Shell(p["path"], p["ext"]),
        help: "[add shell ? path = <value>]");

    Bước 4. Dịch và chạy thử chương trình

    Kết quả thực hiện lệnh add shell
    Kết quả thực hiện lệnh add shell

    Trong phần thực hành trên chúng ta lần đầu áp dụng các lớp .NET hỗ trợ làm việc với hệ thống file của windows.

    Tất cả các lớp để làm việc với file trong .NET nằm trong không gian tên System.IO. Ba class chính để làm việc với hệ thống file là Directory (làm việc với thư mục), File (làm việc với file), Path (làm việc với đường dẫn).

    Lớp Directory

    Lớp Directory chứa hầu hết các phương thức tĩnh giúp làm việc với file và thư mục. Trong phần thực hành chúng ta đã sử dụng một số phương thức của lớp này giúp kiểm tra đường dẫn và giúp lấy danh sách file trong một thư mục.

    • Phương thức tĩnh Exists của lớp Directory: kiểm tra xem một đường dẫn tới thư mục có tồn tại hoặc chính xác không.
    if (!Directory.Exists(folder))
    {
        Error("Folder not found!");
        return;
    }
    • Phương thức tĩnh GetFiles của lớp Directory: tìm tất cả các file trong thư mục có phần mở rộng là pdf:
    Directory.GetFiles(folder, ext ?? "*.pdf", SearchOption.AllDirectories);

    Phương thức này sử dụng ba tham số:

    1. đường dẫn tới thư mục;
    2. mẫu tìm kiếm: mẫu văn bản mà phương thức GetFiles sử dụng trong quá trình tìm kiếm. GetFiles chỉ trả lại những file mà tên phù hợp với mẫu văn bản của tham số này.
    3. phạm vi tìm kiếm: xác định xem phương thức GetFiles chỉ tìm trong thư mục được chỉ định (TopDirectoryOnly) hay tìm cả trong các thư mục con của nó (AllDirectories).

    Kết quả thực hiện của phương thức này là một mảng string chứa tên đầy đủ (bao gồm cả đường dẫn) của các file tìm thấy.

    Lớp Path

    Lớp Path cũng chứa hầu hết các phương thức tĩnh giúp phân tích đường dẫn tới file hoặc thư mục. Ở phần thực hành chúng ta đã sử dụng một phương thức của lớp này để phân tách tên file khỏi đường dẫn và phần mở rộng.

    Phương thức GetFileNameWithoutExtension của lớp Path trích ra phần tên của file, bỏ phần đường dẫn và phần mở rộng.

    Repository.Insert(new Book { Title = Path.GetFileNameWithoutExtension(f), File = f });

    Tên file này được sử dụng tạm thời làm tiêu đề của sách (vì thường sách điện tử đặt tên file trùng với tiêu đề sách).

    Các phương thức của hai class Directory và Path đều tương đối dễ sử dụng. Bạn đọc có thể tự mình tìm hiểu các phương thức khác.

    Thực hành 4: thêm chức năng đọc sách từ chương trình

    Chức năng này cho phép mở file pdf bằng chương trình đọc pdf mặc định của windows (Acrobat Reader, Foxit Reader, v.v.). Chức này này tiện lợi cho người sử dụng vì không cần phải mở các thư mục để tìm đến file.

    Bước 1. Thêm phương thức vào ShellController

    public void Read(int id)
    {
        var book = Repository.Select(id);
        if (book == null)
        {
            Error("Book not found!");
            return;
        }
        if (!File.Exists(book.File))
        {
            Error("File not found!");
            return;
        }
        Process.Start(book.File);
        Success($"You are reading the book '{book.Title}'");
    }
    

    Bước 2. Bổ sung route vào ConfigRouter

    r.Register(route: "read",
         action: p => shell.Read(p["id"].ToInt()),
         help: "[read ? id = <value>]");

    Bước 3. Dịch và chạy thử chương trình

    Kết quả thực hiện lệnh read
    Kết quả thực hiện lệnh read

    Sau lệnh này, cuốn “A first course in discrete mathematics” sẽ được mở ra bằng chương trình đọc pdf mặc định trên windows.

    Thực hành 5: đánh dấu những cuốn sách đang đọc

    Chức năng này cho phép đánh dấu những cuốn sách đang đọc để có thể dễ dàng tìm đọc tiếp. Khi xây dựng lớp Book chúng ta có thuộc tính Reading kiểu bool dành cho chức năng này.

    Bước 1. Bổ sung phương thức vào lớp Repository

    public Book[] SelectMarked()
    {
        var list = new List<Book>();
        foreach (var b in Books)
        {
            if (b.Reading) list.Add(b);
        }
        return list.ToArray();
    }

    Bước 2. Bổ sung hai phương thức vào lớp BookController

    public void Mark(int id, bool read = true)
    {
        var book = Repository.Select(id);
        if (book == null)
        {
            Error("Book not found!");
            return;
        }
        book.Reading = read;
        Success($"The book '{book.Title}' are marked as { (read ? "READ" : "UNREAD")}");
    }
    
    public void ShowMarks()
    {
        var model = Repository.SelectMarked();
        var view = new BookListView(model);
        Render(view);
    }
    

    Bước 3. Bổ sung route vào ConfigRouter

    r.Register(route: "mark",
        action: p => controller.Mark(p["id"].ToInt()),
        help: "[mark ? id = <value>]");
    r.Register(route: "unmark",
        action: p => controller.Mark(p["id"].ToInt(), false),
        help: "[unmark ? id = <value>]");
    r.Register(route: "show marks",
        action: p => controller.ShowMarks(),
        help: "[show marks]");

    Bước 4. Dịch và chạy thử chương trình

    Kết quả thực hiện lệnh mark và show marks
    Kết quả thực hiện lệnh mark show marks

    Chức năng này tận dụng lại lớp BookListView đã xây dựng từ trước. Lớp này có thể hiển thị các cuốn sách đang đọc bằng màu Cyan, còn các cuốn sách khác hiện màu trắng.

    Thực hành 6: bổ sung khả năng xóa toàn bộ dữ liệu

    Bước 1. Thêm phương thức vào lớp Repository

    public void Clear()
    {
        _context.Books.Clear();
    }

    Bước 2. Thêm phương thức Clear vào ShellController

    public void Clear(bool process = false)
    {
        if (!process)
        {
            Confirm("Do you really want to clear the shell? ", "do clear");
            return;
        }
        Repository.Clear();
        Inform("The shell has been cleared");
    }

    Bước 3. Bổ sung các route

    r.Register(route: "clear",
        action: p => shell.Clear(),
        help: "[clear]rnUse with care");
    r.Register(route: "do clear",
        action: p => shell.Clear(true),
        help: "[clear]rnUse with care");

    Bước 4. Dịch và chạy thử chương trình với lệnh clear

    Kết quả thực hiện lệnh clear
    Kết quả thực hiện lệnh clear

    Kết luận

    Trong bài này chúng ta vận dụng các class xây dựng trong các bài trước để hoàn thiện hầu hết các chức năng đã phân tích. Qua bài này chúng ta có thể thấy được khả năng mở rộng của ứng dụng và sự rành mạch trong phân chia code. Khi cần bổ sung thêm chức năng mới, chúng ta xác định được ngay các code mới cần thêm vào đâu.

    Trong các bài sau chúng ta sẽ hoàn thiện nốt chức năng cuối cùng: lưu trữ dữ liệu.

    + Nếu bạn thấy site hữu ích, trước khi rời đi hãy giúp đỡ site bằng một hành động nhỏ để site có thể phát triển và phục vụ bạn tốt hơn.
    + Nếu bạn thấy bài viết hữu ích, hãy giúp chia sẻ tới mọi người.
    + Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần thảo luận cuối trang.
    Cảm ơn bạn!

    Subscribe
    Notify of
    guest
    0 Thảo luận
    Inline Feedbacks
    View all comments