78°

【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&lt;int&gt;
{
    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&lt;T&gt; SearchPage(PageCondition&lt;T&gt; condition);

    void Delete(Expression&lt;Func&lt;T, bool&gt;&gt; predicate);

    void Update(T entity);

    List&lt;T&gt; Search(Expression&lt;Func&lt;T, bool&gt;&gt; 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&lt;T&gt; repository)
    {
        LocalRepository = repository;
    }
    
    
    public T Get(object key)
    {
        return LocalRepository.Get(key);
    }

    public T Get(Expression&lt;Func&lt;T, bool&gt;&gt; predicate)
    {
        return LocalRepository.Get(predicate);
    }

    public PageModel&lt;T&gt; SearchPage(PageCondition&lt;T&gt; condition)
    {
        return LocalRepository.Search(condition);
    }

    public void Delete(Expression&lt;Func&lt;T, bool&gt;&gt; predicate)
    {
        LocalRepository.Delete(predicate);
    }

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

    public List&lt;T&gt; Search(Expression&lt;Func&lt;T, bool&gt;&gt; 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 =&gt; p.LoginType == auth.LoginType &amp;&amp; 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

本文转载自博客园,原文链接:https://www.cnblogs.com/c7jie/p/13173433.html

全部评论: 0

    我有话说: