DotNetCore实现后台脚本化

本文详细介绍了将 DotNetFramework 项目迁移到 DotNetCore 的过程中遇到的挑战,特别是如何在 DotNetCore 中实现类似 ashx 的后台脚本化功能。通过抽象接口、自动编译和反射等技术,成功解决了编译性质代码与频繁修改需求之间的矛盾,实现了动态编译和执行。文章强调了不断探索和积累基础知识的重要性。
摘要由CSDN通过智能技术生成

从DotNetFrameWork迁移到DotNetCore经历了各种坎坷。不是DotNetCore有多难。而且迁移既要保证继承老的代码遗产,又要保证新DotNetCore业务代码向老系统兼容(业务和UI代码层兼容性)。各种完成之后就剩下迁移前就是心病的一个地方。老的Framework的后台ashx是脚本性质的,不需要编译直接改代码就好使。新的DotNetCore是编译性质的,代码都编译dll了给asp.netcore站点使用。但是我们的业务不允许编译性质存在啊,一个项目6-7年不验收都是有的。而且频繁改业务代码,每改个后台都要编译那可了的,基本难以维护。

不过船到桥头自然直,没开始迁移之前发现的问题随着对DotNetCore的理解深入也浮现了解决方案。经此折腾后,借用一句话,一顶王冠落地,检验要变天了。

那么怎么实现DotNetCore后台脚本化呢?
1.抽象和ashx一样的IBaseHttpHandler接口,替代老的HttpHandler
2.所有后台服务类继承该接口,后面用程序把每个类代码和工程文件组装,调用dotnet build命令编译dll
3.通过反射为每个请求路径从编译生成dll目录查询有没有dll,没有就先组装编译。有了就反射得到接口实现类,然后执行接口方法。

抽象接口

using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace XXXLIS
{
    /// <summary>
    /// http执行请求接口
    /// </summary>
    public interface IBaseHttpHandler
    {
        /// <summary>
        /// 执行请求
        /// </summary>
        /// <param name="context"></param>
        public void ProcessRequest(HttpContext context);
    }
}

自动编译封装

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;
using System.Text;

namespace XXXLIS
{
    /// <summary>
    /// 实现ashx转cs后自动编译
    /// </summary>
    public static class AutoBuild
    {
        /// <summary>
        /// 编译
        /// </summary>
        /// <param name="codepath">类代码路径</param>
        /// <param name="proname">工程名称,不带后缀的</param>
        /// <param name="standprofilepath">标准工程文件全路径</param>
        /// <returns>返回空成功,非空就是失败原因</returns>
        public static string Bulid(string codepath,string proname,string standprofilepath)
        {
            try
            {
                //编译临时总目录
                string buildPath = Path.Combine(AppContext.BaseDirectory, "AutoBuildTmp");
                if (!Directory.Exists(buildPath))
                {
                    Directory.CreateDirectory(buildPath);
                }
                //构建项目编译文件夹
                string proBuildPath = Path.Combine(buildPath, proname);
                if (!Directory.Exists(proBuildPath))
                {
                    Directory.CreateDirectory(proBuildPath);
                }
                //编译目录的项目文件全路径
                string proFullName = Path.Combine(proBuildPath, proname + ".csproj");
                //拷贝标准项目文件
                File.Copy(standprofilepath, proFullName, true);
                FileInfo fi = new FileInfo(codepath);
                //编译目录类全名
                string clsFullName = Path.Combine(proBuildPath, fi.Name);
                //复制类代码
                File.Copy(codepath, clsFullName, true);
                //创建一个ProcessStartInfo对象 使用系统shell指定命令和参数 设置标准输出
                var psi = new ProcessStartInfo("dotnet", "build " + proBuildPath) { RedirectStandardOutput = true };
                //启动
                var proc = Process.Start(psi);
                if (proc == null)
                {
                    LIS.Core.Util.LogUtils.WriteSecurityLog("编译:" + codepath + "无法运行命令!");
                    return "编译:" + codepath + "无法运行命令!";
                }
                else
                {
                    StringBuilder retsb = new StringBuilder();
                    //读取标准输出
                    using (var sr = proc.StandardOutput)
                    {
                        while (!sr.EndOfStream)
                        {
                            retsb.Append(sr.ReadLine());
                            LIS.Core.Util.LogUtils.WriteSecurityLog(sr.ReadLine());
                        }
                        //进程没结束就杀进程
                        if (!proc.HasExited)
                        {
                            proc.Kill();
                        }
                        //删除源文件
                        DeleteDirectory(proBuildPath);
                    }
                    if (proc.ExitCode == 0)
                    {
                        return "";
                    }
                    return retsb.ToString();
                }
            }
            catch(Exception ex)
            {
                return ex.Message;
            }
        }

