网站迁移纪实:从Web Form 到 Asp.Net Core (Abp vNext 自定义开发)
问题和需求
从2004年上线,ZLDNN.COM运行已经超过16年了,一直使用DotNetNuke平台(现在叫DNN Platform),从最初的DotNetNuke 2.1到现在使用的7.4。先是在亦庄的独立服务器托管,后来迁到美国的PowerDNN的云服务器ECS,再后来迁移到阿里云的ECS,采用Windows 2008系统,运行几年以后,C盘已经满了,可又没有办法清理,网站速度越来越慢,干脆长痛不如短痛,彻底更新一下。
DotNetNuke严重依赖Web Form技术,其开发团队到现在也没有找到从.Net Framework迁移到.Net Core以及最新的.Net 5或.Net 6的合适技术路线,导致其只能在Windows下运行。另外一个问题是DotNetNuke只能使用SqlServer数据库,无法使用更便宜更灵活的数据库。网站的迁移实际上是在满足现有基本功能的前提下,采用新的技术重新开发。在选择新的技术之前,首先需要梳理一下网站的功能,确定哪些需要保留,哪些可以通过其它方式替代,哪些可以暂时不实现。网站需要保留的部分包括:
- 数据:包括用户数据、订单数据、技术支持数据等。
- 网站页面的Url: 大部分Url已经是SEO友好,但有些页面仍然采用web form模式,比如GetLicense.aspx,这部分Url也需要保留。
- 关键功能:诸如订单接收、产品激活等。
其它的需求还有,希望网站可以运行在维护成本比较低的轻量级应用服务器中,可以采用MySql等开源数据库,视觉效果上尽量与原有系统相同等等。还要留有用户管理已经用户注册的接口。
在大的技术路线上,仍然采用.Net体系,研究了几种技术,包括Qutane、Orchard等,最后决定采用Abp vNext进行开发。
开发
将开发中遇到的具体问题和最终的解决方案总结一下。
外观
采用ABP自带的Theme,根据现有网站的风格进行修改,保持风格大体一致。修改的办法是从Abp源码中复制Theme相关文件到自定义项目对应的目录中,直接修改就可以了。需要修改的文件如下图:

网站的颜色等需要修改文件global-styles.css:

