【asp.net core 系列】- 11 Service層的實現樣板


0.前言

在《asp.net core 系列》之實戰系列中,我們在之前的篇幅中對項目有了一個大概的認知,也搭建了一個基礎的項目骨架。那么就讓我們繼續完善這個骨架,讓它更加豐滿。這一篇,我將帶領小伙伴們一起實現用戶管理功能。

1. 數據表

一般情況下,我們會把用戶表和登錄信息表放在兩個表里。為什么會這樣設計呢?出於以下幾種考慮:

  • 使功能分割,用戶信息管理是用戶管理,登錄是登錄
  • 增加安全,降低無關信息的查詢,例如訪問登錄接口不會連帶檢索用戶的普通信息,當進行用戶信息管理的時候,不會把登錄信息也帶過來

等等

廢話不多說,直接上代碼:

namespace Data.Enums
{
    /// <summary>
    /// 登錄類型
    /// </summary>
    public enum LoginType : byte
    {
        /// token登錄
        Token,
        /// 用戶名密碼
        Password
    }
    /// <summary>
    /// 性別
    /// </summary>
    public enum SexEnum
    {
        /// 男
        Male,
        /// 女
        Female,
        /// 隱私
        None
    }
}

SysUserAuthEntity.cs

using Data.Enums;
using Data.Infrastructure;

namespace Data.Models
{
    public class SysUserAuthEntity : BaseEntity<int>
    {
        public string UserName { get; set; }
        public string Password { get; set; }
        
        public LoginType LoginType { get; set;}
    }
}

SysUserInfoEntity.cs

using System;
using Data.Enums;
using Data.Infrastructure;

namespace Data.Models
{

    public class SysUserInfoEntity : BaseEntity<int>
    {
        public string NickName { get; set; }
        public string ImageUrl { get; set; }
        public SexEnum Sex { get; set; }
        public DateTime? BirthDay { get; set; }

        public int SysUserAuthId { get; set; }

        public virtual SysUserAuthEntity SysUserAuth { get; set; }
    }
}

這里並沒有使用數據庫Sql語句作為數據庫描述,而是使用了Entity類作為描述,這是因為數據庫到實體類之間還是有一層轉換,對於開發而言接觸更多的是實體類,而不是數據表。

2. 生成 Repository相關

使用工具代碼的方式有很多,我在這里推薦一種, Test項目中,添加一個測試類,具體代碼如下:

using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Text;
using Utils.Develop;

namespace Test
{
    public class DevelopTest
    {
        [Test]
        public void TetDevelop()
        {
            var d = Develop.CurrentDirect;
            Console.WriteLine(d);
            Assert.IsTrue(d.Contains("template"));
            var entities = Develop.LoadEntities();
            foreach (var item in entities)
            {
                Console.WriteLine(item.FullName);
            }
        }
        [Test]
        public void TestCreateDevelop()
        {
            var entities = Develop.LoadEntities();
            foreach (var item in entities)
            {
                Develop.CreateRepositoryInterface(item);
                Develop.CreateRepositoryImplement(item);
                Develop.CreateEntityTypeConfig(item);
            }
            Assert.Pass();
        }
    }
}

具體的命令行執行比較麻煩,會執行所有的測試單元:

cd Test/
dotnet test

當然了,IDE支持單個測試單元的執行,具體操作這里就不做過多的介紹了。

3. Service 接口和實現類

通常Service接口會提供一個簡單Crud的Service接口,然后其他業務接口繼承該接口。這樣可以減少代碼的重復,因為重復的代碼在開發過程中是非常討厭的一種情況,因為一旦一處發生變更,其他的也有可能發生變更。所以遇到重復代碼一般都會進行一定程度的封裝:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Data.Infrastructure;

namespace Service.Insfrastructure
{
    public interface BaseService<T>
    {
        T Get(object key);
        T Get(Expression<Func<T, bool>> predicate);

        PageModel<T> SearchPage(PageCondition<T> condition);

        void Delete(Expression<Func<T, bool>> predicate);

        void Update(T entity);

        List<T> Search(Expression<Func<T, bool>> predicate);

    }
}

暫時就提供了這些最常見的請求方法。

在Service.Implements項目中:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Data.Infrastructure;
using Domain.Insfrastructure;
using Service.Insfrastructure;

namespace Service.Implements.Insfrastructure
{
    public abstract class BaseServiceImpl<T> : BaseService<T>
    {
        private IRepository<T> LocalRepository { get; }

        protected BaseServiceImpl(IRepository<T> repository)
        {
            LocalRepository = repository;
        }
        
        
        public T Get(object key)
        {
            return LocalRepository.Get(key);
        }

        public T Get(Expression<Func<T, bool>> predicate)
        {
            return LocalRepository.Get(predicate);
        }

        public PageModel<T> SearchPage(PageCondition<T> condition)
        {
            return LocalRepository.Search(condition);
        }

        public void Delete(Expression<Func<T, bool>> predicate)
        {
            LocalRepository.Delete(predicate);
        }

        public void Update(T entity)
        {
            LocalRepository.Update(entity);
        }

        public List<T> Search(Expression<Func<T, bool>> predicate)
        {
            return LocalRepository.Search(predicate);
        }
    }
}

這個類設置為抽象類。

4. 用戶管理的接口

先創建了兩個簡單的示范接口:

using Data.Models;
using Service.Insfrastructure;

namespace Service
{
    public interface ISysUserService : BaseService<SysUserInfoEntity>
    {
        void Register(SysUserAuthEntity auth, SysUserInfoEntity info);

        void ChangePassword(int userId, string oldPwd, string newPwd);
    }
}

實現類:

using System;
using Data.Models;
using Domain.Repository;
using Service.Implements.Insfrastructure;

namespace Service.Implements
{
    public class SysUserServiceImpl : BaseServiceImpl<SysUserInfoEntity>, ISysUserService
    {
        protected ISysUserAuthRepository AuthRepository { get; }
        protected ISysUserInfoRepository InfoRepository { get; }

        public SysUserServiceImpl(ISysUserAuthRepository authRepository, ISysUserInfoRepository infoRepository) : base(
            infoRepository)
        {
            AuthRepository = authRepository;
            InfoRepository = infoRepository;
        }

        public void Register(SysUserAuthEntity auth, SysUserInfoEntity info)
        {
            var authItem = AuthRepository.Get(p => p.LoginType == auth.LoginType && p.UserName == auth.UserName);
            if (authItem != null)
            {
                throw new Exception("用戶信息已經存在");
            }

            info.SysUserAuth = auth;
            InfoRepository.Insert(info);
        }

        public void ChangePassword(int userId, string oldPwd, string newPwd)
        {
            var info = InfoRepository.Get(userId);
            var auth = AuthRepository.Get(info.SysUserAuthId);
            if (oldPwd == null || oldPwd != auth?.Password)
            {
                throw new Exception("原密碼錯誤");
            }

            auth.Password = newPwd;

        }
    }
}

這里沒對密碼進行加密處理,直接使用明文。這在正式開發中是不允許的,密碼不能使用明文保存。當然,這也不是最終代碼,下一篇我們將介紹一下.net core中常見的加密實現。

5. 總結

這一篇通過幾個簡單的示例為大家介紹了一下Service層的開發邏輯以及理念。下一篇將為大家介紹一下.net core中幾種簡單的加密實現。

更多內容煩請關注我的博客《高先生小屋》

file


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM