Console MVC Library cho .NET (4): ví dụ minh họa

0

Trong hai bài trước của loạt bài này, chúng ta đã xây dựng xong một bộ thư viện lớp. Bộ thư viện này giúp dễ dàng áp dụng mô hình MVC cho ứng dụng console. Nếu không muốn sử dụng MVC, thư viện này cũng giúp đơn giản hóa việc lập trình với console.

Trong bài cuối cùng này chúng ta sẽ vận dụng thư viện để tạo ra một số ứng dụng nhỏ.

Loạt bài Xây dựng thư viện hỗ trợ ứng dụng Console:
Phần 1 – Giới thiệu chung
Phần 2 – Truy vấn, xây dựng router
Phần 3 – Hỗ trợ I/O với console, view, controller
Phần 4 – Ví dụ minh họa

Đơn giản hóa lập trình cho console

Dưới đây là code của một ví dụ rất đơn giản và quen thuộc. Có hai phương thức, Fact để tính giai thừa, Solve để giải phương trình bậc hai.

Bộ thư viện vừa xây dựng giúp chúng ta nhanh chóng có được một ứng dụng đầy đủ mà không cần quan tâm đến các chi tiết xử lý console nữa. Với bộ thư viện này chúng ta chỉ cần tập trung cho thuật toán/ cách thức xử lý.

using System;
using System.Text;

namespace Equation
{
    using Framework;

    internal class Program
    {
        private static void Main(string[] args)
        {
            Console.Title = "Sample App";
            Console.OutputEncoding = Encoding.UTF8;

            var app = new Application
            {
                Config = Config                
            };
            app.Run();
        }

        private static void Config()
        {
            var router = Router.Instance;
            router.Register("solve", Solve, "Solve a square equation\r\nsolve ? a=<> & b=<> & c=<>");
            router.Register("fact", Fact, "fact ? n=<>");
        }

        private static void Fact(Parameter parameter)
        {
            var n = parameter["n"].To<int>();
            var f = 1;
            for (var i = 1; i <= n; i++)
                f *= i;
            ViewHelper.WriteLine($"{n}! = {f}", ConsoleColor.Cyan);
        }

        private static void Solve(Parameter p)
        {
            var a = p["a"].To<double>();
            var b = p["b"].To<double>();
            var c = p["c"].To<double>();

            var d = b * b - 4 * a * c;
            if (d < 0)
            {
                ViewHelper.WriteLine("No real solution found", ConsoleColor.Red);
                return;
            }

            ViewHelper.WriteLine("Solutions:", ConsoleColor.Cyan);
            var x1 = (-b + Math.Sqrt(d)) / (2 * a);
            var x2 = (-b - Math.Sqrt(d)) / (2 * a);
            ViewHelper.WriteLine($"X1 = {x1}");
            ViewHelper.WriteLine($"X2 = {x2}");
        }
    }
}
Chạy thử nghiệm chương trình
Chạy thử nghiệm chương trình

Xây dựng ứng dụng quản lý Contact với MVC

Trong ví dụ này, chúng ta sẽ tạo ra một ứng dụng đơn giản giúp quản lý thông tin liên hệ. Code của ứng dụng này sẽ tổ chức theo mô hình MVC.

Tạo mới một project ConsoleApp đặt tên là ContactManager và cho tham chiếu đến thư viện Framework.

Tạo ra cấu trúc thư mục và các file mã nguồn như dưới đây:

Cấu trúc project ContactManager

Model – lớp Contact

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ContactManager.Models
{
    [Serializable]
    class Contact
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Phone { get; set; }
        public string Email { get; set; }
        public string Facebook { get; set; }
        public string Twitter { get; set; }
    }
}

Contact là một lớp C# cơ bản mô tả những thông tin liên hệ chính của cá nhân. Attribute [Serializable] giúp sử dụng Binary serializer để ghi vào file sau này.

View – List, Single, Create, Update

Trong thư mục Views chúng ta tạo ra 4 class dành cho 4 loại view cơ bản nhất của ứng dụng. ListView hiển thị danh sách contact. SingleView hiển thị chi tiết của một contact. CreateView giúp nhập thông tin của một contact mới. UpdateView giúp cập nhật thông tin của một contact.

ListView class

using System;

namespace ContactManager.Views
{
    using Framework;
    using Models;

    internal class ListView : ViewBase<Contact[]>
    {
        public ListView(Contact[] model) : base(model)
        {

        }

