在net Core3.1上基于winform实现依赖注入实例
2021-03-08 19:27
标签:overflow ack 工厂 throw ima inject dapper 其它 esc net core3.1是微软LTS长期3年支持版本,正式发布于2019-12-03,并且在windows平台上支持了Winfrom跟WPF桌面应用。本文介绍了使用Winform时的第一步,将应用层以及ORM涉及到的DBconfig,仓储层等依赖注入到容器中,并通过构造函数法从容器中调用实例,供给各窗体控件使用。 依赖注入是通过反转控制(IOC),设计模式属于代理模式+工厂模式,由serviceProvider根据实例接口或者实例类型调用,注入时生命周期的设置,控制实例化及配置实例生命周期,并返回实例给程序员调用,从而达到解放程序员的生产力,不用再去new 一个个实例,也不用去考虑实例之间的依赖关系,也不用去考虑实例的生命周期。实现,分为三个阶段,第一,程序员将服务注入服务容器阶段,第二程序员DI实例调用阶段,第三serviceProvider服务管理者根据注入时的配置返回给程序对应的实例以及配置好实例的生命周期。 一张图就可以理解依赖注入实例调用过程 图片来源出处,感谢作者。 这里再向读者做个说明ServiceCollection是服务容器,serviceProvider是服务管理者,管理着服务容器,当程序发送抽象接口,或者类型时,serviceProvider会根据设置好的生命周期,返回需要的实例配置好实例的生命周期给程序员使用。 通过代理模式serviceProvider控制反转,他将持有控制权,将所有需要用到的接口,类型,反射出对应的实例,实例化以及设置好实例的生命周期,然后将控制权返还给程序员,不用再去new 一个个实例,也不用去考虑实例之间的依赖关系,也不用去考虑实例的生命周期,最终目的就是解放程序员的生产力,让程序员更轻松地写程序。 在注入的同时可以设置如下三种生命周期: 可自行做测试,具体可参考此博客 如上,实例是在HomeController中通过接口来调用实例的,因此修改程序只需要在实例中需改,而不需要在调用层修改。 3.细节应该依赖于抽象 服务类的实现也依赖于接口: 使程序员不用再去考虑各个DI实例之间的依赖,以及new很多个相互依赖的实例。 在依赖注入的服务调用的地方,容器管理者serviceProvider从程序员手中取得控制权,控制所需服务实例化以及设置好他的生命周期,然后返回给程序员。 根据DI的生命周期设置,根据接口或者类型,生产出各种生命周期的实例,需要注意的是这里有可能是同一实例(scope的单次请求中,或者Transient生命周期),Transient每次产生的都是新的实例。 笔者发现在最新的VS发行版中,能创建winform工程,但却无法打开设计器,也无法打开winform的工具箱。怎么办? NetCore3.1 winform截图如下: 可以看到控件明显比基于dot Net Framework的好看很多,同时,工具箱中的控件很少,微软把一些老的已经有替代的控件删除了,并且以后会慢慢加入一些必要的控件。 net core MVC容器是自动创建好的,只需要在ConfigureServices方法里配置服务即可。而在Net Core3.1上创建了winform工程之后窗体是new实例,以单例的形式跑的。容器的配置创建,都需要自己来做。 那如果需要向Form窗体中注入服务就需要在new实例的时候就传入实参。 调用的时候用窗体的构造函数调用服务接口即可。 本方法摘自此文 这样至少有两个缺点: 把form的类型也以单例的形式注入到容器中,调用时,获取MainForm类型的服务。这样此服务实例依赖于其他的服务。ServiceProvider容器管理者会自动解决好服务之间的依赖关系,并将对应的服务实例化并根据生命周期设置好,交给程序员去使用。问题完美解决。 此思路有借鉴于以下两篇文章 这里需要说明的是,笔者这里的IoC是应用层,展现层,仓储层分层注入了,每层都写了ServiceCollection服务容器的静态方法,所以服务可以在各层注入,读者可以不去追究,将自己的服务注入在此即可。 分层注入简单实现 重点关注 此步骤调用的就是3.4.1中的方法。 向服务管理者请求MainForm类型的实例服务,具体调用过程详见2.1。 这一步是重点,也是winform跟MVC使用上的区别,但是本质却是相同的,都是由serviceProvider管理着WPF,winform或者MVC这些实例以及他们对应的类型,只不过MVC容器已经创建好了,容器管理者serviceProvider也已经创建好了,直接往容器里Add服务即可,而winform,WPF,net core控制台程序需要我们自己去往容器里添加注册服务,并且创建容器管理者serviceProvider。因为ServiceCollection容器是死的,只有创建了serviceProvider容器管理者这个代理角色,容器才能体现出他的价值。而只有serviceProvider,没有ServiceCollection里的服务也是毫无意义的。 3.4.1到3.4.5整体代码如下: 点击按钮之后从摄像头服务中获取到了摄像头的数量。 点击确定之后从摄像头服务中获取到了3号摄像头的安装时间。 3.6属于12/23/13:33新增,为了答复1楼网友 编程老油条所问问题。 把serviceProvider设计成全局静态的,可设计成单例模式或直接放在Main的属性中,供全局任意子窗体访问获取DI实例即可,当然同时,其他窗体也需要注入到容器中。 注入生命周期为瞬时的Form1类型。 因为Form1是MainForm的子窗体,而MainForm设置成了单例模式,所以在MainForm中打开Form1是属于同一次请求,姑不能用AddSingleton跟AddScope模式。如果使用以上两种模式,会报如下异常: 第一次调用正常, 关闭Form1第二次点击MainForm的button1时,报如下异常: 因为是单例模式,我们关闭了Form1,MainForm还在,再次点击button1,会找不到生命周期是单例模式的Form1实例,就会报如上异常。 问题得到完美解决,无论关闭多少次Form1,都能通过MainForm的button1调用打开Form1。 修改serviceProvider为Program静态类的公用属性(全局),以给子窗体或其他winform中的组件来容器服务者获取DI实例 到Program服务管理者属性手中拿到对应所需类型的设置好生命周期的Form1实例。显示Form1。 Form1调用camera服务 本来就想写篇短文,谁知道洋洋洒洒还写得有点长。本文如果大家读了有疑惑,请提出来,我会耐心解答;如果知识点上有不妥当不正确或者不同见解的地方,也恳请指出,我同时也很渴望进步。最后祝大家冬至安康,阖家幸福。 在net Core3.1上基于winform实现依赖注入实例 标签:overflow ack 工厂 throw ima inject dapper 其它 esc 原文地址:https://www.cnblogs.com/mtxcat/p/12782014.html在net Core3.1上基于winform实现依赖注入实例
1.背景
备注:本文的依赖注入讲解基于微软原生自带的DI,通过Ninject或者AutoFac可自行仿照操作,原理相通。2.依赖注入
2.1依赖注入是什么?
2.1依赖注入的目的
2.2依赖注入带来的好处
2.2.1生命周期的控制
每次注入时,都重新 new 一个新的实例。
每个 Request 都重新 new 一个新的实例,同一个 Request 不管经过多少个 Pipeline 都是用同一个实例。
被实例化后就不会消失,程序运行期间只会有一个实例。2.2.1.1 生命周期测试举例
public interface ISample
{
int Id { get; }
}
public interface ISampleTransient : ISample
{
}
public interface ISampleScoped : ISample
{
}
public interface ISampleSingleton : ISample
{
}
public class Sample : ISampleTransient, ISampleScoped, ISampleSingleton
{
private static int _counter;
private int _id;
public Sample()
{
_id = ++_counter;
}
public int Id => _id;
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient
public class HomeController : Controller
{
private readonly ISample _transient;
private readonly ISample _scoped;
private readonly ISample _singleton;
public HomeController(
ISampleTransient transient,
ISampleScoped scoped,
ISampleSingleton singleton)
{
_transient = transient;
_scoped = scoped;
_singleton = singleton;
}
public IActionResult Index() {
ViewBag.TransientId = _transient.Id;
ViewBag.TransientHashCode = _transient.GetHashCode();
ViewBag.ScopedId = _scoped.Id;
ViewBag.ScopedHashCode = _scoped.GetHashCode();
ViewBag.SingletonId = _singleton.Id;
ViewBag.SingletonHashCode = _singleton.GetHashCode();
return View();
}
}
Cotroller
Lifetimes
Id
Hash Code
Transient
@ViewBag.TransientId
@ViewBag.TransientHashCode
Scoped
@ViewBag.ScopedId
@ViewBag.ScopedHashCode
Singleton
@ViewBag.SingletonId
@ViewBag.SingletonHashCode
2.2.2 实现了展现层(调用者)与服务类之间的解耦
这符合了6大程序设计原则中的依赖倒置原则:
1.高层模块不应该依赖于低层模块,两者都应该依赖其抽象
展现层Controller没有依赖Model层Sample类,两者都依赖了Sample的接口抽象ISample,ISampleTransient,ISampleScoped,ISampleSingleton.
2.抽象不应该依赖于细节
接口层只定义规范,没有定义细节。public interface ISample
{
int Id { get; }
}
public interface ISampleTransient : ISample
{
}
public interface ISampleScoped : ISample
{
}
public interface ISampleSingleton : ISample
{
}
DI中取实例依赖于接口:ISampleTransient transient;
public class Sample : ISampleTransient, ISampleScoped, ISampleSingleton
{
private static int _counter;
private int _id;
public Sample()
{
_id = ++_counter;
}
public int Id => _id;
}
2.2.3 开发者不用再去考虑依赖之间的关系
2.3 依赖注入使用的设计模式
2.3.1 代理模式
2.3.2 工厂模式
3.在Net Core 3.1上基于winform实现依赖注入
3.1 Net Core 3.1中对winform的支持。
在微软官方博客中提到在VS16.5预览版中支持了winform设计器,根据博客中提到,需要在此下载链接下载VS16.5预览版。
3.2 winform依赖注入与net core MVC的不同?
static class Program
{
///
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var services = new ServiceCollection();
ConfigureServices(services);
using (ServiceProvider serviceProvider = services.BuildServiceProvider())
{
var logg = services.BuildServiceProvider().GetRequiredService
public partial class Form1 : Form
{
private readonly ILogger _logger;
private readonly IBusinessLayer _business;
public Form1(ILogger
Application.Run(new Form1(logg, businessObject));
3.3 解决3.2的思路
微软MSDN
stackoverflow
这里向大家重点推荐下stackoverflow,这个基于世界级的程序员论坛,在我遇到很多的疑难杂症,孤立无援的时候,他都会给予我解决问题的思路,方向甚至方案,再次致敬感谢stackoverflow,同时也感谢谷歌。3.4代码实现
3.4.1 在Program.cs中建立服务注册静态方法
private static void ConfigureServices(ServiceCollection services)
{
//App
services.ApplicationServiceIoC();
//Infra
//Repo
services.InfrastructureORM
分层注入:
CameraDM_Service注册在了ApplicationServiceIoC,ApplicationServiceIoC注册在了ConfigureServices。这就是我刚说的分层注入每层的依赖。 public static class ServicesIoC
{
public static void ApplicationServiceIoC(this IServiceCollection services)
{
services.AddScoped(typeof(IServiceBase), typeof(ServiceBase));
services.AddScoped
将窗体类型注入,当然后续加入其它窗体也可用同样方法进行注入。services.AddSingleton(typeof(MainForm));
3.4.2 创建服务容器对象
var services = new ServiceCollection();
3.4.3 添加服务注册
ConfigureServices(services);
3.4.4 构建ServiceProvider对象
var serviceProvider = services.BuildServiceProvider();
3.4.5 运行MainForm服务
Application.Run(serviceProvider.GetService
static class Program
{
///
3.4.6构造函数法调用DI实例
public partial class MainForm : Form
{
ICameraDM_Service _cameraDM_Service;
public MainForm(ICameraDM_Service cameraDM_Service)
{
_cameraDM_Service = cameraDM_Service;
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(_cameraDM_Service.GetAllCameraInfo().ToList().Count().ToString());
var _camera =await _cameraDM_Service.GetAllIncludingTasksAsync();
//textBox1.Text = _camera.ToList().Count().ToString();
var _cameraNo3 = await _cameraDM_Service.GetByIdAsync(3);
textBox1.Text = _cameraNo3.InstallTime.ToString();
}
}
3.5演示效果
3.6 如何调用用依赖注入在母窗体中调用子窗体
问题如下:
假设在MainForm中的button1_Click,还需要打开其他窗口,要如何实现?(只能往 MainForm 中传递serviceProvider吗?)3.6.1 注入子窗体
services.AddTransient(typeof(Form1));
private static void ConfigureServices(ServiceCollection services)
{
//App
services.ApplicationServiceIoC();
//Infra
//Repo
services.InfrastructureORM
比如设置Form1生命周期为单例模式 services.AddSingleton(typeof(MainForm));
services.AddSingleton(typeof(Form1));
修改成services.AddTransient(typeof(Form1));
3.6.2 设置全局serviceProvider容器服务管理者
public static IServiceProvider serviceProvider { get; set; }
Program.cs全部代码如下。 static class Program
{
public static IServiceProvider serviceProvider { get; set; }
///
3.6.3 MainForm中调用Form1
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
var child = (Form)Program.serviceProvider.GetService((typeof(Form1)));
child.Show();
}
}
3.6.4 构造函数法调用DI实例
此处同3.5 public partial class Form1 : Form
{
ICameraDM_Service _cameraDM_Service;
public Form1(ICameraDM_Service cameraDM_Service)
{
_cameraDM_Service = cameraDM_Service;
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(_cameraDM_Service.GetAllCameraInfo().ToList().Count().ToString());
}
}
3.6.5效果
4.最后
上一篇:C#创建日志方法
文章标题:在net Core3.1上基于winform实现依赖注入实例
文章链接:http://soscw.com/index.php/essay/61946.html