Asp.Net Core 項目實戰之許可權管理系統(4) 依賴註入、倉儲、服務的多項目分層實現

来源:http://www.cnblogs.com/fonour/archive/2016/09/25/5904530.html
-Advertisement-
Play Games

0 Asp.Net Core 項目實戰之許可權管理系統(0) 無中生有 1 Asp.Net Core 項目實戰之許可權管理系統(1) 使用AdminLTE搭建前端 2 Asp.Net Core 項目實戰之許可權管理系統(2) 功能及實體設計 3 Asp.Net Core 項目實戰之許可權管理系統(3) 通過 ...


0 Asp.Net Core 項目實戰之許可權管理系統(0) 無中生有

1 Asp.Net Core 項目實戰之許可權管理系統(1) 使用AdminLTE搭建前端

2 Asp.Net Core 項目實戰之許可權管理系統(2) 功能及實體設計

3 Asp.Net Core 項目實戰之許可權管理系統(3) 通過EntityFramework Core使用PostgreSQL

4 Asp.Net Core 項目實戰之許可權管理系統(4) 依賴註入、倉儲、服務的多項目分層實現

5 Asp.Net Core 項目實戰之許可權管理系統(5) 用戶登錄

github源碼地址

0 項目結構

寫這個系列的最初目的其實只是為了自己能更好的學習Asp.Net Core,用一個小的系統作為練習,也督促自己。短期內不見得在實際項目中真的會運用,但至少通過學習,大致的對Asp.Net Core有個瞭解,也是為以後可能的應用做一下技術儲備。起初的設想很簡單,就是在一個Web項目中完成所有工作,大致瞭解Asp.Net Core的知識體系。

在實踐的過程中不自覺的對這個練習的項目進行了一下分層。目前項目整體機構如下:

QQ圖片20160924222505

項目說明:

  • Fonour.MVC

      Asp.Net Core MVC網站項目。

  • Fonour.Application

      應用服務項目,定義應用服務介面及實現,供Fonour.MVC控制器調用;同時定義接收及返回數據對象(Dto,這裡我有可能會省去,直接拿實體往表現層傳了……)

  • Fonour.Domain

      主要定義實體、倉儲介面等。

  • Fonour.EntityFrameworkCore

      主要是倉儲介面的EF Core具體實現

  • Fonour.Utility

      通用項目,定義項目無關的一些公共類庫。

1 倉儲介面定義

1.0 基本介面定義

倉儲介面定義使用泛型介面,主要定義實體基本的增、刪、改、查操作。在Fonour.Domain項目中新建一個名稱為“IRepositories”的文件夾,在該文件夾中新建一個名稱為“IRepository”的介面文件。暫時定義以下幾個基本的介面,日後針對批量插入、刪除、分頁等操作再做進一步的完善。

/// <summary>
/// 倉儲介面定義
/// </summary>
public interface IRepository
{

}
/// <summary>
/// 定義泛型倉儲介面
/// </summary>
/// <typeparam name="TEntity">實體類型</typeparam>
/// <typeparam name="TPrimaryKey">主鍵類型</typeparam>
public interface IRepository<TEntity, TPrimaryKey> : IRepository where TEntity : Entity<TPrimaryKey>
{
    /// <summary>
    /// 獲取實體集合
     /// </summary>
    /// <returns></returns>
    List<TEntity> GetAllList();

    /// <summary>
    /// 根據lambda表達式條件獲取實體集合
     /// </summary>
    /// <param name="predicate">lambda表達式條件</param>
    /// <returns></returns>
    List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate);

    /// <summary>
    /// 根據主鍵獲取實體
     /// </summary>
    /// <param name="id">實體主鍵</param>
    /// <returns></returns>
    TEntity Get(TPrimaryKey id);

    /// <summary>
    /// 根據lambda表達式條件獲取單個實體
     /// </summary>
    /// <param name="predicate">lambda表達式條件</param>
    /// <returns></returns>
    TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);

    /// <summary>
    /// 新增實體
     /// </summary>
    /// <param name="entity">實體</param>
    /// <returns></returns>
    TEntity Insert(TEntity entity);

    /// <summary>
    /// 更新實體
     /// </summary>
    /// <param name="entity">實體</param>
    TEntity Update(TEntity entity);

    /// <summary>
    /// 新增或更新實體
     /// </summary>
    /// <param name="entity">實體</param>
    TEntity InsertOrUpdate(TEntity entity);

    /// <summary>
    /// 刪除實體
     /// </summary>
    /// <param name="entity">要刪除的實體</param>
    bool Delete(TEntity entity);

    /// <summary>
    /// 刪除實體
     /// </summary>
    /// <param name="id">實體主鍵</param>
    bool Delete(TPrimaryKey id);
}

