2023年2月

在上篇随笔《
基于SqlSugar的开发框架循序渐进介绍(23)-- Winform端管理系统中平滑增加对Web API对接的需求
》中介绍了基于一个接口,实现对两种不同接入方式(直接访问数据库实现,基于Web API代理类实现)的处理,由于定义的接口中,我们为了方便,也是用了Lambda表达式的进行一些参数的处理,那么如果在Web API代理类中,Lambda表达式是不能直接传递给Web API的控制器的,那么如何对这个Lambda表达式进行序列化和反序列化还原就是一个急需解决的问题。 本篇随笔介绍采用Serialize.Linq 第三方组件的方式实现对Lambda表达式进行序列化和反序列化的处理。

1、Lambda表达式的接口使用

Lambda 表达式本质上是一个匿名函数,是C#中一种特殊语法,它的引入,使得匿名方法更加简单易用,最直接的是在方法体内调用代码或者为委托传入方法体的形式与过程变得更加优雅。 使用Lambda表达式可大大减少代码量,使得代码更加的优美、简洁,更有
可观性。由于Lambda表达式的便利性,因此虽然在整合多个接入实现比较麻烦一些,我依旧希望通过寻找方法实现对Lambda表达式的兼容处理。

例如,以下就是一个根据名称简单进行判断的Lambda表达式的处理。

/// <summary>
///新增状态下的数据保存/// </summary>
/// <returns></returns>
public async override Task<bool>SaveAddNew()
{
CustomerInfo info
= tempInfo;//必须使用存在的局部变量,因为部分信息可能被附件使用 SetInfo(info); try{#region 新增数据 //检查是否还有其他相同关键字的记录 bool isExist = await BLLFactory<ICustomerService>.Instance.IsExistAsync(s=>s.Name.Equals(info.Name));if(isExist)
{
MessageDxUtil.ShowTips(
"指定的【姓名】已经存在,不能重复添加,请修改");return false;
}
var success = await BLLFactory<ICustomerService>.Instance.InsertAsync(info);if(success)
{
//可添加其他关联操作 return true;
}
#endregion}catch(Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
return false;
}

它的函数原型就是一个Lambda表达式,如下所示的定义

/// <summary>
///判断是否存在指定条件的记录/// </summary>
/// <param name="input">表达式条件</param>
/// <returns></returns>
Task<bool> IsExistAsync(Expression<Func<TEntity, bool>> input);

有些稍微复杂一点的函数,如下定义所示。

/// <summary>
///获取某字段数据字典列表/// </summary>
Task<List<string>> GetFieldList(Expression<Func<TEntity, object>> selector, Expression<Func<TEntity, bool>> where = null);

调用的时候,如下所示。

/// <summary>
///初始化数据字典/// </summary>
private async voidInitDictItem()
{
//初始化代码 var list = await BLLFactory<IFormService>.Instance.GetFieldList(s=>s.Category);this.txtCategory.BindDictItems(list, "");
}

不过简单是简单了,但是本身Lambda表达式不能直接传递给Web API端参数,因为它无法直接序列化进行传递。

我们在之前说过,接入两种不同的数据提供方式。

因此我们为了继续使用Lambda表达是的优点,就需要使用Serialize.Linq对Lambda表达式进行序列化和反序列化。这样就可以在Web API端和Web API 代理端对Lambda表达式进行正常的使用了。

2、采用Serialize.Linq 对Lambda表达式进行序列化和反序列化的处理

首先在需要的地方,引入Serialize.Linq对Lambda表达式进行序列化和反序列化处理。

