【WebForms王者归来】在 ASP.NET Core 中运行 WebForms 业务代码,99%相似度!
1. 先说结论
我们为 ASP.NET Core 带来了全新的 WebForms 开发模式,可以让 20 年前的 WebForms 业务代码在最新的 ASP.NET Core 框架中运行,代码相似度99%!
一图胜万言!
2. 为什么要升级到ASP.NET Core?
将十几年依赖于 WebForms 和 .Net Framework 的项目移植到 ASP.NET Core 将是一项艰巨的任务,特别是对于企业管理系统而言,数百个页面可不是闹着玩的。
经典WebForms已经不再更新
为什么要迁移到 ASP.NET Core?
虽然 ASP.NET Core 非常优秀,但最根本的问题却是 WebForms 已经不再更新。
随着时间的推移,WebForms 项目将面临越来越多的安全风险,因此容易受到攻击,维护成本也会越来越高,因为想找到一个熟悉过时技术的开发人员也会越来越难。及时将自己的项目升级到最新的技术是减少系统风险的不二法门。
ASP.NET Core的性能好是公认的
值得一提的是,ASP.NET Core 性能好是公认的,有报道称 Microsoft Teams 从 .NET Framework 4.6.2 迁移到 .NET Core 3.1,CPU 性能提升 25%。
另有报道,ASP.NET Core性能已经 10 倍于 Node.js,甚至比 Go, C++, Java都要快。
小结
总的来说,ASP.NET Core足够优秀来支撑这次升级:
1. ASP.NET Core开源免费(MIT),信创产品适用。
2. ASP.NET Core跨平台,Linux、Windows、Mac都可以开发和运行。
3. 可以使用最新的 C# 特性,以及最新 VS 带来的效率提升。
4. 更好的性能,意味着更快的访问速度。
5. 更好的安全性。
3. 简化开发工作,我们一直在努力!
为了减少大家从 WebForms 升级到最新的 ASP.NET Core 的工作量,我们一直在努力。
ASP.NET Core - MVC开发模式
2017-12-06,我们正式发布了支持跨平台开发和部署的FineUICore,此时只有经典的Model-View-Controller模式,并且前台页面是Razor函数的写法。如果你当时要从FineUIPro升级到FineUICore,工作量还是蛮大的,来看下直观的对比。
由于 ASP.NET Core Razor视图的写法和标签的写法完全不同,所以前台代码的相似度几乎为零!仅有部分后台业务逻辑是一样的。
ASP.NET Core - RazorPages开发模式
2019-06-20,我们推出了支持 Razor Pages 和 Tag Helpers的 FineUICore,可以方便的迁移之前的WebForms应用,这个版本尽量保证 .cshtml 视图文件和 WebForms 的 .aspx 的一致性,可以减轻升级的工作量。
我们专门写了一篇文章详细描述升级过程,可以参考:【FineUICore】全新ASP.NET Core,比WebForms还简单! - 三生石上(FineUI控件) - 博客园
ASP.NET Core - WebForms开发模式
2024年的今天,我们推出支持WebForms开发模式的 FineUICore,不仅可以做到前台页面的高度相似,而且后台业务代码也可以做到99%的相似度。
小结
十几年如一日,我们初心不变,始终恪守如下三个原则,为提升大家的开发体验而不懈努力:
1. 一切为了简单。
2. 用心实现 80% 的功能。
3. 创新所以独一无二。
4. 为什么引入 WebForms 开发模式?
自从 2019年推出支持 RazorPages 的FineUICore以来,我们不断收到用户反馈,吐槽 ASP.NET Core 的使用复杂,没有之前的 WebForms好用。
我简单总结了一下,有人吐糟传递参数麻烦,还要自己写JavaScript代码;有人吐槽后台代码的一致;还有人搞不清楚UIHelper该什么时间使用,以及创建的控件和页面上的控件实例有啥关系。
初始化数据的方式不同
在 ASP.NET Core 中,我们需要在 OnGet 函数中初始化数据,然后通过 ViewData 传入视图文件:
public void OnGet()
{
LoadData();
} private void LoadData()
{
var recordCount = DataSourceUtil.GetTotalCount();
// 1.设置总项数(特别注意:数据库分页初始化时,一定要设置总记录数RecordCount)
ViewBag.Grid1RecordCount = recordCount;
// 2.获取当前分页数据
ViewBag.Grid1DataSource = DataSourceUtil.GetPagedDataTable(pageIndex: 0, pageSize: 5);
}
而在WebForms的 Page_Load 中,我们可以直接获取表格控件进行数据绑定:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
BindGrid();
}
} private void BindGrid()
{
// 1.设置总项数(特别注意:数据库分页一定要设置总记录数RecordCount)
Grid1.RecordCount = GetTotalCount();
// 2.获取当前分页数据
DataTable table = GetPagedDataTable(Grid1.PageIndex, Grid1.PageSize);
// 3.绑定到Grid
Grid1.DataSource = table;
Grid1.DataBind();
}
向后台传递数据的方式不同
在 ASP.NET Core 中,所有后台拿到的数据都需要在视图代码中通过JavaScript的方式获取:
<f:Button ID="btnSubmit" CssClass="marginr" ValidateForms="SimpleForm1" Text="登录"
OnClick="@Url.Handler("btnSubmit_Click")"
OnClickParameter1="@(new Parameter("userName", "F.ui.tbxUserName.getValue()"))"
OnClickParameter2="@(new Parameter("password", "F.ui.tbxPassword.getValue()"))">
</f:Button>
比如这个示例向后台传递了两个参数userName和password,后台通过函数参数的方式接受:
public IActionResult OnPostBtnSubmit_Click(string userName, string password)
{
UIHelper.Label("labResult").Text("用户名:" + userName + " 密码:" + password);
return UIHelper.Result();
}
这个示例有两个难点:
- 代码抽象不好理解:通过UIHelper.Label函数拿到的控件是一个在内存中新建的实例(其目的是为了向前台输出一段改变标签控件文本的JavaScript脚本),和页面初始化时的那个Label控件没有任何关系。
- 不小心写错参数名称的话,编译不会报错,运行时不能正确获取传入的参数值。
而在WebForms中,可以直接在后台获取控件的属性,无需任何特殊处理:
<f:Button ID="btnSubmit" CssClass="marginr" runat="server" OnClick="btnSubmit_Click" ValidateForms="SimpleForm1" Text="登录">
</f:Button>
后台直接通过控件实例的属性获取,可以直接通过智能提示快速输入属性名称,而且有编译时提示:
protected void btnSubmit_Click(object sender, EventArgs e)
{
labResult.Text = "用户名:" + tbxUserName.Text + " 密码:" + tbxPassword.Text;
}
回发时数据处理方式不同
在 ASP.NET Core 中,后台更新表格数据需要一套单独的代码(因为页面初始化时使用ViewData进行数据传递,所以无法和回发时的数据绑定共用一套代码):
public IActionResult OnPostGrid1_PageIndexChanged(string[] Grid1_fields, int Grid1_pageIndex)
{
var grid1 = UIHelper.Grid("Grid1");
var recordCount = DataSourceUtil.GetTotalCount();
// 1.设置总项数(数据库分页回发时,如果总记录数不变,可以不设置RecordCount)
grid1.RecordCount(recordCount);
// 2.获取当前分页数据
var dataSource = DataSourceUtil.GetPagedDataTable(pageIndex: Grid1_pageIndex, pageSize: 5);
grid1.DataSource(dataSource, Grid1_fields);
return UIHelper.Result();
}
而在WebForms中,页面回发时重新绑定表格数据和页面初始化时共用一套代码:
protected void Grid1_PageIndexChange(object sender, GridPageEventArgs e)
{
BindGrid();
}
小结
经过前面的对比,我们能明显感觉到WebForms的代码更加直观,更加容易理解,并且WebForms的代码量更少,易于维护。
5. 全新WebForms开发模式(全球首创)
全球首创,实至名归
为了解决上述问题,让开发人员在享受 ASP.NET Core 免费开源跨平台速度快的优点同时,还能拥有WebForms比较高的开发效率,我们为 ASP.NET Core 引入了 WebForms 模式。
截止目前,能真正将 WebForms 引入 ASP.NET Core 的控件库厂商仅此一家,别无分店。我们也诚挚的邀请你来试用,相信你一定会喜欢这个全球首创的创新功能。
视图文件+页面模型文件+自动生成的设计时文件
首先从一个最简单的页面入手,我们来看下启用WebForms的 ASP.NET Core 到底是个什么样子?
一个简单的模拟登录页面,用户输入指定的用户名和密码之后,弹出登录成功提示框。
在 ASP.NET Core RazorPages项目中,我们需要新建一个页面文件以及后台代码文件(或者称之为页面模型):
注意,在登录按钮的点击事件中,可以直接读取输入框的 tbxUserName 的 Text 属性,这个就是 FineUICore 黑魔法,我们会将控件的一些关键属性回发到后台,并自动绑定到相应的控件实例。
而这个控件实例(tbxUserName)是在一个名为 Login.cshtml.designer.cs 文件中声明的,FineUICore会在页面回发时自动初始化这个实例,并绑定关键属性值。
注:我们会提供一个Visual Studio插件自动生成这个文件,无需开发人员手工编写。
小结
如果上述代码让你想起了20年前的WebForms,那就对了。业务代码99%的相似度是实打实的,这也就为经典WebForms的项目迁移到最新的ASP.NET Core奠定了扎实的基础。
让我们用工具对比下实现相同功能的经典WebForms和FineUICore(开启WebForms模式)代码。
6. 哪些所谓的WebForms缺点怎么办?
WebForms的缺点已经不复存在!
20年前大家所诟病的WebForms的缺点之一(网络传输量大)已经不复存在,而WebForms的快速开发特性(Rapid Application Development - RAD)却越来越重要。
有报告显示,今天的主流网站的网页过于臃肿,以至于严重影响浏览性能,而能流畅玩手游《绝地求生》的入门级移动设备甚至难以正常加载。Wix 每个网页需要加载 21MB,Patreon 和 Threads 每个网页需要加载 13MB 的数据。臃肿的网页导致加载时间长达 33 秒,部分情况下甚至无法加载。基本上主流社交平台都存在臃肿的问题。而内容创建平台 Squarespace 和论坛 Discourse 的新版本通常比旧版本性能更差。
How web bloat impacts users with slow devices
WebForms需要在客户端和服务端保持控件状态,所以在页面回发时,需要将页面上所有控件的状态信息一并回发,导致比较大的网络传输。20年后的今天,随着4G、5G移动网络的普及,以及充足的宽带网络,这些流量已经变得不值一提。
WebForms是划时代的技术,也可以看做是微软的低代码解决方案,只不过20年前出来太超前了,受制于网络传输带宽的限制,所以才为大家所诟病。现在回头看看,每次页面回发时多传输10K数据算个事吗?想想你刷一个抖音视频怎么说也要消耗10M(10,240K)流量吧。而WebForms带来的开发效率提升,以及后期节约的维护成本,则是实实在在的好处,真金白金看得见摸得着。
实测WebForms的数据传输量
我猜测大家估计还是心有不甘,虽然多点数据传输能提高开发效率,减少我们写的代码量,提高可维护性。但是成年人的世界既要、又要还要,能少传输点数据岂不是更妙。
带着这个疑问,我们来对比下FineUICore(RazorPages)、FineUIPro(经典WebForms)和FineUICore(WebForms开发模式)下传输的数据量,争取让大家用的心情舒畅。
示例一:表格的数据库分页与排序
示例二:省市县联动
示例三:树控件延迟加载
注:上述表格中数字表示网络数据传输量,单位KB。
经过上述三个页面对比,我们可以看出,经典WebForms不管是页面第一次加载,还是回发时上传和下载的数据量都是最大的。
小结
1. 相比经典WebForms,不管是页面第一次加载,还是回发时的数据传输量,ASP.NET Core(WebForms开发模式)都是碾压级的,综合数据下载量比经典WebForms减少 50% 左右。
2. 与数据传输量最少的ASP.NET Core RazorPages相比,启用WebForms时,只有在页面回发时上传数据量有所增加,而页面第一次加载和回发时的下载数据量两者保持一致。
3. 不管哪种技术,上述三个示例的数据传输都是10KB之内,相比现在动辄10MB(大了1000倍!)的数据传输,你觉得WebForms数据传输量大的缺点还存在吗?
7. 如何开启WebForms开发模式?
首先确保你使用的是ASP.NET Core RazorPages 开发模式,只需要如下两个步骤即可在FineUICore项目中轻松开启 WebForms 模式。
第一步:修改appsettings.json配置文件
{
"FineUI": {
"EnableWebForms": true,
"DebugMode": true,
"Theme": "Pure_Black",
"EnableAnimation": true,
"MobileAdaption": true
}
}
第二步:修改 Startup.cs启动文件
在 ConfigureServices 函数中,增加 WebForms过滤器,如下所示。
// FineUI 服务
services.AddFineUI(Configuration); services.AddRazorPages().AddMvcOptions(options =>
{
// 自定义JSON模型绑定(添加到最开始的位置)
options.ModelBinderProviders.Insert(0, new FineUICore.JsonModelBinderProvider()); // 自定义WebForms过滤器(仅在启用EnableWebForms时有效)
options.Filters.Insert(0, new FineUICore.WebFormsFilter()); }).AddNewtonsoftJson().AddRazorRuntimeCompilation();
搞定!
小结
深度集成到FineUICore中,仅仅通过一个参数来控制是否开启WebForms,可以对比学习RazorPages和WebForms,降低了学习成本,同时也让之前购买FineUICore企业版的客户享受到WebForms带来的便利。
8. Page_Load事件的回归
在经典WebForms页面中,Page_Load事件非常重要,也是大家耳熟能详的,甚至在20年前ASP.NET 1.0 发布的时候,我们就是这么写代码的。
Page_Load事件往往伴随着对IsPostBack属性的判断,因为Page_Load事件不管是页面第一次加载,还是页面回发都会执行。因此对于哪些只需要在页面第一次加载的代码,就需要放到 !IsPostBack的逻辑判断中。
RazorPages中的复选框列表的初始化
示例:https://pages.fineui.com/#/Form/CheckBoxList
在ASP.NET Core RazorPages开发模式下,我们需要在OnGet中初始化数据,由于此时页面视图尚未初始化,因此我们无法知道页面视图上的任何定义。
public void OnGet()
{
LoadData();
} private void LoadData()
{
List<TestClass> myList = new List<TestClass>();
myList.Add(new TestClass("1", "数据绑定值 1"));
myList.Add(new TestClass("2", "数据绑定值 2"));
myList.Add(new TestClass("3", "数据绑定值 3"));
myList.Add(new TestClass("4", "数据绑定值 4")); ViewBag.CheckBoxList2DataSource = myList;
ViewBag.CheckBoxList2SelectedValueArray = new string[] { "1", "3" };
}
将准备好的数据保存在ViewData(自定义的ViewBag)中,然后传入视图文件,并在页面视图标签中使用这些数据。
<f:CheckBoxList ID="CheckBoxList2" Label="列表二(一列)" ColumnNumber="1"
DataTextField="Name" DataValueField="Id"
DataSource="@ViewBag.CheckBoxList2DataSource"
SelectedValueArray="@ViewBag.CheckBoxList2SelectedValueArray">
</f:CheckBoxList>
WebForms复选框列表的初始化
示例:https://forms.fineui.com/#/Form/CheckBoxList
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
LoadData();
}
} private void LoadData()
{
List<TestClass> myList = new List<TestClass>();
myList.Add(new TestClass("1", "数据绑定值 1"));
myList.Add(new TestClass("2", "数据绑定值 2"));
myList.Add(new TestClass("3", "数据绑定值 3"));
myList.Add(new TestClass("4", "数据绑定值 4")); CheckBoxList2.DataSource = myList;
CheckBoxList2.DataBind(); CheckBoxList2.SelectedValueArray = new string[] { "1", "3" };
}
其中,IsPostBack属性定义在页面模型基类BaseModel.cs中:
public bool IsPostBack
{
get
{
return FineUICore.PageContext.IsFineUIAjaxPostBack();
}
}
注意:在Page_Load事件中,页面视图已经初始化完毕,因此我们可以直接调用页面视图上的控件实例,比如这里的CheckBoxList2,对应于页面上的CheckBoxList标签定义。
<f:CheckBoxList ID="CheckBoxList2" Label="列表二(一列)" ColumnNumber="1"
DataTextField="Name" DataValueField="Id">
</f:CheckBoxList>
小结
从上面示例中可以看出,WebForms模式下的页面初始化更加直观,等视图文件初始化完毕后,直接获取控件实例,并设置控件属性。反过来看RazorPages的实现就有点繁琐了,必须通过ViewData进行中转,先赋值,再使用,在页面模型OnGet函数中无法获取视图中定义的变量。
9. 页面回发事件(PostBack)
简化页面回发事件的函数名
首先看下RazorPages中的按钮点击事件,:
<f:Button ID="btnChangeEnable" Text="启用后面的按钮"
OnClick="@Url.Handler("btnChangeEnable_Click")" />
<f:Button ID="btnEnable" Text="禁用的按钮" OnClick="@Url.Handler("btnEnable_Click")"
Enabled="false" />
对应的后台事件处理器:
public IActionResult OnPostBtnChangeEnable_Click()
{
var btnEnable = UIHelper.Button("btnEnable");
btnEnable.Enabled(true);
btnEnable.Text("本按钮已经启用(点击弹出对话框)");
return UIHelper.Result();
}
在视图文件中,定义了按钮的点击事件名为btnChangeEnable_Click,而后台对应的事件处理器名称为OnPostBtnChangeEnable_Click。由于前后台事件名称的不一致,导致很多开发人员将后台事件名称误写为OnPostbtnChangeEnable_Click,导致无法进入事件处理函数。
而WebForms开发模式下,再看下相同的示例:
<f:Button ID="btnChangeEnable" Text="启用后面的按钮"
OnClick="btnChangeEnable_Click" />
<f:Button ID="btnEnable" Text="禁用的按钮" OnClick="btnEnable_Click"
Enabled="false" />
对应的后台处理函数名称和前台的定义一模一样:
protected void btnChangeEnable_Click(object sender, EventArgs e)
{
btnEnable.Enabled = true;
btnEnable.Text = "本按钮已经启用(点击弹出对话框)";
}
除了事件名称保持前后台一致,代码逻辑中已经完全移除UIHelper的调用,我们可以直接调用控件实例,修改实例属性(并非所有属性都可以在页面回发中改变,我们将这些能够在回发中改变的属性为AJAX属性,这个概念和经典FineUIPro保持一致)。
.......
小结
下面简单总结一下WebForms模式下回发事件和RazorPages中的不同之处:
- 视图代码中无需将事件名称置于Url.Handler()函数中。
- 视图代码中无需编写JavaScript代码来获取控件状态。
- 视图中也无需设置OnClickFields来向后台传递控件状态。
- 后台事件名称和前台视图定义的事件名称完全一致。
- 事件处理函数的返回值是void,因此无需返回UIHelper.Result()。
- 事件处理函数参数和经典的WebForms保持一致,第一个参数是触发事件的控件实例,第二个是事件参数(比如表格分页的事件参数类型为GridPageEventArgs)。
- 事件处理函数中完全移除对UIHelper的依赖(之前需要重建控件实例,比如UIHelper.Button("btnEnable"))。
如果你是从经典的 ASP.NET WebForms直接学习的FineUICore(WebForms开发模式),忘记上面所有的不同,你只需要记着一点:FineUICore(WebForms模式)的事件处理和经典WebForms的事件处理一模一样!
- 产品名称:FineUICore(WebForms开发模式)
- 单位全称:XXX单位
- 申请人邮箱:XXX
- 申请人QQ:XXX
- 申请人姓名:XXX
- 申请人地址:XX省XX市
【WebForms王者归来】在 ASP.NET Core 中运行 WebForms 业务代码,99%相似度!的更多相关文章
- 项目开发中的一些注意事项以及技巧总结 基于Repository模式设计项目架构—你可以参考的项目架构设计 Asp.Net Core中使用RSA加密 EF Core中的多对多映射如何实现? asp.net core下的如何给网站做安全设置 获取服务端https证书 Js异常捕获
项目开发中的一些注意事项以及技巧总结 1.jquery采用ajax向后端请求时,MVC框架并不能返回View的数据,也就是一般我们使用View().PartialView()等,只能返回json以 ...
- ASP.NET Core中的依赖注入(2):依赖注入(DI)
IoC主要体现了这样一种设计思想:通过将一组通用流程的控制从应用转移到框架之中以实现对流程的复用,同时采用"好莱坞原则"是应用程序以被动的方式实现对流程的定制.我们可以采用若干设计 ...
- ASP.NET Core中的依赖注入(3): 服务的注册与提供
在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...
- ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理
ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...
- ASP.NET Core 中文文档 第二章 指南(4.6)Controller 方法与视图
原文:Controller methods and views 作者:Rick Anderson 翻译:谢炀(Kiler) 校对:孟帅洋(书缘) .张仁建(第二年.夏) .许登洋(Seay) .姚阿勇 ...
- ASP.NET Core 中文文档 第三章 原理(6)全球化与本地化
原文:Globalization and localization 作者:Rick Anderson.Damien Bowden.Bart Calixto.Nadeem Afana 翻译:谢炀(Kil ...
- ASP.NET Core 中文文档 第三章 原理(13)管理应用程序状态
原文:Managing Application State 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:高嵩 在 ASP.NET Core 中,有多种途径可以对应用程序的状态进行 ...
- ASP.NET Core 中文文档 第三章 原理(16).NET开放Web接口(OWIN)
原文:Open Web Interface for .NET (OWIN) 作者:Steve Smith. Rick Anderson 翻译:谢炀(kiler398) 校对:孟帅洋(书缘) ASP.N ...
- 在ASP.NET Core中使用Angular2,以及与Angular2的Token base身份认证
注:下载本文提到的完整代码示例请访问:How to authorization Angular 2 app with asp.net core web api 在ASP.NET Core中使用Angu ...
- 如何在ASP.NET Core中实现CORS跨域
注:下载本文的完整代码示例请访问 > How to enable CORS(Cross-origin resource sharing) in ASP.NET Core 如何在ASP.NET C ...
随机推荐
- 看你能解锁哪些新身份?OpenHarmony大使、MVP、金码达人在线申报
- OpenHarmony定义组件重用样式:@Styles装饰器
如果每个组件的样式都需要单独设置,在开发过程中会出现大量代码在进行重复样式设置,虽然可以复制粘贴,但为了代码简洁性和后续方便维护,我们推出了可以提炼公共样式进行复用的装饰器@Styles. @St ...
- SQLite主键自增代码
引用:https://blog.csdn.net/maowendi/article/details/81115401 insert into TubeRunInfo (UserName) values ...
- CondeseNetV2:清华与华为出品,保持特征的新鲜是特征复用的关键 | CVPR 2021
论文提出SFR模块,直接重新激活一组浅层特征来提升其在后续层的复用效率,而且整个重激活模式可端到端学习.由于重激活的稀疏性,额外引入的计算量非常小.从实验结果来看,基于SFR模块提出的CondeseN ...
- Qt调用摄像头一,基础版
本示例,为纯Qt调用摄像头,功能比较简单,打开摄像头,设置参数,拍照 涉及到的功能有: 获取摄像头列表 获取摄像头分辨率 获取摄像头帧率 获取摄像头支持的视频模式 设置摄像头参数 拍照 此版本的缺点是 ...
- Linux之隔离技术
前言 Linux的内核有两大特性Namespace和CGroup,这两种特性可以在Linux主机上实现主机名.用户.网络等全局资源的隔离,也是实现网络虚拟化.容器技术的基础. 命名空间 Linux N ...
- 手把手教你基于gin从零搭建一个属于你自己的go项目(一)
一.为什么写这个,适合什么人看 原因 因为自己想写点小玩意,本来是打算用egg.js来写服务端的,后来发现了个更好玩的midway,但是后来发现自己手上的服务器都是一核2g的小水管,用node有点难顶 ...
- ABP -Vnext框架一步一步入门落地教程——ABP Vnext框架代码安装和启动(一)
兄弟们,人生需要指引,而复制是成功最快的方式,让我们开始行动吧 --codesoft 教程介绍 ABP-Vnext框架我们之前摸了无数次,好象初恋的女孩,一直在靠近,一直在努力,一直不敢盯着她的眼睛说 ...
- 牛客网-SQL专项训练13
①某软件公司正在升级一套水务管理系统.该系统用于县市级供排水企业.供水厂.排水厂中水务数据的管理工作.系统经重新整合后,开发人员决定不再使用一张备份数据表waterinfo001表,需永久删除.选出符 ...
- 阿里云 ACK 容器服务生产级可观测体系建设实践
简介: 随着容器被越来越对企业接纳与落地,可观测成为重点.那么,让我们深入了解阿里云 ACK 容器服务生产级可观测体系建设实践,为自身业务可观测提供参考- 作者:冯诗淳(行疾) ACK 可观测体系 ...