        /// <summary>
        /// 删除文件夹
        /// </summary>
        /// <param name="dir">路径</param>
        private static void DeleteDirectory(string dir)
        {
            //递归结束
            if (Directory.GetDirectories(dir).Length == 0 && Directory.GetFiles(dir).Length == 0)
            {
                Directory.Delete(dir);
                return;
            }
            //删目录
            foreach (string var in Directory.GetDirectories(dir))
            {
                DeleteDirectory(var);
            }
            //删文件
            foreach (string var in Directory.GetFiles(dir))
            {
                File.Delete(var);
            }
            Directory.Delete(dir);
        }
    }
}

中间件处理调用请求片段

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Cors.Infrastructure;

namespace XXXLIS
{
    /// <summary>
    /// 主中间件
    /// </summary>
    public class MianMiddleware
    {
        /// <summary>
        /// 请求委托
        /// </summary>
        private readonly RequestDelegate _next;

        /// <summary>
        /// 静态资源目录
        /// </summary>
        public static string StaticFilesPath = "";

        /// <summary>
        /// 缓存路径和类型
        /// </summary>
        private static System.Collections.Hashtable hsType = new System.Collections.Hashtable();

        /// <summary>
        /// 类文件和dll时间最后检测时间
        /// </summary>
        private static System.Collections.Hashtable hsClassCheckTime = new System.Collections.Hashtable();

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="next"></param>
        public MianMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        /// <summary>
        /// 执行方法
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public Task Invoke(HttpContext httpContext)
        {
            //得到请求路径
            string path = httpContext.Request.Path;
            if (path.Contains(".asmx"))
            {
                return _next(httpContext);
            }
            if (!path.Contains(".ashx"))
            {
                httpContext.Response.WriteAsync("");
                return Task.CompletedTask;
            }
            //按路径解析得到类名
            string className = path.Split('.')[0];
            if (className == "")
            {
                httpContext.Response.WriteAsync("");
                return Task.CompletedTask;
            }
            //反射得到类型
            object objDeal = GetObjectByConfString(className, httpContext);
            IBaseHttpHandler baseDeal = (IBaseHttpHandler)objDeal;
            if (baseDeal != null)
            {
                try
                {
                    baseDeal.ProcessRequest(httpContext);
                }
                catch (Exception ex)
                {
                    httpContext.Response.WriteAsync(ex.Message);
                }
            }
            else
            {
                httpContext.Response.WriteAsync("系统提示:未找到全名为:" + className + "的处理类");
            }
            return Task.CompletedTask;
        }

        /// <summary>
        /// 通过配置得当对象
        /// </summary>
        /// <param name="confStr">配置</param>
        /// <returns></returns>
        private object GetObjectByConfString(string confStr, HttpContext httpContext)
        {
            //最后检测时间
            DateTime lastCheckTime = DateTime.Now.AddMinutes(-10);
            if (hsClassCheckTime.ContainsKey(confStr))
            {
                lastCheckTime = (DateTime)hsClassCheckTime[confStr];
            }
            //使用路径和类型哈希,提升创建对象速度
            if (hsType.ContainsKey(confStr))
            {
                if ((DateTime.Now - lastCheckTime).TotalSeconds < 60)
                {
                    Type objType = hsType[confStr] as Type;
                    return Activator.CreateInstance(objType);
                }
            }
            try
            {
                if (confStr[0] == '/')
                {
                    confStr = confStr.Substring(1);
                }
                string[] strArr = confStr.Replace(" ", "").Split(',');
                string[] nameArr = strArr[0].Split('/');
                string classFullName = "UI";
                //类代码全路径
                string classCodePath = StaticFilesPath;
                for (int i = 1; i < nameArr.Length; i++)
                {
                    classCodePath = Path.Combine(classCodePath, nameArr[i]);
                    classFullName += "." + nameArr[i];
                }
                //类代码地址
                classCodePath = classCodePath + ".cs";
                //编译的dll名字
                string clsDllPath = Path.Combine(AppContext.BaseDirectory, "BinAshx", classFullName + ".dll");
                if ((DateTime.Now - lastCheckTime).TotalSeconds > 60)
                {
                    //有代码没dll就执行编译
                    if (File.Exists(classCodePath) && (!File.Exists(clsDllPath)))
                    {
                        //标准工程文件
                        string standprofilepath = Path.Combine(AppContext.BaseDirectory, "LISStandAshxProj.csproj");
                        string buildRet = AutoBuild.Bulid(classCodePath, classFullName, standprofilepath);
                        if (buildRet != "")
                        {
                            httpContext.Response.WriteAsync("编译:" + classFullName + "发生错误:" + buildRet);
                        }
                        if (!hsClassCheckTime.ContainsKey(confStr))
                        {
                            hsClassCheckTime.Add(confStr, DateTime.Now);
                        }
                        else
                        {
                            hsClassCheckTime[confStr] = DateTime.Now;
                        }
                    }
                    else if (File.Exists(classCodePath) && (File.Exists(clsDllPath)))
                    {
                        FileInfo fiCode = new FileInfo(classCodePath);
                        FileInfo fiDll = new FileInfo(clsDllPath);
                        //更新检测时间
                        if (!hsClassCheckTime.ContainsKey(confStr))
                        {
                            hsClassCheckTime.Add(confStr, DateTime.Now);
                        }
                        else
                        {
                            hsClassCheckTime[confStr] = DateTime.Now;
                        }
                        //代码比dll新,执行编译
                        if ((fiCode.LastWriteTime - fiDll.LastWriteTime).TotalSeconds > 0)
                        {
                            //标准工程文件
                            string standprofilepath = Path.Combine(AppContext.BaseDirectory, "LISStandAshxProj.csproj");
                            string buildRet = AutoBuild.Bulid(classCodePath, classFullName, standprofilepath);
                            if (buildRet != "")
                            {
                                httpContext.Response.WriteAsync("编译:" + classFullName + "发生错误:" + buildRet);
                            }
                        }
                        else
                        {
                            if (hsType.ContainsKey(confStr))
                            {
                                Type objType = hsType[confStr] as Type;
                                return Activator.CreateInstance(objType);
                            }
                        }

                    }
                }
                Assembly assembly = null;
                //类的程序集不在,且目录程序集不在
                if ((!File.Exists(clsDllPath)) && strArr.Length < 2)
                {
                    assembly = Assembly.GetExecutingAssembly();
                }
                else
                {
                    string path = "";
                    //加载程序集
                    if (File.Exists(clsDllPath))
                    {
                        path = clsDllPath;
                    }
                    else
                    {
                        path = Path.Combine(AppContext.BaseDirectory, strArr[1]);
                        if (!path.Contains(".dll"))
                        {
                            path = path + ".dll";
                        }
                    }
                    assembly = Assembly.LoadFile(path);
                }
                object obj = assembly.CreateInstance(classFullName, false);
                //更新类型
                if (!hsType.ContainsKey(confStr) && obj != null)
                {
                    hsType.Add(confStr, obj.GetType());
                }
                else
                {
                    hsType[confStr] = obj.GetType();
                }
                return obj;
            }
            catch (Exception ex)
            {
                LIS.Core.Util.LogUtils.WriteExceptionLog("反射:" + confStr + "异常", ex);
                throw ex;
            }
        }

    }