为了更好通用的实现Lambda表达式的正常序列化为文本,以及对文本的反序列化到Lambda表达式,我们这里编写了一个扩展函数,以便更方便的处理。

    /// <summary>
    ///对Lambda表达式的序列号和反序列化处理/// </summary>
    public static classSerializeExtension
{
/// <summary> ///序列化 LINQ Expression 表达式为JSON文本/// </summary> /// <typeparam name="TEntity">处理对象类型</typeparam> /// <typeparam name="TResult">返回结果类型</typeparam> /// <param name="express"></param> /// <returns></returns> public static string SerializeText<TEntity, TResult>(this Expression<Func<TEntity, TResult>>express)
{
//使用Serialize.Linq组件序列化表达式,传递给API端,API端需要对应反序列化的处理操作进行转换Expression var serializer = new ExpressionSerializer(newJsonSerializer());var expressJson =serializer.SerializeText(express);//接收端的反序列化处理//var express = (Expression<Func<TEntity, TResult>>)serializer.DeserializeText(expressJson); returnexpressJson;
}
/// <summary> ///反序列化JSON文本为LINQ Expression 表达式/// </summary> /// <typeparam name="TEntity">处理对象类型</typeparam> /// <typeparam name="TResult">返回结果类型</typeparam> /// <param name="text"></param> /// <returns></returns> public static Expression<Func<TEntity, TResult>> DeserializeText<TEntity, TResult>(this stringjson)
{
Expression
<Func<TEntity, TResult>> express = null;if (!string.IsNullOrWhiteSpace(json))
{
var serializer = new ExpressionSerializer(newJsonSerializer());
express
= (Expression<Func<TEntity, TResult>>)serializer.DeserializeText(json);
}
returnexpress;
}
}

这样我们来看看两个对Lambda表达式的Web API代理类的封装处理代码

        /// <summary>
        ///根据条件,获取所有记录/// </summary>
        public virtual async Task<ListResultDto<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>> input, string orderBy = null)
{
var express = input.SerializeText(); //使用扩展函数处理生成JSON var postData = new{
express,
orderBy
};
return await DoActionAsync<ListResultDto<TEntity>>("all-expression", postData, HttpVerb.Post);
}
/// <summary> ///根据条件计算记录数量/// </summary> /// <returns></returns> public virtual async Task<long> CountAsync(Expression<Func<TEntity, bool>>input)
{
var expressJson = input.SerializeText(); //使用扩展函数处理生成JSON return await DoActionAsync<long>("count-expression", expressJson, HttpVerb.Post);
}

而对应的在Web API的基类控制器中,我们对这个通用的实现处理下就可以了

        /// <summary>
        ///根据条件,获取所有记录/// </summary>
[HttpPost]
[Route(
"all-expression")]public async Task<ListResultDto<TEntity>>GetAllAsync(ExpressionOrderDtoinput)
{
ListResultDto
<TEntity>? result = null;string json =input.expression;var express = json.DeserializeText<TEntity, bool>();if (express != null)
{
result
= await _service.GetAllAsync(express!);
}
returnresult;
}

[HttpPost]
[Route(
"count-expression")]public virtual async Task<long> CountAsync(dynamicexpressJson)
{
long result = 0;string json =expressJson;var express = json.DeserializeText<TEntity, bool>();if (express != null)
{
result
= await _service.CountAsync(express!);
}
returnresult;
}

这样在服务器端的Web API控制器上,就还原了原先的Lambda表达式,可以正常的接收到客户端提交的条件处理了。

通过这样的迂回的方式,我们就可以在常规的接口,以及Web  API的代理类中,都可以使用到Lambda表达式带来的便利性了,而不需要为了兼容而抛弃Lambda表达式接口参数的方式了。

我们可以把其中相关的Lambda表达式,放在一个区块中,方便查看和处理,如下代码所示是在服务端的Web API控制器的基类函数处理代码。

系列文章:


基于SqlSugar的开发框架的循序渐进介绍(1)--框架基础类的设计和使用


基于SqlSugar的开发框架循序渐进介绍(2)-- 基于中间表的查询处理


基于SqlSugar的开发框架循序渐进介绍(3)-- 实现代码生成工具Database2Sharp的整合开发


基于SqlSugar的开发框架循序渐进介绍(4)-- 在数据访问基类中对GUID主键进行自动赋值处理


基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接口注入方式实现IOC控制反转


基于SqlSugar的开发框架循序渐进介绍(6)-- 在基类接口中注入用户身份信息接口


基于SqlSugar的开发框架循序渐进介绍(7)-- 在文件上传模块中采用选项模式【Options】处理常规上传和FTP文件上传


基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录


基于SqlSugar的开发框架循序渐进介绍(9)-- 结合Winform控件实现字段的权限控制


基于SqlSugar的开发框架循序渐进介绍(10)-- 利用axios组件的封装,实现对后端API数据的访问和基类的统一封装处理


基于SqlSugar的开发框架循序渐进介绍(11)-- 使用TypeScript和Vue3的Setup语法糖编写页面和组件的总结


基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理


基于SqlSugar的开发框架循序渐进介绍(13)-- 基于ElementPlus的上传组件进行封装,便于项目使用


基于SqlSugar的开发框架循序渐进介绍(14)-- 基于Vue3+TypeScript的全局对象的注入和使用


