【Side Project】 程序码整理 -Model运用

之前为方便快速了解我们程序完整的功能,
所以把所有的资料都放在Controller里面。
今天这篇我们来整理一下先前的程序码,
顺便带大家了解一下MVC-Model的功用。

appsettings.json

这是一只在专案创建时就会有的文件,
在这文件中会存放一些关於系统中的设定。
我们也会在这新增一些系统参数在这边,供我们系统运行的时候使用。

  1. 打开 appsettings.json 文件
  2. 新增我们的资料库连线字串
"WebmenuConnectionString":"Server=.\\SQLEXPRESS;Initial Catalog=WebMenu;user id=webmenu;password=xxxxxxxx;Integrated Security=False"

(帐号密码用自己当初在资料库设定的密码)
https://ithelp.ithome.com.tw/upload/images/20211001/20115941oVLrkWh5zm.jpg

3.开启 HomeController.cs
4.在 建构子时透过 DI 注入 IConfiguration

private IConfiguration _config;

        public HomeController(ILogger<HomeController> logger, IConfiguration config)
        {
            _logger = logger;
            _config = config;
        }

因为在ASP.NET Core 中一开始执行Startup.cs的时候,
就会透过 DI 注入 IConfiguration 组态设定档内容。
所以我们在撰写的时候不需要去注意修改Startup.cs

MVC-Model

大家实际运用MVC开发的时候,或多或少都有一些功能分类上的差异。
那这边对Model处理的业务范围,用处理资料的地方 简言概括之,
不管是传送、接收、或是对资料的运算,我们都会放在Model里面。

我们这边分类的方式也很简单,就是一个画面配上一个Model,
用来处理data的传送、接收、运算。

Common.cs

这边放一些通用的资料处理function()

  1. 我们新增一个档案 /api/Common.cs
  2. 将Json序列化的程序码移动到这里面
public static string Obj2JsonString(object obj)
        {
            return    JsonConvert.SerializeObject(obj);
        }

https://ithelp.ithome.com.tw/upload/images/20211002/20115941w0EgOCQtAS.jpg

Customer.cs

  1. 我们新增一个档案 /api/CustomerModel.cs
  2. 做一个 Singleton pattern的设计模式
public class Customer
    {
        public static CustomerModel customer
        {
            
             get
            {
                if (mCustomer == null)
                {
                    mCustomer = new CustomerModel();
                }
                return mCustomer;
            }
        }

https://ithelp.ithome.com.tw/upload/images/20211002/20115941nlZfmFiBHg.jpg
这样的设计方式可以保证只创建单一物件,
并且在系统刚启动时不直接创建物件,也能加快系统的加载速度。
(不过这边没有特别处理异步的问题,有兴趣的小夥伴在自行改写)

  1. 取得资料库中菜单的程序码移动到这里面
public List<Menu> SelectMenu(IConfiguration config)
        {
            //连线设定
            SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
            {
                //连线字串
                ConnectionString = config.GetValue<string>("WebmenuConnectionString"),
                DbType = DbType.SqlServer,//连线类型
                IsAutoCloseConnection = true //自动关闭连线
            });
            //当执行时,触发事件
            db.Aop.OnLogExecuting = (sql, pars) =>
            {
                Console.WriteLine(sql);//查看SQL语法
            };

            var list = db.Queryable<Menu>().ToList();
            foreach (var menu in list)
            {
                menu.Item = menu.Item.Trim();
                Console.WriteLine(menu.Item);
            }
            return list;
        }
  1. 菜单资料转成Object
public void CreateOrder(string data, string phone,out Dictionary<string, Menulistb> order_body,out Menulisth order_head)
        {
            order_head = new Menulisth()
            {
                Formnum = phone
            };
            order_body = JsonConvert.DeserializeObject<Dictionary<string, Menulistb>>(data);
        }
  1. 将菜单写入资料库