首页
DotNetNuke完全采用动态页面,原来的首页采用的是HTML模块加上DNNArticle的子模块,考虑到首页的更新频率不高,这次采用静态页面。
维护页面
这部分主要是产品、版本、订单等等的维护,属于标准的CRUD界面。这部分采用ABP的标准化设计,首先设计各个实体,然后使用AbpHelper.Gui和AbpHelper.CLI生成相关代码和界面。所生成的界面基本可以使用,需要改造的地方是增加查询功能和调整权限。这里简单介绍一下如何增加查询功能。
ABP MVC/Razor Page 模板生成的基于DataTables.Net的页面支持分页和排序,但缺省情况下不支持查询,需要根据实际情况自行添加。我们可以利用DataTables.Net自带的查询功能实现查询。
首先修改Application项目,增加带有查询的Application服务。先在PagedAndSortedResultRequestDto基础上定义带有关键字的Dto:
public class OrderNotificationSearchDto: PagedAndSortedResultRequestDto
{
public string Key { get; set; }
}
然后在Application 服务中增加查询服务:
public async Task<PagedResultDto<OrderNotificationDto>> GetSearchListAsync(OrderNotificationSearchDto input)
{
var query = await CreateFilteredQueryAsync(input);
if (!string.IsNullOrEmpty(input.Key))
{
query = query.Where(o => o.InvoiceID.Contains(input.Key)
|| o.OptionName.Contains(input.Key)
|| o.PackageName.Contains(input.Key)
|| o.BillToEmail.Contains(input.Key));
}
var totalCount = await AsyncExecuter.CountAsync(query);
query = ApplySorting(query, input);
query = ApplyPaging(query, input);
var entities = await AsyncExecuter.ToListAsync(query);
var entityDtos = await MapToGetListOutputDtosAsync(entities);
return new PagedResultDto<OrderNotificationDto>(
totalCount,
entityDtos
);
}
然后需要改造客户端,首先将index.js中datatables的设置searching改为true:
var dataTable = $('#OrderNotificationTable').DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
serverSide: true,
paging: true,
searching: true,
接下来修改ajax的定义:
//ajax: abp.libs.datatables.createAjax(service.getList),
ajax: abp.libs.datatables.createAjax(service.getSearchList, inputAction, responseCallback),
将getList修改为新的getSearchList,增加新的传入参数和Callback。这两个函数定义如下:
var inputAction = function (requestData, dataTableSettings) {
var ctl = $("#OrderNotificationTable_filter input").val();
return {
key: ctl,
};
};
var responseCallback = function (result) {
// your custom code.
return {
recordsTotal: result.totalCount,
recordsFiltered: result.totalCount,
data: result.items
};
};
网站数据的导入
这部分主要包括产品数据和与激活相关的数据,采用自己写的一个面向.Net Core的ADO库,将原有的数据导出到Xml中,在新的应用中从Xml中读取数据进行初始化。
产品内容页
产品的内容原来保持在数据库中,现在改为在文件中保存,加载产品页时,从文件读出展示。
外部数据交换
主要包括接收从DNNStore发来的数据和产品激活两部分。采用Application Service实现,通过Apb框架的动态Web Api可以访问。
Url重定位
包括与外界交换数据的Url和为了保证SEO一致的界面Url。采用.Net Core的ReWrite中间件实现,感觉这部分真的很好用。代码如下:
var options = new RewriteOptions()
.AddRewrite(@"^GetLicense.aspx", "Products/Services/ManualActivate",true)
.AddRewrite(@"^Products/currentpage/(\d+)", "Products?currentpage=$1", true)
.AddRewrite(@"^ProductDetail/(.+)", "Products/ViewDetail?name=$1", true)
.AddRewrite(@"^LicenseCode.aspx", "api/app/activate/req-license-code", true)
.AddRewrite(@"^desktopmodules/OrderNotification/OrderNotify.aspx", "api/app/activate/save-order", true)
app.UseRewriter(options);
还有其它一些细节包括关闭多租户、关闭用户注册、国际化修改等等。
从十一前开始,断断续续开发了两到三周的时间。
部署和运行
以前一直在Windows生态中,部署应用似乎不是大问题,只有在IIS上创建网站或者应用就可以了。现在希望将ZLDNN.COM迁移到阿里云的轻量级服务器,在Linux系统下部署,还是遇到一些挑战。
首先解决.Net Core应用在Linux上运行的问题。由于在生产环境中只运行一个.Net Core应用,所以在生成部署文件时采用独立运行模式,这样就不需要在生产环境中安装.Net框架。这一步没有遇到大问题,测试应用能够运行。
然后是ABP应用在生产环境上运行,这里遇到一个问题,Couldn’t find a valid ICU package installed on the system.这个问题在本地测试没有遇到,查了一下是没有安装ICU库,安装完成后问题解决了。
数据库的配置没有遇到太大麻烦,但在后期运行时出现了MySql异常退出的问题,发现是内存问题,创建内存交换文件后解决了。
Apache服务器配置花了一些时间,因为两个域名驻留在同一个服务器上,需要将Apache服务器配置为反向代理服务器,由于对Apache服务器不熟悉,折腾了一些时间,不过最后也成功了。
到现在新网站运行了两个多月,基本没有遇到太大的问题。效果还是不错的,速度提升很多。
网站迁移纪实:从Web Form 到 Asp.Net Core (Abp vNext 自定义开发)的更多相关文章
- ASP.NET Web Form 与 ASP.NET MVC 区别
Asp.net 微软提供web开发框架或者技术.分Web Form和ASP.NET MVC.下面简单说明各自优缺点及使用场景. Web Form ASP.NET Webform提供了一个类似于winf ...
- 002.Create a web API with ASP.NET Core MVC and Visual Studio for Windows -- 【在windows上用vs与asp.net core mvc 创建一个 web api 程序】
Create a web API with ASP.NET Core MVC and Visual Studio for Windows 在windows上用vs与asp.net core mvc 创 ...
- 004.Create a web app with ASP.NET Core MVC using Visual Studio on Windows --【在 windows上用VS创建mvc web app】
Create a web app with ASP.NET Core MVC using Visual Studio on Windows 在 windows上用VS创建mvc web app 201 ...
- Using MongoDB with Web API and ASP.NET Core
MongoDB is a NoSQL document-oriented database that allows you to define JSON based documents which a ...
- 基于ASP.NET core的MVC站点开发笔记 0x01
基于ASP.NET core的MVC站点开发笔记 0x01 我的环境 OS type:mac Software:vscode Dotnet core version:2.0/3.1 dotnet sd ...
- 零基础ASP.NET Core MVC插件式开发
零基础ASP.NET Core MVC插件式开发 一个项目随着业务模块的不断增加,系统会越来越庞大.如果参与开发的人员越多,管理起来也难度也很大.面对这样的情况,首先想到的是模块化插件式开发,根据业务 ...
- asp.net core 3.1 自定义中间件实现jwt token认证
asp.net core 3.1 自定义中间件实现jwt token认证 话不多讲,也不知道咋讲!直接上代码 认证信息承载对象[user] /// <summary> /// 认证用户信息 ...
- asp.net core 实现支持自定义 Content-Type
asp.net core 实现支持自定义 Content-Type Intro 我们最近有一个原本是内网的服务要上公网,在公网上有一层 Cloudflare 作为网站的公网流量提供者,CloudFla ...
- ASP.NET Core中显示自定义错误页面-增强版
之前的博文 ASP.NET Core中显示自定义错误页面 中的方法是在项目中硬编码实现的,当有多个项目时,就会造成不同项目之间的重复代码,不可取. 在这篇博文中改用middleware实现,并且放在独 ...
随机推荐
- 强化学习实战 | 表格型Q-Learning玩井字棋(二)
在 强化学习实战 | 表格型Q-Learning玩井字棋(一)中,我们构建了以Game() 和 Agent() 类为基础的框架,本篇我们要让agent不断对弈,维护Q表格,提升棋力.那么我们先来盘算一 ...
- Shell学习(九)——chattr与lsattr命令详解
有时候你发现用root权限都不能修改某个文件,大部分原因是曾经用chattr命令锁定该文件了.chattr命令的作用很大,其中一些功能是由Linux内核版本来支持的,不过现在生产绝大部分跑的linux ...
- jvm的优化
a) 设置参数,设置jvm的最大内存数 b) 垃圾回收器的选择
- 利用Lombok编写优雅的spring依赖注入代码,去掉繁人的@Autowired
大家平时使用spring依赖注入,都是怎么写的? @Servicepublic class OrderService {@Autowiredprivate UserService userServic ...
- Bash shell(六)-管道命令
就如同前面所说的, bash 命令执行的时候有输出的数据会出现! 那么如果这群数据必需要经过几道手续之后才能得到我们所想要的格式,应该如何来设定? 这就牵涉到管线命令的问题了 (pipe) ,管线命令 ...
- 【C/C++】最大连续子序列和/动态规划
思路主要是看了晴神的算法笔记,实现是自己重新用vector实现了一下,对付逗号隔开的输入 #include <iostream> #include <string> #incl ...
- 【C/C++】算法入门:排序/算法笔记
(设排序从小到大) 冒泡排序 这个大家都会,从第一个开始往后俩俩交换,然后第二个,最后到最后一个,复杂度n^2 选择排序 思路和冒泡差不多,比如要得到从小到大的排序,就是从第一个开始,i取1~n,每次 ...
- 论文翻译:2021_A Perceptually Motivated Approach for Low-complexity, Real-time Enhancement of Fullband Speech
论文地址:一种低复杂度实时增强全频带语音的感知激励方法 论文代码 引用格式:A Perceptually Motivated Approach for Low-complexity, Real-tim ...
- Python利用ctypes实现C库函数调用
0X00 ctypes 是强大的,使用它我们就能够调 用动态链接库中函数,同时创建各种复杂的 C 数据类型和底层操作函数.使得python也具备了底层内存操作的能力,再配合python本身强大的表达能 ...
- SpringBoot Redis 发布订阅模式 Pub/Sub
SpringBoot Redis 发布订阅模式 Pub/Sub 注意:redis的发布订阅模式不可以将消息进行持久化,订阅者发生网络断开.宕机等可能导致错过消息. Redis命令行下使用发布订阅 pu ...