        public override void Render()
        {
            if (Model.Length == 0)
            {
                ViewHelper.WriteLine("Sorry, no contact found", ConsoleColor.Magenta);
                return;
            }

            ViewHelper.WriteLine($"{Model.Length} contact(s) found", ConsoleColor.Green);
            foreach (var c in Model)
            {
                ViewHelper.WriteLine($"[{c.Id}] {c.Name} | {c.FirstName} {c.LastName}");
            }
            ViewHelper.WriteLine($"{Model.Length} contact(s) found", ConsoleColor.Green);
        }
    }
}

SingleView class

using System;

namespace ContactManager.Views
{
    using Framework;
    using Models;

    internal class SingleView : ViewBase<Contact>
    {
        public SingleView(Contact model) : base(model)
        {
        }

        public override void Render()
        {
            if(Model == null)
            {
                ViewHelper.WriteLine("Sorry, contact not found.", ConsoleColor.Magenta);
                return;
            }

            Model.Name.WriteLine(ConsoleColor.Magenta);
            $"ID:           {Model.Id}".WriteLine(ConsoleColor.Yellow);
            $"First name:   {Model.FirstName}".WriteLine(ConsoleColor.Yellow);
            $"Last name:    {Model.LastName}".WriteLine(ConsoleColor.Yellow);
            $"Phone:        {Model.Phone}".WriteLine(ConsoleColor.Yellow);
            $"Email:        {Model.Email}".WriteLine(ConsoleColor.Yellow);
            $"Facebook:     {Model.Facebook}".WriteLine(ConsoleColor.Yellow);
            $"Twitter:      {Model.Twitter}".WriteLine(ConsoleColor.Yellow);
        }
    }
}

CreateView class

namespace ContactManager.Views
{
    using Framework;

    internal class CreateView : ViewBase
    {
        public CreateView()
        {
        }

        public override void Render()
        {
            var id = "ID".Read<int>();
            var name = "Name".Read();
            var firstname = "First name".Read();
            var lastname = "Last name".Read();
            var email = "Email".Read();
            var phone = "Phone".Read();
            var facebook = "Facebook".Read();
            var twitter = "Twitter".Read();
            Router.Forward($"do create ? id={id} & name={name} & firstname={firstname} & lastname={lastname} & email={email} & phone={phone} & facebook={facebook} & twitter={twitter}");
        }
    }
}

UpdateView

namespace ContactManager.Views
{
    using Framework;
    using Models;

    internal class UpdateView : ViewBase<Contact>
    {
        public UpdateView(Contact model) : base(model)
        {
        }

        public override void Render()
        {
            if(Model == null)
            {
                ViewHelper.WriteLine("Sorry, no contact found", System.ConsoleColor.Magenta);
                return;
            }

            var id = "ID".Update(Model.Id);
            var name = "Name".Update(Model.Name);
            var firstname = "First name".Update(Model.FirstName);
            var lastname = "Last name".Update(Model.LastName);
            var email = "Email".Update(Model.Email);
            var phone = "Phone".Update(Model.Phone);
            var facebook = "Facebook".Update(Model.Facebook);
            var twitter = "Twitter".Update(Model.Twitter);
            Router.Forward($"do update ? id={id} & name={name} & firstname={firstname} & lastname={lastname} & email={email} & phone={phone} & facebook={facebook} & twitter={twitter}");
        }
    }
}

ContactController class

using System.Collections.Generic;
using System.Linq;

namespace ContactManager.Controllers
{
    using Framework;
    using Models;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    using Views;

    internal class ContactController : ControllerBase
    {
        private List<Contact> _contacts = new List<Contact>();

        public void List()
        {
            var model = _contacts.ToArray();
            var view = new ListView(model);
            Render(view);
        }

        public void Single(Parameter p)
        {
            var id = p["id"].To<int>();
            var model = _contacts.FirstOrDefault(c => c.Id == id);
            var view = new SingleView(model);
            Render(view);
        }

        public void Create()
        {
            var view = new CreateView();
            Render(view);
        }

        public void Create(Parameter p)
        {
            var contact = Convert(p);
            _contacts.Add(contact);
            Success("New contact has been created!");
        }

        public void Update(int id)
        {
            var model = _contacts.FirstOrDefault(c => c.Id == id);
            var view = new UpdateView(model);
            Render(view);
        }

