在前面随笔介绍的基于SqlSugar的WInform端管理系统中,数据提供者是直接访问数据库的方式,不过窗体界面调用数据接口获取数据的时候,我们传递的是标准的接口,因此可扩展性比较好。我曾经在随笔《基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接口注入方式实现IOC控制反转》中介绍过,该SqlSugar开发框架本身是基于IOC控制反转的,因此对于接入不同的数据提供者,只需要切换到对应的实现层上即可。本篇随笔介绍基于SqlSugar开发框架的Winform端,实现包括对直接访问数据库,远程调用Web API接口的两种不同的处理方式的整合。

1、Winform模块中对具体接口的调用及接口注册

Winform中的界面展示,以及数据处理,都需要具体实现的支撑,由于本身IOC控制反转的接口设计,我们对具体数据的访问,也是基于特定的接口层进行调用的,具体的实现,则是在程序启动的时候,注入对应的接口实现即可。

例如对于客户信息的展示业务操作,代码如下所示

/// <summary>
/// 数据显示的函数
/// </summary>
public async override void DisplayData()
{
if (!string.IsNullOrEmpty(ID))
{
#region 显示信息
var info = await BLLFactory<ICustomerService>.Instance.GetAsync(ID);
if (info != null)
{
tempInfo = info;//重新给临时对象赋值,使之指向存在的记录对象
txtName.Text = info.Name;
txtAge.Value = info.Age;
}
#endregion
//this.btnOK.Enabled = HasFunction("Customer/Edit");
}
else
{
//this.btnOK.Enabled = HasFunction("Customer/Add");
}
}

上面代码可以看到,我们是调用接口进行数据的处理的,而这个接口就是在程序启动之处,通过自动的方式获得对应的接口和实现类,然后进行注入的。

.net 中 负责依赖注入和控制反转的核心组件有两个:IServiceCollection和IServiceProvider。其中,IServiceCollection负责注册,IServiceProvider负责提供实例。

在注册接口和类时,IServiceCollection提供了三种注册方法,如下所示:

1、services.AddTransient<IDictDataService, DictDataService>();  // 瞬时生命周期
2、services.AddScoped<IDictDataService, DictDataService>(); // 域生命周期
3、services.AddSingleton<IDictDataService, DictDataService>(); // 全局单例生命周期

如果使用AddTransient方法注册,IServiceProvider每次都会通过GetService方法创建一个新的实例;

如果使用AddScoped方法注册, 在同一个域(Scope)内,IServiceProvider每次都会通过GetService方法调用同一个实例,可以理解为在局部实现了单例模式;

如果使用AddSingleton方法注册, 在整个应用程序生命周期内,IServiceProvider只会创建一个实例。

前面说到,接口我们是自动遍历响应的程序集进行注册的,注册接口的逻辑,我们可以统一抽取唯一个公用的函数处理,如下代码所示。

        /// <summary>
/// 配置依赖注入对象
/// </summary>
/// <param name="services"></param>
public static void ConfigureRepository(IServiceCollection services)
{
#region 自动注入对应的服务接口
var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
var getFiles = Directory.GetFiles(path, "*.dll").Where(Match); //.Where(o=>o.Match())
var referencedAssemblies = getFiles.Select(Assembly.LoadFrom).ToList(); //.Select(o=> Assembly.LoadFrom(o)) var baseType = typeof(IDependency);
var types = referencedAssemblies
.SelectMany(a => a.DefinedTypes)
.Select(type => type.AsType())
.Where(x => x != baseType && baseType.IsAssignableFrom(x)).ToList();
var implementTypes = types.Where(x => x.IsClass).ToList();
var interfaceTypes = types.Where(x => x.IsInterface).ToList(); RegisterService(services, implementTypes, interfaceTypes); #endregion
}

如果我们这里增加一个对Web API的调用,那么在这里注册的时候,切换向Web API代理的注册接口就可以,如下图所示。

因此原来的Winform界面上的调用代码,不需要任何变化,只需要注入不同的接口实现,就能获得不同的方式:普通访问数据库方式,还是分布式获取服务WebAPI的处理方式。

通过切换开关变量的方式,客户可以非常方便的自由切换不同的数据访问方式。数据提供服务,可以是直接访问数据库的方式,也可以是远端的Web API服务方式,从而实现更加广泛的业务需求。

根据不同开关变量,处理不同的接口注册的代码如下所示。