這個練習項目使用的是Guid類型的主鍵,為方便使用,再繼承定義一個主鍵類型為Guid的介面。

/// <summary>
/// 預設Guid主鍵類型倉儲
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public interface IRepository<TEntity> : IRepository<TEntity, Guid> where TEntity : Entity
{

}

1.1 用戶管理倉儲介面定義

如無特殊操作需要,我們的基礎介面基本上能夠滿足各類實體共性的增刪改查操作。對於某種實體特有的操作,就需要單獨進行操作介面的定義。比如後面我們要實現用戶登錄的驗證功能,即提供用戶名、密碼,驗證該用戶是否存在,以及用戶名密碼是否正確。對於此類特定需求,我們針對用戶實體定義一個用戶管理的倉儲介面。

在“IRepositories”文件夾下新建一個名稱為“IUserRepository”的介面,裡面暫時只定義一個檢查用戶是否存在的方法,做為我們最後的測試介面。

/// <summary>
/// 用戶管理倉儲介面
/// </summary>
public interface IUserRepository : IRepository<User>
{
    /// <summary>
    /// 檢查用戶是存在
     /// </summary>
    /// <param name="userName">用戶名</param>
    /// <param name="password">密碼</param>
    /// <returns>存在返回用戶實體,否則返回NULL</returns>
    User CheckUser(string userName, string password);
}

QQ圖片20160924225551

2 倉儲介面實現

倉儲介面的實現全部放在Fonour.EntityFrameworkCore項目中,通過EF Core使用PostgresSQL資料庫實現。

2.0 基本介面實現

在Fonour.EntityFrameworkCore項目中新建一個名稱為“Repositories”的文件夾,在文件夾中添加一個名稱為“FonourRepositoryBase”的抽象類。

該抽象類除了實現基本倉儲介面定義的方法外,還有2個需要註意的地方。、

1 定義了數據訪問上下文對象

該數據訪問上下文對象通過構造函數進行依賴註入,Asp.Net Core已經預設對數據訪問上下文對象進行了構造函數依賴註入的實現,具體應用後面會說到。

2 定義了一個Save操作方法

目的為了在應用服務層調用多個倉儲後,統一進行數據上下文的SaveChanges操作,保證數據存儲的事務性。

/// <summary>
/// 倉儲基類
/// </summary>
/// <typeparam name="TEntity">實體類型</typeparam>
/// <typeparam name="TPrimaryKey">主鍵類型</typeparam>
public abstract class FonourRepositoryBase<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey> where TEntity : Entity<TPrimaryKey>
{
    //定義數據訪問上下文對象
    protected readonly FonourDbContext _dbContext;

    /// <summary>
    /// 通過構造函數註入得到數據上下文對象實例
     /// </summary>
    /// <param name="dbContext"></param>
    public FonourRepositoryBase(FonourDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    /// <summary>
    /// 獲取實體集合
     /// </summary>
    /// <returns></returns>
    public List<TEntity> GetAllList()
    {
        return _dbContext.Set<TEntity>().ToList();
    }

    /// <summary>
    /// 根據lambda表達式條件獲取實體集合
     /// </summary>
    /// <param name="predicate">lambda表達式條件</param>
    /// <returns></returns>
    public List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate)
    {
        return _dbContext.Set<TEntity>().Where(predicate).ToList();
    }

    /// <summary>
    /// 根據主鍵獲取實體
     /// </summary>
    /// <param name="id">實體主鍵</param>
    /// <returns></returns>
    public TEntity Get(TPrimaryKey id)
    {
        return _dbContext.Set<TEntity>().FirstOrDefault(CreateEqualityExpressionForId(id));
    }

    /// <summary>
    /// 根據lambda表達式條件獲取單個實體
     /// </summary>
    /// <param name="predicate">lambda表達式條件</param>
    /// <returns></returns>
    public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
    {
        return _dbContext.Set<TEntity>().FirstOrDefault(predicate);
    }

    /// <summary>
    /// 新增實體
     /// </summary>
    /// <param name="entity">實體</param>
    /// <returns></returns>
    public TEntity Insert(TEntity entity)
    {
        _dbContext.Set<TEntity>().Add(entity);
        return entity;
    }

    /// <summary>
    /// 更新實體
     /// </summary>
    /// <param name="entity">實體</param>
    public TEntity Update(TEntity entity)
    {
        _dbContext.Set<TEntity>().Attach(entity);
        _dbContext.Entry(entity).State = EntityState.Modified;
        return entity;
    }

    /// <summary>
    /// 新增或更新實體
    /// </summary>
    /// <param name="entity">實體</param>
    public TEntity InsertOrUpdate(TEntity entity)
    {
        if (Get(entity.Id) != null)
            return Update(entity);
        return Insert(entity);
    }

