业务服务层
概述
一个模块的服务层,主要负责如下几个方面的工作:
向API层
提供各个实体的数据查询的IQueryable<T>
类型的数据源
接收API层
传入的IInputDto
参数,完成实体的新增、更新、删除
等业务操作
接收API层
传入的参数,处理各种模块级别
的综合业务
处理完业务之后,将数据通过数据仓储IRepository
更新到数据库
向1API
层返回业务操作结果
代码布局
一个业务模块,是负责完成一系列功能的,这些功能相互之间具有密切的关联性,所以对于一个模块来说,业务服务是一个整体,不应把他们再按单个实体拆分开来。 YuebonCore
的业务模块代码结构设计,也是根据这一原则来设计的。设计规则如下:
服务接口ISequenceService:一个模块的业务服务共享一个服务接口,接口中包含模块的综合业务服务,也包含模块的各个实体的查询数据集、新增、更新、删除等自有业务服务。
服务实现SequenceService:服务实现使用,负责各实体的仓储服务注入,辅助服务注入,模块综合业务实现。
src # 源代码文件夹
└─Yuebon.Security.Core # 项目核心工程
└─IServices # 业务单据编码模服务接口文件夹
│ ├─ISequenceService.cs # 业务单据服务接口类
│ └─ISequenceRuleService.cs # 单据编码规则服务接口类
└─Services # 业务单据编码模块实体类文件夹
├─SequenceService.cs # 业务单据服务接口实现类
└─SequenceRuleService.cs # 单据编码规则服务接口实现类
IService、BaseService是服务接口和实现基类,所有业务模块的服务类都继承该基类。
服务接口
所有业务的Service接口必须继承IService基础接口。主要用于定义业务自己的功能业务接口,不要与IService接口名称一致即可。
根据 <业务模块设计#服务层> 的需求分析,我们需要给业务单据Sequence
实体定义新增、更新、删除、获取最新单据编码
等服务。
接口定义如下:
/// <summary>
/// 定义单据编码服务接口
/// </summary>
public interface ISequenceService:IService<Sequence,SequenceOutputDto, string>
{
/// <summary>
/// 获取最新业务单据编码
/// </summary>
/// <param name="sequenceName">业务单据编码名称</param>
/// <returns></returns>
CommonResult GetSequenceNext(string sequenceName);
}
接口的实现
1、主要实现仓储接口中定义方法实现,同时对BaseService中的方法实现不能满足业务需要时可以重写相关的方法。
2、此处需根据业务情况依赖注入服务生命周期接口,方法同仓储Repository
实现一样,不再赘述。
3、依赖服务注入方式分析
服务层的业务实现,通过向服务实现类注入 数据仓储IRepository<TEntity, TKey>
对象来获得向数据库存取数据的能力。根据 .NetCore 的依赖注入使用原则,常规的做法是在服务实现类的构造函数
进行依赖服务的注入。形如:
/// <summary>
/// 单据编码服务接口实现
/// </summary>
public class SequenceService : BaseService<Sequence, SequenceOutputDto, string>, ISequenceService,ITransientDependency
{
private readonly ISequenceRepository _repository;
private readonly ISequenceRuleRepository _repositoryRule;
private readonly ILogService _logService;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
/// <param name="repositoryRule"></param>
/// <param name="logService"></param>
public SequenceService(ISequenceRepository repository, ISequenceRuleRepository repositoryRule, ILogService logService) : base(repository)
{
_repository = repository;
_logService = logService;
_repositoryRule = repositoryRule;
}
}
构造函数注入带来的性能影响
每个仓储都使用构造函数注入的话,如果模块的业务比较复杂,涉及的实体比较多(比如十几个实体是很经常的事),就会造成每次实例化SequenceService
类的实例的时候,都需要去实例化很多个依赖服务,而实际上**一次业务执行只执行服务中的某个方法,可能也就用到其中的一两个依赖服务,**这就造成了很多不必要的额外工作,也就是性能损耗。
依赖服务注入的性能优化
如果不考虑业务服务的可测试性(单元测试通常需要Mock依赖服务)的话,在业务代码中使用 App.GetService<T>()
的方式来按需获取依赖服务的实例,是比较经济的方式。则服务实现变为如下所示:
/// <summary>
/// 单据编码服务接口实现
/// </summary>
public class SequenceService : BaseService<Sequence, SequenceOutputDto, string>, ISequenceService,ITransientDependency
{
private readonly ISequenceRepository _repository=App.GetService<ISequenceRepository>();
private readonly ISequenceRuleRepository _repositoryRule=App.GetService<ISequenceRuleRepository>();
private readonly ILogService _logService=App.GetService<ILogService>();
/// <summary>
///
/// </summary>
public SequenceService() : base(repository)
{
}
}
4、本例需要重写分页方法
/// <summary>
/// 单据编码服务接口实现
/// </summary>
public class SequenceService : BaseService<Sequence, SequenceOutputDto, string>, ISequenceService,ITransientDependency
{
private readonly ISequenceRepository _repository;
private readonly ISequenceRuleRepository _repositoryRule;
private readonly ILogService _logService;
/// <summary>
///
/// </summary>
/// <param name="repository"></param>
/// <param name="repositoryRule"></param>
/// <param name="logService"></param>
public SequenceService(ISequenceRepository repository, ISequenceRuleRepository repositoryRule, ILogService logService) : base(repository)
{
_repository = repository;
_logService = logService;
_repositoryRule = repositoryRule;
}
/// <summary>
/// 获取最新业务单据编码
/// </summary>
/// <param name="sequenceName">业务单据编码名称</param>
/// <returns></returns>
public async Task<CommonResult> GetSequenceNextTask(string sequenceName)
{
CommonResult result = new CommonResult();
//生成编号
string sequenceNewNo = "";
#region 获取序号生成器属性
if (string.IsNullOrWhiteSpace(sequenceName))
{
result.ErrMsg = "参数错误:业务编码编号";
return result;
}
//获取序号生成器属性
Sequence sequence = _repository.GetWhere("SequenceName='" + sequenceName + "'");
if (sequence != null)
{
IEnumerable<SequenceRule> list = _repositoryRule.GetListWhere("SequenceName='" + sequenceName + "' order by RuleOrder asc");
if (list.Count() > 0)
{
int delimiterNum = 0;
foreach (SequenceRule item in list)
{
delimiterNum++;
switch (item.RuleType)
{
case "const"://常量方式
sequenceNewNo += item.RuleValue;
break;
case "shortdate"://短日期 年2位月2位日期2位
sequenceNewNo += DateTime.Now.ToString("yyyyMMdd").Substring(2);
break;
case "date"://日期,年4位
sequenceNewNo += DateTime.Now.ToString("yyyyMMdd");
break;
case "ydate"://年月,年4位月2位
sequenceNewNo += DateTime.Now.ToString("yyyyMMdd").Substring(0,6);
break;
case "timestamp"://日期时间精确到毫秒
sequenceNewNo += DateTime.Now.ToString("yyyyMMddHHmmssffff");
break;
case "number"://计数,流水号
int num = CurrentReset(sequence, item);
//计数拼接
sequenceNewNo += NumberingSeqRule(item, num).ToString();
//更新当前序号
sequence.CurrentNo =num;
break;
case "guid"://Guid
sequenceNewNo += GuidUtils.NewGuidFormatN();
break;
case "random"://随机数
Random random = new Random();
string strMax = "9".ToString().PadLeft(item.RuleValue.Length - 1, '9');
string strRandom = random.Next(item.RuleValue.ToInt(), strMax.ToInt()).ToString(); //生成随机编号
sequenceNewNo += strRandom;
break;
}
if (!string.IsNullOrEmpty(sequence.SequenceDelimiter)&& delimiterNum!= list.Count())
{
sequenceNewNo += sequence.SequenceDelimiter;
}
}
//当前编号
sequence.CurrentCode = sequenceNewNo;
sequence.CurrentReset = DateTime.Now.ToString("yyyyMMdd");
await _repository.UpdateAsync(sequence, sequence.Id);
result.ResData = sequenceNewNo;
result.Success = true;
}
else
{
result.Success = false;
result.ErrMsg = "未查询到业务编码对应的编码规则配置, 请检查编码规则配置";
return result;
}
}
else
{
result.Success = false;
result.ErrMsg = "请定义" + sequenceName + "的单据编码!";
return result;
}
#endregion
return result;
}
/// <summary>
/// 根据条件查询数据库,并返回对象集合(用于分页数据显示)
/// </summary>
/// <param name="search">查询的条件</param>
/// <returns>指定对象的集合</returns>
public override async Task<PageResult<SequenceOutputDto>> FindWithPagerAsync(SearchInputDto<Sequence> search)
{
bool order = search.Order == "asc" ? false : true;
string where = GetDataPrivilege();
if (!string.IsNullOrEmpty(search.Keywords))
{
where += string.Format(" and SequenceName like '%{0}%' ", search.Keywords);
};
PagerInfo pagerInfo = new PagerInfo
{
CurrenetPageIndex = search.CurrenetPageIndex,
PageSize = search.PageSize
};
List<Sequence> list = await repository.FindWithPagerAsync(where, pagerInfo, search.Sort, order);
PageResult<SequenceOutputDto> pageResult = new PageResult<SequenceOutputDto>
{
CurrentPage = pagerInfo.CurrenetPageIndex,
Items = list.MapTo<SequenceOutputDto>(),
ItemsPerPage = pagerInfo.PageSize,
TotalItems = pagerInfo.RecordCount
};
return pageResult;
}
}
如遇到问题到Issues反馈