基于SqlSugar的开发框架循序渐进介绍(15)-- 整合代码生成工具进行前端界面的生成


基于SqlSugar的开发框架循序渐进介绍(16)-- 工作流模块的功能介绍


基于SqlSugar的开发框架循序渐进介绍(17)-- 基于CSRedis实现缓存的处理


基于SqlSugar的开发框架循序渐进介绍(18)-- 基于代码生成工具Database2Sharp,快速生成Vue3+TypeScript的前端界面和Winform端界面


基于SqlSugar的开发框架循序渐进介绍(19)-- 基于UniApp+Vue的移动前端的功能介绍


基于SqlSugar的开发框架循序渐进介绍(20)-- 在基于UniApp+Vue的移动端实现多条件查询的处理


基于SqlSugar的开发框架循序渐进介绍(21)-- 在工作流列表页面中增加一些转义信息的输出,在后端进行内容转换


基于SqlSugar的开发框架循序渐进介绍(22)-- Vue3+TypeScript的前端工作流模块中实现统一的表单编辑和表单详情查看处理


基于SqlSugar的开发框架循序渐进介绍(23)-- Winform端管理系统中平滑增加对Web API对接的需求


基于SqlSugar的开发框架循序渐进介绍(24)-- 使用Serialize.Linq对Lambda表达式进行序列化和反序列化

今年开年,最火的莫过于ChatGPT的相关讨论,这个提供了非常强大的AI处理,并且整个平台也提供了很多对应的API进行接入的处理,使得我们可以在各种程序上无缝接入AI的后端处理,从而实现智能AI的各种应用。ChatGPT的API可以在前端,以及一些后端进行API的接入,本篇随笔主要介绍基于ChatGPT的API的C#接入研究。

1、ChatGPT的介绍

ChatGPT(全名:Chat Generative Pre-trained Transformer),美国OpenAI研发的聊天机器人程序 ,于2022年11月30日发布。ChatGPT是人工智能技术驱动的自然语言处理工具,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,真正像人类一样来聊天交流,甚至能完成撰写邮件、视频脚本、文案、翻译、代码,写论文 等任务。

ChatGPT是美国人工智能研究实验室OpenAI新推出的一种人工智能技术驱动的自然语言处理工具,使用了Transformer神经网络架构,也是GPT-3.5架构,这是一种用于处理序列数据的模型,拥有语言理解和文本生成能力,尤其是它会通过连接大量的语料库来训练模型,这些语料库包含了真实世界中的对话,使得ChatGPT具备上知天文下知地理,还能根据聊天的上下文进行互动的能力,做到与真正人类几乎无异的聊天场景进行交流。ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

ChatGPT 的官网地址如下:
https://platform.openai.com/
,如果我们需要使用它的对话处理以及强大的API能力,需要注册才能使用,至于如何注册以及接收验证码的操作,请百度一下一下即可,这里忽略,只是介绍它的功能以及如何介入使用。

ChatGPT 可以做很多不同类型的工作,常规的问答聊天,编写各种语言的应用代码,编写论文、摘要等,以及图片处理,翻译等等,如下是它们的一些功能分类和介绍。

如我们可以在对话中测试其聊天/应答功能。

我们也可以让它编写一段操作代码,如下所示。

如果我们要了解ChatGPT平台的API介绍,可以参考
https://platform.openai.com/docs/api-reference/introduction
,其大概分类也是很常规的功能应用相关的,如下是它的列表介绍。

我们要学会如何使用API的话,需要了解它的相关模型概念,以及对各种处理的定义。

2、基于ChatGPT的API的C#接入

从上面的API介绍中,我们可以看到,API平台没有针对不同的语义给出不同的实现方式,只是给出了一个通用的调用方式,主要是基于输入参数,响应的数据格式的参考,具体的不同语言的应用如C#的接入,需要根据实际的格式进行对接。

主要的代码如下所示。

usingRestSharp;usingSystem;namespaceGpt3APIExample
{
classProgram
{
static void Main(string[] args)
{
//OpenAI API地址 string apiUrl = "https://api.openai.com/v1/engines/gpt-3/jobs";//OpenAI API密钥 string apiKey = "YOUR_API_KEY";//创建一个RestClient对象 var client = newRestClient(apiUrl);//创建一个RestRequest对象 var request = newRestRequest(Method.POST);//在请求头中添加API密钥 request.AddHeader("Authorization", "Bearer" +apiKey);//添加请求内容 request.AddJsonBody(new{
model
= "text-davinci-002",
prompt
= "What is the capital of France?",
max_tokens
= 100,
n
= 1,
stop
= null,
temperature
= 0.5,
});
//发送请求并获取响应 IRestResponse response =client.Execute(request);//显示响应内容 Console.WriteLine(response.Content);
Console.ReadLine();
}
}
}