    /// <summary>
    /// 刪除實體
    /// </summary>
    /// <param name="entity">要刪除的實體</param>
    public void Delete(TEntity entity)
    {
        _dbContext.Set<TEntity>().Remove(entity);
    }

    /// <summary>
    /// 刪除實體
     /// </summary>
    /// <param name="id">實體主鍵</param>
    public void Delete(TPrimaryKey id)
    {
        _dbContext.Set<TEntity>().Remove(Get(id));
    }

    /// <summary>
    /// 事務性保存
    /// </summary>
    public void Save()
    {
        _dbContext.SaveChanges();
    }

    /// <summary>
    /// 根據主鍵構建判斷表達式
     /// </summary>
    /// <param name="id">主鍵</param>
    /// <returns></returns>
    protected static Expression<Func<TEntity, bool>> CreateEqualityExpressionForId(TPrimaryKey id)
    {
        var lambdaParam = Expression.Parameter(typeof(TEntity));
        var lambdaBody = Expression.Equal(
            Expression.PropertyOrField(lambdaParam, "Id"),
            Expression.Constant(id, typeof(TPrimaryKey))
            );

        return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
    }
}

同樣的實現一個主鍵類型為Guid的倉儲操作基類。

/// <summary>
 /// 主鍵為Guid類型的倉儲基類
 /// </summary>
 /// <typeparam name="TEntity">實體類型</typeparam>
 public abstract class FonourRepositoryBase<TEntity> : FonourRepositoryBase<TEntity, Guid> where TEntity : Entity
 {
     public FonourRepositoryBase(FonourDbContext dbContext) : base(dbContext)
     {
     }
 }

2.1 用戶管理倉儲介面實現

在Fonour.EntityFrameworkCore項目的“Repositories”文件夾中新建一個用戶管理倉儲介面的實現類“UserRepository”,實現介面中定義的用戶檢查方法。

/// <summary>
/// 用戶管理倉儲實現
/// </summary>
public class UserRepository : FonourRepositoryBase<User>, IUserRepository
{
    public UserRepository(FonourDbContext dbcontext) : base(dbcontext)
    {
        
    }
    /// <summary>
    /// 檢查用戶是存在
     /// </summary>
    /// <param name="userName">用戶名</param>
    /// <param name="password">密碼</param>
    /// <returns>存在返回用戶實體,否則返回NULL</returns>
    public User CheckUser(string userName, string password)
    {
        return _dbContext.Set<User>().FirstOrDefault(it => it.UserName == userName && it.Password == password);
    }
}

QQ圖片20160924231108

3 應用服務層介面定義及實現

在Fonour.Application項目中新建一個名稱為“UserApp”的文件夾,文件夾中新建一個名稱為“IUserAppService”的服務介面,及服務介面的具體實現“UserAppService”,在服務層中調用對應的倉儲方法實現具體相關業務邏輯。

UserAppService中定義一個私有且只讀的用戶管理倉儲介面對象,依然通過構造函數的方式進行依賴註入。

/// <summary>
/// 用戶管理服務
/// </summary>
public class UserAppService : IUserAppService
{
    //用戶管理倉儲介面
    private readonly IUserRepository _userReporitory;

    /// <summary>
    /// 構造函數 實現依賴註入
     /// </summary>
    /// <param name="userRepository">倉儲對象</param>
    public UserAppService(IUserRepository userRepository)
    {
        _userReporitory = userRepository;
    }

    public User CheckUser(string userName, string password)
    {
        return _userReporitory.CheckUser(userName, password);
    }
}

4 Asp.Net Core依賴註入實現

在上一節中,我們講到在Fonour.MVC項目的Startup.cs文件的ConfigureServices方法中通過使用

services.AddDbContext<FonourDbContext>(options =>options.UseNpgsql(sqlConnectionString));

方法將資料庫上上下文添加到系統服務中,正是在此時同時對數據訪問上下文進行了依賴註入實現。

通過添加以下代碼在ConfigureServices方法中添加對上面創建的倉儲及服務進行依賴註入的實現。

services.AddScoped<IUserRepository, UserRepository>();
services.AddScoped<IUserAppService, UserAppService>();

註意:Asp.Net Core提供的依賴註入擁有三種生命周期模式,由短到長依次為:

  • Transient     ServiceProvider總是創建一個新的服務實例。
  • Scoped         ServiceProvider創建的服務實例由自己保存,(同一次請求)所以同一個ServiceProvider對象提供的服務實例均是同一個對象。
  • Singleton      始終是同一個實例對象

對於數據訪問上下文,我們可以通過重載方法的第二個參數,控制數據訪問上下文對象的生命周期,預設生命周期為Scoped。