    /// <summary>
    /// Extension method used to add the middleware to the HTTP request pipeline.
    /// </summary>
    public static class MianMiddlewareExtensions
    {
        public static IApplicationBuilder UseMianMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MianMiddleware>();
        }
    }
}

这样请求的类代理改动的话就会在60秒内编译新dll到BinAshx下供中间件反射,自动编译在Linux和Window都一样,不需要其他额外设置,部署时候安装DotNetCore的SDK,而不是仅仅运行时额。

请求了哪些后台服务类就编译哪些
在这里插入图片描述

在这里插入图片描述

写在最后,不停探索,积累基础知识,达到一定量了可能就来质变的方案了,敢于尝试,敢想敢干。

.NET 4 dotnet4 Friday, December 08, 2017 .Net4.0°²×°¼ì²â function isDotNet_Installed() -- .Net 4 Reg Key local DotNet_Key = "SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full"; --Check to see if the registry key exists local DotNet_Registry = Registry.DoesKeyExist(HKEY_LOCAL_MACHINE, DotNet_Key); if (DotNet_Registry == false) then -- The registry key does not exist -- Run the .NET Installation script -- Output to the log file that .NET could not be found, so it will be installed. SetupData.WriteToLogFile("Info\t.NET 4 Module: No version of .NET 4 was found. .NET 4 will be installed.\r\n", true); return false; else -- The key does exist -- Get the .NET install success value from the registry local DotNet_Install_Success = Registry.GetValue(HKEY_LOCAL_MACHINE, DotNet_Key, "Install", true); if (DotNet_Install_Success == "1") then -- Check the version key. local DotNet_Install_Version = Registry.GetValue(HKEY_LOCAL_MACHINE, DotNet_Key, "Version", true); -- Compare the returned value against the needed value Compare = String.CompareFileVersions(DotNet_Install_Version, "4.0.30319"); if (Compare == 0 or Compare == 1) then -- .NET version 4 is installed already SetupData.WriteToLogFile("Info\t.NET 4 Module: .NET version 4 is installed already\r\n", true); return true; else SetupData.WriteToLogFile("Info\t.NET 4 Module: A lesser version of .NET 4 was found on the users system.\r\n", true); return false; end else -- The success value wasn't found -- Run the .NET Installation script -- Output to the log file that .NET could not be found, so it will be installed. SetupData.WriteToLogFile("Info\t.NET 4 Module: No version of .NET 4 was found. .NET 4 will be installed.\r\n", true); return f
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小乌鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值