        public void Update(Parameter p)
        {
            var contact = Convert(p);
            var model = _contacts.FirstOrDefault(c => c.Id == contact.Id);
            _contacts.Remove(model);
            _contacts.Add(contact);
            Success("Contact has been updated!");
        }

        public void Delete(int id)
        {
            var model = _contacts.FirstOrDefault(c => c.Id == id);
            if (model == null)
            {
                Error("Contact not found!");
                return;
            }
            Confirm("Delete this contact? [y/n] ", $"do delete ? id={id}");
        }

        public void Delete(Parameter p)
        {
            var id = p["id"].To<int>();
            var model = _contacts.FirstOrDefault(c => c.Id == id);
            _contacts.Remove(model);
            Success("Contact has been removed!");
        }

        public void Save()
        {
            using (FileStream stream = File.OpenWrite("data.dat"))
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(stream, _contacts);
                Inform("Data has been saved!");
            }
        }

        public void Load()
        {
            if (!File.Exists("data.dat"))
            {
                Inform("Data file not exists");
                return;
            }

            using (FileStream stream = File.OpenRead("data.dat"))
            {
                var formatter = new BinaryFormatter();
                _contacts = formatter.Deserialize(stream) as List<Contact>;
                Inform($"Data have been loaded. {_contacts.Count} item(s) found.");
            }

        }

        private Contact Convert(Parameter p)
        {
            var id = p["id"].To<int>();
            var name = p["name"];
            var firstname = p["firstname"];
            var lastname = p["lastname"];
            var phone = p["phone"];
            var email = p["email"];
            var facebook = p["facebook"];
            var twitter = p["twitter"];

            var contact = new Contact
            {
                Id = id,
                Name = name,
                FirstName = firstname,
                LastName = lastname,
                Phone = phone,
                Email = email,
                Facebook = facebook,
                Twitter = twitter
            };

            return contact;
        }
    }
}

Program class

namespace ContactManager
{
    using Controllers;
    using Framework;
    using System;

    internal class Program
    {
        private static void Config()
        {
            var controller = new ContactController();
            var _router = Router.Instance;

            _router.Register("list", p => controller.List());
            _router.Register("detail", p => controller.Single(p));
            _router.Register("create", p => controller.Create());
            _router.Register("do create", p => controller.Create(p));
            _router.Register("update", p => controller.Update(p["id"].To<int>()));
            _router.Register("do update", p => controller.Update(p));
            _router.Register("delete", p => controller.Delete(p["id"].To<int>()));
            _router.Register("do delete", p => controller.Delete(p));
            _router.Register("save", p => controller.Save());
            _router.Register("load", p => controller.Load());
        }

        private static void Main()
        {
            Console.Title = "Contact Manager";

            var app = new Application { Config = Config };
            app.Run();
        }
    }
}

Chạy thử nghiệm

Chương trình vừa xây dựng có thể thực hiện các lệnh: list, detail, create, update, delete, save, load, help.

Ba lệnh đặc biệt, do create, do update, do delete chỉ dùng từ view thông qua code.

Kết quả chạy thử nghiệm ứng dụng:

Kết quả thực hiện một số lệnh của chương trình

Chúng ta thấy ứng dụng console này cũng không đến nỗi nhàm chán lắm. Việc tạo ra nó khá nhanh gọn. Ứng dụng chạy cũng ổn định (ít nhất là không có crash giữa chừng).

Kết luận

Qua loạt 4 bài này chúng ta đã tạo ra một bộ thư viện giúp phát triển ứng dụng console. Bộ thư viện này giúp đơn giản hóa lập trình cho console. Thư viện cũng hỗ trợ các thành phần cơ bản của mô hình MVC, giúp tổ chức code tốt hơn.

Đây là phiên bản thu gọn và đơn giản hóa của tập bài giảng Tự học lập trình C# qua dự án mini. Bài giảng này giúp tự học C# một cách hiệu quả thông qua việc tự mình xây dựng một bộ thư viện tương tự và áp dụng để giải quyết một bài toán cụ thể.

Loạt bài Xây dựng thư viện hỗ trợ ứng dụng Console:
Phần 1 – Giới thiệu chung
Phần 2 – Truy vấn, xây dựng router
Phần 3 – Hỗ trợ I/O với console, view, controller
Phần 4 – Ví dụ minh họa

* Bản quyền bài viết thuộc về Tự học ICT. Đề nghị tôn trọng bản quyền. DMCA.com Protection Status
Subscribe
Notify of
guest
0 Thảo luận
Inline Feedbacks
View all comments