services.AddDbContext<FonourDbContext>(options => options.UseNpgsql(sqlConnectionString), ServiceLifetime.Transient);
services.AddDbContext<FonourDbContext>(options => options.UseNpgsql(sqlConnectionString), ServiceLifetime.Scoped);
services.AddDbContext<FonourDbContext>(options => options.UseNpgsql(sqlConnectionString), ServiceLifetime.Singleton);

對於要依賴註入的介面和對象提供AddTransient、AddScoped、AddSingleton三個方法控制對象聲明周期。

5 測試

我們在Fonour.MVC項目的LoginController中增加一個IUserAppService服務對象的定義,同時提供LoginController的構造函數,在構造函數中實現對UserAppService服務的依賴註入。

在Index控制器中增加IUserAppService的用戶檢查方法的調用代碼,增加一個斷點,用於測試。

public class LoginController : Controller
{
    private IUserAppService _userAppService;
    public LoginController(IUserAppService userAppService)
    {
        _userAppService = userAppService;
    }
    // GET: /<controller>/
    public IActionResult Index()
    {
        var user = _userAppService.CheckUser("admin", "123456");
        return View();
    }
}

運行程式,進入斷點,發現已經成功根據用戶名和密碼,把上一節創建的用戶數據信息取出,至此,我們項目的分層之間通道已經打通。

QQ圖片20160924234550

6 總結

本節主要涉及到Asp.Net Core的知識點是它的依賴註入機制,我們通過清晰多項目分層結構,採用依賴註入機制,實現了各通之間的連接。

下一節實現用戶登錄相關,主要有用戶登錄驗證,以及用戶對控制器Action路由訪問的攔截及判斷。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 子查詢,又叫做嵌套查詢。 將一個查詢語句做為一個結果集供其他SQL語句使用,就像使用普通的表一樣,被當作結果集的查詢語句被稱為子查詢。 子查詢有兩種類型: 一種是只返回一個單值的子查詢,這時它可以用在一個單值可以使用的地方,這時子查詢可以看作是一個擁有返回值的函數; 另外一種是返回一列值的子查詢,這 ...
  • 設計表格我覺得用PowerDesigner比起在SQL Server中設計表格簡單快捷許多。 首先,我們新建一個Model(可以使用快捷鍵Ctrl + N) 在PowerDesigner中側邊欄有浮動小工具欄: 這都是我們設計的工具,然後點擊表格Columns設計表格: 之後就設計表格就可以了。 就 ...
  • 1.背景說明: vim編輯器是linux一款非常流行功能強大的文本編輯。配置好vim可大大提高代碼的開發效率 2.配置方案: 我在github搜索了別人是如何進行vim配置的,在這裡你可以選擇一款適合你的配置方案,這裡我選擇Star比較多的配置方案:vimrc 3.如何安裝: 參考上述鏈接,安裝說明 ...
  • 有時候因為斷電或者其他原因導致伺服器關機,這在測試伺服器上面是很常見的事情。而伺服器上面的多個進程也就關閉了,每次都要去重啟很麻煩,於是就查了一下設置開機自動啟動的方法,這樣開機就只用稍微看看服務是否啟動就好了,不用一個一個去啟動了。 ...
  • 一、POST加電自檢 按下電源後ROM晶元中的CMOS程式執行並檢測CPU、記憶體等設備是否存在並正常運行,CMOS中的程式叫BIOS,可以設置硬碟介面,網卡音效卡開關之類的簡單設置。一般PC機主板上有一個紐扣電池,這個電池就是給ROM供電的,可以保證主板斷電後BIOS的一些基本設置不會重置。 下麵是一 ...
  • 我們知道,迄今為止,Ubuntu已有多個發行版,如11.04、11.10,以至於現在最新的16. 。而我們平常通過apt get來安裝軟體,如果OS版本不同,那麼鏡像源的配置就不同,否則就會出現找不到對應軟體、軟體版本不匹配等一系列奇怪的問題。這裡,我就講述一下如何配置不同的版本的鏡像源。在此,我就 ...
  • 1.編譯器gcc的語法順序 PC: gcc -o ana *.o ${LIBS} 輸出可執行文件名 各種目標文件 要鏈接的庫 交叉編譯:arm-linux-gcc ${LIBS] *.o -o ana 2.Minigui.config 將/usr/local/etc/MiniGUI.cfg 與 ./ ...
  • 關於《rsyslog+mysql+loganalyzer搭建日誌伺服器<個人筆記>》的反思--鏈接--http://www.cnblogs.com/drgcaosheng/p/5832697.html 之前使用這個搭建了一個日誌伺服器,但是在mysql中,日誌增加太快,過了些天,已經有幾千萬的數據了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...