8-3.ASP.NET Core MVC 入門教學 - DI之IoC用法

ASP.NET Core MVC 入門教學

前兩節我們已經學會了依賴注入的基本用法,這節要來教IoC控制反轉

這是稍微進階的技巧,但卻又是很基本的觀念,所以還是要教給大家

剛學依賴注入的時候,可能會覺得不知道好處在哪裡,如果只是要拆分程式,似乎只要另寫類別就好,不用特別使用依賴注入

但其實使用依賴注入主要有兩個重點,第一個就是生命週期的管理,你不用在擔心生命週期的問題,總之生命週期就會依注入的方式決定

第二就是可以使用一些進階的設計模式,例如這節要教的控制反轉Ioc

這邊有兩種情況來示範,第一種是我們今天想要將原有的程式去做大修改

那如果沒有依賴注入,我們可能會將原有的程式清掉,寫上新的,又或者註解起來,但這樣一個是會找不到舊的寫法,一個會使程式雜亂

所以這時就可以使用依賴注入的控制反轉的技巧來寫

那假設我們今天要將原有的linq處理資料的方式改成用sql的方式

那首先我們要先建立一個介面INewsService.cs

namespace Kcg.Interface
{
    public interface INewsService
    {        
        Task<List<NewsDto>> 取得新聞列表();

        Task<NewsDto> 取得新聞詳細資訊(Guid newsId);

        Task 新增(NewsCreateDto news);

        Task<NewsEditViewModel> 修改取得(Guid? id);

        Task 修改(Guid id, NewsEditDto news);

        Task 刪除(Guid id);
    }
}

至於介面的基本觀念,網路上已經很多了,我這邊就不在說明了

接著將我們原來的NewsService.cs繼承該介面

 public class NewsService : INewsService
 {}

這邊說明一下,正常的流程應該是先定義介面,再去寫實作,但因為我們這邊實作已經寫好了所以才會這樣

接著我們要來寫使用sql處理資料的實作,先建立一個NewsSqlService.cs並繼承INewsService

namespace Kcg.Services
{
    public class NewsSqlService : INewsService
    {
        private readonly KcgContext _context;

        public NewsSqlService(KcgContext context)
        {
            _context = context;
        }


        public async Task<List<NewsDto>> 取得新聞列表()
        {
            string sql = @"SELECT a.Click,
                           a.Enable,
                           a.EndDateTime,
                           a.NewsId,
                           a.StartDateTime,
                           Title = a.Title + 'sql',
                           a.UpdateDateTime
                           FROM News a";

            var result = await _context.News.FromSqlRaw(sql)
                .Select(x => new NewsDto
                {
                    Click = x.Click,
                    Enable = x.Enable,
                    EndDateTime =x.EndDateTime,
                    NewsId = x.NewsId,
                    StartDateTime = x.StartDateTime,
                    Title = x.Title,
                    UpdateDateTime = x.UpdateDateTime
                }).ToListAsync();

            return result;
        }

        public Task 修改(Guid id, NewsEditDto news)
        {
            throw new NotImplementedException();
        }

        public Task<NewsEditViewModel> 修改取得(Guid? id)
        {
            throw new NotImplementedException();
        }

        public Task 刪除(Guid id)
        {
            throw new NotImplementedException();
        }

        public Task<NewsDto> 取得新聞詳細資訊(Guid newsId)
        {
            throw new NotImplementedException();
        }

        public Task 新增(NewsCreateDto news)
        {
            throw new NotImplementedException();
        }
    }
}

這邊我們就示範一個就好,所以只改寫第一個取新聞列表資料的部分

接著我們NewsController.cs要改一下取得的服務,改成取得INewsService

private readonly INewsService _newsService;

public NewsController(INewsService newsService)
{
    _newsService = newsService;
}

最後我們到program.cs改寫成控制反轉的注入方式

// 原來是這樣
// builder.Services.AddScoped<NewsService>();
// 如果有介面的話改這樣
// builder.Services.AddScoped<INewsService, NewsService>();
// 改成sql處理資料
builder.Services.AddScoped<INewsService, NewsSqlService>();

如果以後我們要改實作方式,只要改第二個欄位的類別,就可以將實作方式進行替換,我們就不需要把之前的程式清掉,或用註解的方式去改程式了

來看一下結果

我這邊在標題後面加上sql,代表我真的是改成用sql取得資料了

第二種就是如果我今天在本機沒辦法連上資料庫而改用mock資料測試的話,上線時則要真的連到資料庫

那這時候該怎麼寫呢?其實也很簡單,我們可以這樣寫

if (builder.Environment.IsDevelopment())
{
    builder.Services.AddScoped<INewsService, NewsMockService>();
}
else
{
    builder.Services.AddScoped<INewsService, NewsService>();
}

如此就可以簡單又很清晰地去做切換,如果用其他方式處理,都會增加後續維護上的複雜度

而且使用介面還有一個好處,就是以後要改實作,只需要改program這裡的設定,原來程式的地方是完全不需要修改的,因為方法名稱都相同

那這邊如果有在網路上看過依賴注入的人,可能會發現很多都是教界面加類別的注入方式,但卻沒有說為什麼

// 我一開始教的範例
// builder.Services.AddScoped<NewsService>();

// 網路上大部分的範例
// builder.Services.AddScoped<INewsService, NewsService>();

其實,沒要使用這些進階技巧實,是完全不需要使用介面的依賴注入方式

而網路上的範例舉的例子也往往看不到用介面的用意,新手更有可能以為依賴注入就是要這樣寫,因此寫了一堆無用的介面

那這個時候可能會問,那究竟什麼時候要寫介面,什麼時候不用寫介面,我只能說這就很吃自己的實務經驗判斷,沒有一個正確的答案,只有適合的情境

不過我這邊建議,一開始如果你還不清楚該不該用的時候,一律不使用介面的方式進行依賴注入,等哪天真的需要用了,我們再改過來

這樣做的原因,一來是可以減少很多不必要的介面,二來你有改過才會有深刻記憶說這個地方原來會很需要用到介面

 

範例檔:下載




Copyright © 凱哥寫程式 2022 | Powered by TalllKai ❤