/// <summary>
/// 根据配置文件,决定采用直连的DLL,还是代理API的DLL,构建接口进行注入
/// </summary>
/// <param name="services"></param>
public static void ConfigureRepositoryAuto(IServiceCollection services)
{
var config = new AppConfig();
string callerType = config.AppConfigGet("CallerType");
if (!string.IsNullOrWhiteSpace(callerType) && callerType.Equals("api", StringComparison.OrdinalIgnoreCase))
{
//如果配置为API模式
ConfigureRepositoryApi(services);
}
else
{
//如果配置为普通模式
ConfigureRepository(services);
}
}

API方式的注册,和普通的注册方式类似,就是定位具体的实现,获得接口和具体的实现对象,进行服务注册即可,在此不再赘述。

2、具体的Web API代理实现

在随笔《基于SqlSugar的开发框架循序渐进介绍(5)-- 在服务层使用接口注入方式实现IOC控制反转》我们介绍过具体实现类的继承关系,一般都是构建相应的基类和接口,然后才是具体的业务实现和接口,这样处理可以重用基类的很多接口,提高代码的重用效率。

我们以其中简单的Customer业务表为例,它的服务类代码如下所示(主要关注服务类的定义即可)。

    /// <summary>
/// 客户信息应用层服务接口实现
/// </summary>
public class CustomerService : MyCrudService<CustomerInfo, string, CustomerPagedDto>, ICustomerService
{
...............
}

而对应Web API的代理调用类,那么为了极大的重用常规的接口处理,我们需要类似的继承关系。

具体的代码实现关系如下所示。

/// <summary>
/// 客户信息的Web API调用处理
/// </summary>
public class CustomerApiCaller : AsyncCrudApiCaller<CustomerInfo, string, CustomerPagedDto>, ICustomerService
{
}

我们可以利用代码生成工具生成主要的继承关系,然后实现具体的函数封装即可。我们独立一个项目用来承载API的代理类处理。

在AsyncCrudApiCaller 类中做了很多Web API的调用封装,因此对于具体的调用,代码是比较简单的。

/// <summary>
/// 获取所有对象列表
/// </summary>
/// <returns></returns>
public async virtual Task<ListResultDto<TEntity>> GetAllAsync()
{
return await DoActionAsync<PagedResultDto<TEntity>>("all");
} /// <summary>
/// 获取所有对象列表
/// </summary>
/// <param name="input">获取所有条件</param>
/// <returns></returns>
public async virtual Task<ListResultDto<TEntity>> GetAllByIdsAsync(IEnumerable<TPrimaryKey> input)
{
return await DoActionAsync<PagedResultDto<TEntity>>("all-byids", input);
}

GET参数可以选用Dict方式传递,或者直接传入匿名类也可以,后台代码自动生成相关的URL参数传递的。

public async Task<bool> SetDeletedFlag(int id, bool deleted = true)
{
var action = $"set-deleted";
var input = new
{
id,
deleted
};
return await DoActionAsync<bool>(action, input, HttpVerb.Post);
}
public async Task<OuInfo> FindByName(string name)
{
var action = $"byname/{name}";
var dict = new Dictionary<string, string> { { "name", name } };
return await DoActionAsync<OuInfo>(action, dict, HttpVerb.Get);
}

剩下的任务就是完善ApiCaller项目的类,与Web API控制器提供的接口的对应关系了,处理完成后,就可以进行测试了。