这个代码主要就是针对输入信息和输出的内容进行简单的包装,使用 RestSharp 的一个Http类库进行访问的包装。

而为了更好的使用ChatGPT的API处理,我们可以使用更高级一点的类库,如下面介绍的两个开源C#包装ChatGPT的类库:

https://github.com/OkGoDoIt/OpenAI-API-dotnet

https://github.com/RageAgainstThePixel/OpenAI-DotNet

我觉得他们都是很不错的,都是基于ChatGPT的API格式进行了一定程度的面向对象的包装,使得我们基于C#开发起来更方便。

例如我们基于
https://github.com/RageAgainstThePixel/OpenAI-DotNet

来查看使用代码,它的介绍也比较详细,基本上覆盖到了各个方面。

我们可以模仿来生成自己的代码即可,如下所示。

    /// <summary>
    ///基于OpenAI-DotNet的使用/// </summary>
    public classTest2
{
public static asyncTask ExcuteCode()
{
var api = new OpenAIClient(newOpenAIAuthentication(Constants.ApiKey));var result = await api.CompletionsEndpoint.CreateCompletionAsync("读取图片文件的展示到窗体的C#代码", null, null, 1000, 0.1);
Log.Information(result.ToJson());
}

运行效果,可以看到输出的结果。

对于图片的生成和处理,我们也可以按照类似的API进行调用接口,如下是一段寻找图片的代码。

            var results = await api.ImagesEndPoint.GenerateImageAsync("中国布达拉宫的全景图片", 1, ImageSize.Large);foreach (var item inresults)
{
Log.Information(item);
}

为了验证图片的AI处理,我们生成两张正方形的图片,图片右下角扣掉一块空白的图片,图片格式使用PNG格式,然后调用如下代码进行替换处理。

            var imageAssetPath = "images/休息区.png";var maskAssetPath = "images/休息区2.png";var imageResults = await api.ImagesEndPoint.CreateImageEditAsync(Path.GetFullPath(imageAssetPath), Path.GetFullPath(maskAssetPath), "阳光明媚的室内休息区,有一个鸭子在池塘中", 1, ImageSize.Small);foreach (var item inimageResults)
{
Log.Information(item);
}

生成后的图片效果如下所示。

生成的空白地方,填入一个所需要的区域,红色框是我加上去醒目的。

一 回调方法定义

控制器中定义回调方法

@GetMapping("callback")public String callback(String code, String state, HttpSession session) {System.out.println("callback被调用");System.out.println("code:" + code);System.out.println("state:" + state);return null;}

用户点击“确认登录”后,微信服务器会向谷粒学院的业务服务器发起回调,回调地址就是yml中配置的redirecturi。

二 内网穿透

1 开发步骤

步骤:开通并启动内网穿透ngrok > 开放平台配置回调地址 > yml配置

yml配置:

wx:open:# 微信开放平台 appidappId: <微信开放平台 appid># 微信开放平台 appsecretappSecret: <微信开放平台 appsecret># 微信开放平台 重定向url(guli.shop需要在微信开放平台配置)redirectUri: <微信中你配的回调地址>

注意:yml文件中redirecturi的域名必须和开放平台中应用配置的授权回调域的值完全一致,

但是开放平台上的一个应用只能配置一个回调地址,提供给一个开发者使用。

2 开通方式

ngnok的地址:http://ngrok.cc

开通方式:

3 使用方式

下载客户端,启动客户端

4 原理说明

三 外网服务器跳转

解决多人无法共享回调域设置的问题。

步骤:将跳转程序部署到外网服务器 > 开放平台配置回调地址 > yml配置

跳转程序:部署在guli.shop上

guli.shop服务器的接口可以接收微信的回调请求,将微信回调请求转发到开发者的localhost的8160端口,并传递code和state参数

开放平台配置:

授权回调域一般设置为一个内网穿透地址,例如使用ngrok工具申请一个内网穿透地止

yml配置

wx:open:# 微信开放平台 appidappId: wxed9954c01bb89b47# 微信开放平台 appsecretappSecret: a7482517235173ddb4083788de60b90e# 微信开放平台 重定向url(guli.shop需要在微信开放平台配置)redirectUri: http://guli.shop/api/ucenter/wx/callback8160

四 测试回调跳转服务器

访问回调服务器

http://guli.shop/api/ucenter/wx/callback8160?code=1234&state=666

跳转到

http://localhost:8160/api/ucenter/wx/callback?code=1234&state=666


我在起前面的几篇随笔中,大概介绍了工作流的一些场景化处理,包括如何把具体业务表单组件化,并在查看和编辑界面中,动态加载组件内容,以及对于查看申请单的主页面,把审批、取消、发起会签、会签、批示分阅、阅办等处理过程的进行模块化,并在主页面中灵活引入集成,本篇随笔综合性的介绍Vue&Element开发框架中增加的工作流各个管理界面的内容,以供参考交流。

1、工作流模块化划分

为了更有效的维护和管理工作流的设置和申请单信息,我们把工作流的内容分为三个大部分:工作流维护、工作流业务、业务表单。

工作流维护,主要是工作流模板的管理,包括流程模板的管理、以及流程环节的管理

工作流业务,主要是根据系统模板,提供创建流程申请单的入口(业务受理列表),我的审批工作(当前用户),所有审批工作(管理员维护),我的草稿(当前用户)

业务表单,这是根据具体表单的信息,提供查询统计的处理,包括各类业务表单,如付款申请单、物品领用单等等。

这些工作流的业务菜单如下所示。

2、流程环节管理

我们要开始一项工作流的申请单,首先需要配置好这个申请单的一些基本信息,其中流程环节就是决定每个步骤执行那种处理类型(如审批、撤销)的,因此流程环节也算是最基本的步骤类型定义了,也就是决定针对这类型的步骤的统一处理界面。

里面预定义了一些如审批、归档、会签、阅办等系统性的步骤类型,还可以根据需要自定义处理类型,自定义处理类型后,我们创建对应的处理规则即可在查看详细申请的界面中进行调用处理。

3、流程模板管理

完成了流程步骤类型的定义,就可以开始流程模板的管理了,流程模板主要配置相关工作流的信息,如表单的分类,对应数据表,流程步骤定义和顺序等等。

流程步骤的界面管理如下所示,可以通过双击修改,拖动调整顺序的方式管理各个步骤的信息。

编辑界面中,实现对流程模板的一些信息进行维护,如下所示。

最终,我们根据以上界面完成了流程模板和流程实例,以及相关步骤的设置处理,如下图所示。

4、业务受理列表

我们完成了流程模板,对于启用的流程模板,我们提供给用户创建具体的流程实例申请单,也就是提供创建流程申请单的入口。

由于我们约定了模板的名称和具体申请单的关系,因此查看申请单和编辑申请单的时候,使用的是同一个路由地址页面,而根据传入参数的不同,动态展示组件信息,供查看或者编辑处理。

但我们创建具体的表单的时候,根据表单的编辑界面,录入不同的流程申请单的数据,以及附件、清单、流程用户等信息。

对于主从表单的处理,我们可以通过利用Vxe-table插件的方式直接录入数据的方式进行录入 。

5、我的审批工作

我的审批工作,主要是根据当前用户列出不同类型(我发起的、我的待办、我的已办)等不同状态的申请单,从而可以快速处理自己的工作流审批业务工作。

通过双击行记录或者单击查看按钮,可以打开详细的申请单信息进行处理。

查看申请单中,会根据申请单的状态,以及当前用户的身份等等,自动展示可以操作流程的按钮。

其中流程日志展示相关的流程日志和系统日志等信息,供参考了解。

系统日志信息,则详细记录每个步骤的创建和处理信息。如在用户完成当前步骤的时候,自动创建新的流程步骤信息的日志。

6、所有审批工作

为了方便管理员查看不同的业务表单,以便核对处理或者删除等操作,我们提供了一个管理界面,可以看到所有申请单的记录信息,同时记录提供删除操作,删除申请单的时候,会把该申请单的相关步骤信息,执行用户信息,对应表单记录等信息一并抹除。

7、我的草稿

为了便于在创建申请单的时候暂存草稿信息,我们提供了一个通用的草稿存储表,用于存储任何信息的申请单信息。

在申请单创建界面里面,在末尾都会提供一个【保存草稿】的按钮,如下所示。

草稿的管理界面如下所示。

单击编辑按钮,可以对该草稿进行编辑,以便恢复到之前的录入状态。

8、业务表单

业务表单则是提供对具体类型的申请单数据进行查看,如下菜单所示。

这样不同的表单,有不同的查询界面,可以提供更加进行的业务表单数据查询或者统计处理。

在我们开发各种项目应用的时候,往往都是基于一定框架进行,同时配合专用的代码生成工具,都是为了快速按照固定模式开发项目,事半功倍,本篇随笔对基于ABP开发框架的技术点进行分析和ABP框架项目快速开发实现进行介绍,抛砖引玉的对我们技术人员的日常技术工作进行一定的总结,希望大家不吝支持。

1、ABP开发框架的项目开发

下面对ABP框架的数据库支持、管理端界面、功能模块划分、ABP框架特点、模块开发支持等方面进行介绍,如下图所示。

我们知道,一般我们开发某个项目的时候,往往都会关注项目默认支持的数据库,以及后续能够支持的一些数据库,对于一般的应用,当然是希望它能够支持多种数据库。

对于ABP框架来说,它的底层是基于.netcore的Entity Framework Core ,它对于常规的数据库基本上都是支持的,如下所示。

关于ABP框架使用Oracle数据库及数据库的迁移,可以参考我的随笔《
ABP框架使用Oracle数据库,并实现从SQLServer中进行数据迁移的处理

关于ABP框架使用Mysql数据库及数据库的迁移,可以参考我的随笔《
ABP框架使用Mysql数据库,以及基于SQLServer创建Mysql数据库的架构和数据

现在框架基本上都是多端应用的了,所以在ABP框架中整合Winform管理端、Vue&element的BS前端,以及公司动态网站用于发布产品和网站信息等都是常见的应用,有时候,我们还需要根据功能的需要,增加一些小程序的支持,这些对于Web API后端来说,都是很容易接入的应用处理。

Winform应用端,其实很多时候是很必要的,提供众多丰富的功能展示,以及很好的用户交互能力,特别在对接硬件或者打印处理的时候。

和我们的Winform框架一样,Winform应用端提供封装良好的框架组件模块,包括权限系统、字典模块、附件管理模块、自动升级、分页控件、公用类库及界面基类模块等模块。

而基于Vue + Element 的前端界面,和基于Winform的ABP框架一样,使用同一个API后端,模块包括用户管理、组织机构管理、角色管理、菜单管理、功能管理及权限分配,日志管理、字典管理、产品管理等管理功能,可实现用户的功能及数据权限进行控制管理。

公司动态门户网站,有时候用于我们发布网站信息和产品信息的一个门户网站,采用了Bootstrap-Vue界面组件,由于大多数门户网站都是基于Bootstrap栅格系统的,因此基于最新Bootstrap-Vue界面组件也就是最佳选择的了,而且可以重用很多Bootstrap的网站模板案例。这样也同时保持了前端模块同时也是基于Vue的,摒弃了以前基于JQuery的繁琐操作DOM处理。

有了这些管理端,基本上也就满足了大部分的需求,不过由于现在微信小程序的广泛应用,有时候我们针对一些业务模块功能,可以根据需要推出一些小程序应用场景,这些对接我们的统一授权系统,以及统一的WebAPI调用机制即可满足。

前面介绍了,基于ABP开发框架的Winform端、Vue&Element前端,都整合了很多常规的模块,如权限、字典、附件等常见的模块,这些既可以是系统性的管理模块,也是基础性数据的支撑,同时也会提供众多组件给我们应用模块进行使用的。

而我们在框架提供这些内容外,都需要根据具体业务的需要,按照框架的架构、编码标准要求,增量式的开发一些业务模块界面,一般最好的清空下是借助辅助工具的使用,能够节省大量的时间,同时降低难度,事半功倍。

2、ABP框架的代码生成

借助我们ABP框架的定制代码生成工具Database2sharp,可以对Winform界面开发、Vue&Element界面开发、ABP框架后端代码进行全栈式的开发处理。

Database2sharp它通过整合框架相关的生成规则,我们可以增加对应的ABP框架后端代码的生成,如下代码生成工具界面所示。

Database2Sharp关于ABP框架的Winform界面配置如下界面所示,可以定制化的生成Winform界面代码。

在代码生成工具Database2Sharp界面里面,选择【代码生成】【ABP的Vue+Element界面代码】,如下所示。

确认生成代码后,我们可以看到对应的API调用JS类和Vue视图文件,如下所示。

3、ABP框架的特点

ABP框架,主要可以概括为下面几点。

详细的话,ABP框架涉及到的内容,包括下面这些。

  • 依赖注入
    ,这个部分使用 Castle windsor (依赖注入容器)来实现依赖注入,这个也是我们经常使用IOC来处理的方式;
  • Repository仓储模式
    ,已实现了Entity Framework、NHibernate、MangoDB、内存数据库等,仓储模式可以快速实现对数据接口的调用;
  • 身份验证与授权管理
    ,可以使用声明特性的方式对用户是否登录,或者接口的权限进行验证,可以通过一个很细粒度的方式,对各个接口的调用权限进行设置;
  • 数据有效性验证
    ,ABP自动对接口的输入参数对象进行非空判断,并且可以根据属性的申请信息对属性的有效性进行校验;
  • 审计日志记录
    ,也就是记录我们对每个接口的调用记录,以及对记录的创建、修改、删除人员进行记录等处理;
  • Unit Of Work工作单元模式
    ,为应用层和仓储层的方法自动实现数据库事务,默认所有应用服务层的接口,都是以工作单元方式运行,即使它们调用了不同的存储对象处理,都是处于一个事务的逻辑里面;
  • 异常处理
    ,ABP框架提供了一整套比较完善的流程处理操作,可以很方便的对异常进行进行记录和传递;
  • 日志记录
    ,我么可以利用Log4Net进行常规的日志记录,方便我们跟踪程序处理信息和错误信息;
  • 多语言/本地化支持
    ,ABP框架对多语言的处理也是比较友好的,提供了对XML、JSON语言信息的配置处理;
  • Auto Mapping自动映射
    ,这个是ABP的很重要的对象隔离概念,通过使用AutoMaper来实现域对象和DTO对象的属性映射,可以隔离两者的逻辑关系,但是又能轻松实现属性信息的赋值;
  • 动态Web API层
    ,利用这个动态处理,可以把Application Service 直接发布为Web API层,而不需要在累赘的为每个业务对象手工创建一个Web API的控制器,非常方便;
  • 动态JavaScript的AJax代理处理
    ,可以自动创建Javascript 的代理层来更方便使用Web Api,这个在Web层使用。

一般来说,对于数据库的操作,用标准仓储模式处理就可以了,而且我们基于框架的基类封装,可以减少很多标准的处理代码,只需要简单继承一下关系即可。

其中基类MyAsyncServiceBase 已经封装了常见的数据库操作处理。

而这个MyAsyncServiceBase 构造函数包含了相关的DTO对象,方便用于整个对象接口的处理,如查询条件、返回对象DTO、领域对象、主键类型等等。

如果不需要其他业务对象的操作,那么就保留默认的仓储对象即可,如果需要使用其他业务对象处理,那么同时引入,并在构造函数中注入所需的仓储对象,如下面所示。

一般来说,这些集成关系,我们在利用代码生成工具生成代码的时候,都已经完全处理好的了,各层的关系也都一一生成。

它主要是分为下面几个项目分层。

Core领域核心层
,领域层就是业务层,是一个项目的核心,所有业务规则都应该在领域层实现。这个项目里面,除了定义所需的领域实体类外,其实可以定义我们自己的自定义的仓储对象(类似DAL/IDAL),以及定义自己的业务逻辑层(类似BLL/IBLL),以及基于AutoMapper映射规则等内容。

EntityFrameworkCore
实体框架核心层,这个项目不需要修改太多内容,只需要在DbContext里面加入对应领域对象的仓储对象即可。

Application.Common和
Application应用层

:应用层提供一些应用服务(Application Services)方法供展现层调用。一个应用服务方法接收一个DTO(数据传输对象)作为输入参数,使用这个输入参数执行特定的领域层操作,并根据需要可返回另一个DTO。

Web.Core Web核心层
,基于Web或者Web API的核心层,提供了对身份登陆验证的基础处理,没有其他内容。

Web.Core.Host Web API的宿主层
,也是动态发布Web API的核心内容,另外在Web API里面整合了Swagger,使得我们可以方便对Web API的接口进行调试。

Migrator数据迁移层
,这个是一个辅助创建的控制台程序项目,如果基于DB First,我们可以利用它来创建我们项目的初始化数据库。