基于SqlSugar的开发框架循序渐进介绍(23)-- Winform端管理系统中平滑增加对Web API对接的需求
阅读原文时间:2023年07月09日阅读:1

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

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

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

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

///

/// 数据显示的函数 ///
public async override void DisplayData()
{
if (!string.IsNullOrEmpty(ID))
{
#region 显示信息
var info = await BLLFactory.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(); // 瞬时生命周期
2、services.AddScoped(); // 域生命周期
3、services.AddSingleton(); // 全局单例生命周期

如果使用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服务方式,从而实现更加广泛的业务需求。

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

///

/// 根据配置文件,决定采用直连的DLL,还是代理API的DLL,构建接口进行注入 ///
///
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的代理调用类,那么为了极大的重用常规的接口处理,我们需要类似的继承关系。

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

///

/// 客户信息的Web API调用处理 ///
public class CustomerApiCaller : AsyncCrudApiCaller, ICustomerService
{
}

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

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

///

/// 获取所有对象列表 ///
///
public async virtual Task> GetAllAsync()
{
return await DoActionAsync>("all");
}

///

/// 获取所有对象列表 ///
/// 获取所有条件
///
public async virtual Task> GetAllByIdsAsync(IEnumerable input)
{
return await DoActionAsync>("all-byids", input);
}

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

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

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

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

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章