public bool insertOrder(IConfiguration config ,Dictionary<string, Menulistb> order_body, Menulisth order_head)
        {
            //连线设定
            SqlSugarClient db = new SqlSugarClient(new ConnectionConfig()
            {
                ConnectionString = config.GetValue<string>("WebmenuConnectionString"),
                DbType = DbType.SqlServer,//连线类型
                IsAutoCloseConnection = true //自动关闭连线
            });
            try
            {

                //当执行时,触发事件
                db.Aop.OnLogExecuting = (sql, pars) =>
                {
                    Console.WriteLine(sql);//查看SQL语法
                };
                db.BeginTran();
                //写入表头 并 回传表头资料
                order_head = db.Insertable(order_head).ExecuteReturnEntity();
                //逐笔将表身资料写入
                foreach (var keyvalue in order_body)
                {
                    var item = keyvalue.Value;
                    item.H_uid = order_head.Uid;
                    db.Insertable(item).ExecuteCommand();
                }

                db.CommitTran();
            }
            catch
            {
                db.RollbackTran();//rollback
                throw;
            }
            return true;
        }

乾净的Controller

我们将处理资料的业务转道model之後,
我们的HomeController.cs不仅瘦身成功,
可读性也是大幅度上升。

HomeController.cs 里的Customer():

public IActionResult Customer()
        {
            var customer = CustomerModel.customer;
            var menu_list = customer.SelectMenu(_config);
            string jsonData = Common.Obj2JsonString(menu_list);
            ViewBag.menuData = jsonData;
            return View();
        }

这样我们就可以非常清晰的看懂程序码中做的事情:

  1. 取得Model
  2. 取得菜单
  3. 转成Json String
  4. 携带资料回传

HomeController.cs 里的CreateOrder():

public ActionResult CreateOrder(string data,string phone)
        {
            var customer = CustomerModel.customer;
            customer.CreateOrder(data, phone, out var order_body,out var order_head);
            customer.insertOrder(_config,order_body, order_head);
            
            return Ok();
        }
  1. 取得Model
  2. 建立菜单
  3. 写入资料库

结语

当专案开发到一定程度的时候,适时的回头去做Code Review是件非常重要的事情。
不然常常专案开发到後面会变得残破不堪东贴西补的。

一般在外面使用MVC开发时,很常可以看见这种
1个View + 1个Controller + 1个 model的方式
这样的配置虽然不一定是效率比较好的配置方式,
但在专案中检索时就相对的方便许多。

有些人在看到

public static string Obj2JsonString(object obj)
        {
            return    JsonConvert.SerializeObject(obj);
        }

会有些疑惑,为什麽要把别人包好的功能在包一层起来?
这样做主要可以让Controller与套件解耦合,
当我们要对套件进行更新或是对某些功能做异动的时候,
我们只需要到Model上面进行维护,而不需要连Controller一起更动。

可能有些萌新还会问为什麽不把config直接存在model里面使用,
这样不就不需要一直传参数了吗?
(设计成 function()function(arg1,arg2)的区别)
当然,这种设计方式是完全没问题的。
只是这样的设计方式我们在阅读程序码的时候,
会比较难找到资料的流向。
比较简单的分法就是:

  1. 内部使用的function()就存参数在Class内(privateprotected)
  2. 外部呼叫的function(arg)就用传参数的方式(publicdefault)

简单版:

  1. 定期Code Review
  2. 1个View + 1个Controller + 1个 model配置方式
  3. Controller与套件解藕
  4. 开发时须注意资料流向及可读性问题

有细心的小夥伴应该会发现,我之前menu 跟 order 傻傻分不清楚,
导致命名的时候有一点奇怪。因为不影响功能,所以就将就一下吧。


<<:  Day 30 - Module

>>:  Computer Architecture: Memory Hierarchy

[Day 16] Reverse 小疲累

终於到星期五啦 明天就是周末六日了 今天也是我课最多的一天 从早八到五点连八堂 我遇到做图障碍的挫折...

[JS] You Don't Know JavaScript [Async & Performance] - Now & Later

前言 由於JavaScript是一个单线程的程序语言,这意味着JavaScript一次只能做一件事,...

[Day18] NLP会用到的模型(二)-GRU

一. LSTM的问题 LSTM虽然非常强大,但LSTM也是有一个问题,就是计算时间较久导致执行速度较...

[第05天]理财达人Mx. Ada-历史K棒资料

前言 本文说明取得历史交易资料。 K棒说明 程序实作 取得历史K棒资料 # 取得历史K棒资料 # 资...

[Day8]PHP判断式01

PHP判断式 If Else 判断句 If 可以使用在判断某条件达成时执行语句,else则是在不满该...