只要做好模块接口的对接关系,界面的处理代码不用变化就可以切换到其他方式上去了(如Web API的数据提供方式)。

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

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

    我喜欢在一个项目开发模式成熟的时候,使用代码生成工具Database2Sharp来配套相关的代码生成,对于我介绍的基于SqlSugar的开发框架,从整体架构确定下来后,我就着手为它们量身定做相关的代码 ...

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

    在前面随笔,我们介绍过这个基于SqlSugar的开发框架,我们区分Interface.Modal.Service三个目录来放置不同的内容,其中Modal是SqlSugar的映射实体,Interface ...

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

    在基于SqlSugar的开发框架中,我们设计了一些系统服务层的基类,在基类中会有很多涉及到相关的数据处理操作的,如果需要跟踪具体是那个用户进行操作的,那么就需要获得当前用户的身份信息,包括在Web A ...

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

    在早期的随笔就介绍过,把常规页面的内容拆分为几个不同的组件,如普通的页面,包括列表查询.详细资料查看.新增资料.编辑资料.导入资料等页面场景,这些内容相对比较独立,而有一定的代码量,本篇随笔介绍基于V ...

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

    在我们实际项目开发过程中,往往需要根据实际情况,对组件进行封装,以更简便的在界面代码中使用,在实际的前端应用中,适当的组件封装,可以减少很多重复的界面代码,并且能够非常简便的使用,本篇随笔介绍基于El ...

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

    刚完成一些前端项目的开发,腾出精力来总结一些前端开发的技术点,以及继续完善基于SqlSugar的开发框架循序渐进介绍的系列文章,本篇随笔主要介绍一下基于Vue3+TypeScript的全局对象的注入和 ...

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

    在前面随笔<基于SqlSugar的开发框架循序渐进介绍(12)-- 拆分页面模块内容为组件,实现分而治之的处理>中我们已经介绍过,对于相关的业务表的界面代码,我们已经尽可能把不同的业务逻辑 ...

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

    在一个应用系统的开发框架中,往往很多地方需要用到缓存的处理,有些地方是为了便于记录用户的数据,有些地方是为了提高系统的响应速度,如有时候我们在发送一个短信验证码的时候,可以在缓存中设置几分钟的过期时间 ...

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

    在做一些常规应用的时候,我们往往需要确定条件的内容,以便在后台进行区分的进行精确查询,在移动端,由于受限于屏幕界面的情况,一般会对多个指定的条件进行模糊的搜索,而这个搜索的处理,也是和前者强类型的条件 ...

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

    有时候,为了给前端页面输出内容,有时候我们需要准备和数据库不一样的实体信息,因为数据库可能记录的是一些引用的ID或者特殊字符,那么我们为了避免前端单独的进行转义处理,我们可以在后端进行统一的格式化后再 ...

随机推荐

  1. display:block 和display:inline-block的区别和用法

    1).块状元素:(div,p,form,ul,ol,li) ,独占一行,默认情况width为100% 2).行内块状元素:(span,img,a),不会独占一行,相邻的元素一直排在同一行,排满了才会换 ...

  2. Mysql编程中遇到的小错误

    我在mysql中创建的数据库表语句为如下 create table grade (id int not null, name varchar(255), desc varchar(255), prim ...

  3. JVM中的方法区

    JVM中的方法区 方法区存储什么? 用于存储已被虚拟机加载的类型信息.常量.静态变量.即时编译器编译后的代码缓存 1.类型信息 对每个加载的类型(类class.接口interface.枚举.注解)jv ...

  4. IDEA中设置背景图片(超详细)

    文章目录 1.效果图 2.详细设置过程 1.效果图 2.详细设置过程

  5. Codeforces Round #553 (Div. 2)/codeforces1151

    CodeForces1151 Maxim and Biology 解析: 题目大意 每次可以使原串中的一个字符\(+1/-1\),\(Z + 1\to A, A -1\to Z\),求至少修改多少次可 ...

  6. 从0搭建vue3组件库:自动化发布、管理版本号、生成 changelog、tag

    今天看到一篇文章中提到了一个好用的工具release-it.刚好可以用在我正在开发的vue3组件库.纸上得来终觉浅,绝知此事要躬行,说干就干,下面就介绍如何将release-it应用到实际项目中,让组 ...

  7. 三、redis环境安装

    三.redis环境安装 3.1.下载和安装 下载地址:https://github.com/tporadowski/redis/releases   使用以下命令启动redis服务端 redis-se ...

  8. 开源网络协议栈onps诞生记

    小孩没娘,说来话长,一切都要从LwIP说起.大约是06年9月,本人在二姨的坛口发布了一篇小文--<uC/OS-II 平台下的 LwIP 移植笔记>.自此一发不可收拾,开启了一段我与LwIP ...

  9. 怎么实现无痛刷新token

    最近遇到这个需求,前端登录后,后端返回  access_token 和 refresh_token ,当token 过期时用旧的 refresh_token 去获取新的token,前端要不痛去刷新to ...

  10. 😊SpringBoot 整合 Elasticsearch (超详细).md

    SpringBoot 整合 Elasticsearch (超详细) 注意: 1.环境搭建 安装es Elasticsearch 6.4.3 下载链接 为了方便,环境使用Windows 配置 解压后配置 ...