使用Asp.Net Core进行自我更新/卷影复制

时间:2017-03-14 08:05:27

标签: asp.net-core .net-core appdomain auto-update shadow-copy

我正在编写一个Asp.Net Core应用程序,该应用程序应该能够自行更新(在运行时替换自己的二进制文件)。

This MSDN article描述了使用经典的.Net框架进行影子复制,这正是我所需要的。但.Net Core中缺少整个AppDomain。

所以我的问题是:

  • 在.Net Core中是否有另一种方法可以启用阴影复制程序集?
  • .Net Core中是否有其他机制可以构建自我更新的应用程序?

4 个答案:

答案 0 :(得分:9)

由于.NET Core中没有用于执行此操作的内置机制,因此我最终实现了自己的自定义解决方案。它的工作方式大致如下:

  1. 正在运行的应用程序下载并将新二进制文件提取到新文件夹。
  2. 正在运行的应用程序启动一个小的更新程序进程。以下参数通过命令行传递给更新程序进程:
    • 正在运行的应用程序的进程ID
    • 正在运行的应用程序的二进制路径
    • 下载的二进制文件的路径
  3. 正在运行的应用程序退出。
  4. updater进程一直等待正在运行的应用程序退出(使用进程ID),或强行杀死正在运行的应用程序,如果它在给定的超时时间内没有自行退出。
  5. updater进程删除现有的二进制文件并复制新下载的二进制文件。
  6. 更新程序进程启动主应用程序的新版本。
  7. 确保在主应用程序中尽可能多地执行操作(下载,解包,验证......)并尽可能简化更新程序(最大限度地降低失败风险)。

    这种方法已被证明非常稳定。

答案 1 :(得分:4)

.NET Core中没有构建影子复制工具

答案 2 :(得分:2)

.Net API Browser表示在.Net Core中进行此设置所需的属性为,但AppDomainSetup则不是。

要清楚,.Net Standard 2.0中添加了AppDomain,但当前不支持创建域

答案 3 :(得分:0)

为了节省某人不得不做我刚刚做的事情并制作这个 - 这只会复制具有不同日期修改时间的文件。我检查并重建您的应用程序仅对几个文件进行了更改。这使得自加载程序非常快,然后在新位置启动 exe,然后退出执行从旧位置运行的加载的 exe。这可能取决于一些事情,例如运行代码的 DLL 必须与启动它的 EXE 命名相同。

适用于 .Net 5:

using System;
using System.Diagnostics;
using System.IO;

namespace NetworkHelper
{
    public static class LocalCopier
    {
        public static void EnsureRunningLocally(string callingAssemblyDotLocation)
        {
            var assemblyFileFriendlyName = Path.GetFileName(callingAssemblyDotLocation.Replace(".", "-"));
            var assemblyDirToCheck = Path.GetDirectoryName(callingAssemblyDotLocation);
            var localLocation = Configuration.Tools.AppsLocation + assemblyFileFriendlyName + "\\";
            var assemblyFinalExePath = localLocation + assemblyFileFriendlyName.Replace("-dll", ".exe"); 
            
            // Check what assembly passed in path starts with
            var runningFromNetwork = callingAssemblyDotLocation.ToLower().StartsWith(@"\\w2k3nas1\");
            if (callingAssemblyDotLocation.ToLower().StartsWith(@"i:\"))  runningFromNetwork = true;

            if (!runningFromNetwork) return;
            
            // Check if copied to local already
            Directory.CreateDirectory(localLocation);

            // Foreach file in source dir, recursively
            CopyOnlyDifferentFiles(assemblyDirToCheck, localLocation);

            Process.Start(assemblyFinalExePath);
            
            Environment.Exit(0);
        }

        private static void CopyOnlyDifferentFiles(string sourceFolderPath, string destinationFolderPath)
        {
            string[] originalFiles = Directory.GetFiles(sourceFolderPath, "*", SearchOption.AllDirectories);

            Array.ForEach(originalFiles, (originalFileLocation) =>
            {
                FileInfo originalFile = new FileInfo(originalFileLocation);
                FileInfo destFile = new FileInfo(originalFileLocation.Replace(sourceFolderPath, destinationFolderPath));

                if (destFile.Exists)
                {
                    if (originalFile.LastWriteTime != destFile.LastWriteTime)
                    {
                        originalFile.CopyTo(destFile.FullName, true);
                    }
                }
                else
                {
                    Directory.CreateDirectory(destFile.DirectoryName);
                    originalFile.CopyTo(destFile.FullName, false);
                }
            });
        }
    }
}

请注意,“\w2k3nas1”和“i:”是网络位置的示例,如果它从这些位置运行,它应该将自身复制到本地目录,我使用应用程序数据/漫游/本地应用程序,然后从新目录。

这都可以放入参考库中,并从任何客户端应用程序调用: NetworkHelpers.LocalCopier.EnsureRunningLocally(Assembly.GetExecutingAssembly().Location);

(此处,Assembly.GetExecutingAssembly().Location 是从调用应用程序传入的,因为如果您要从参考项目中运行它,您将获得该库的 dll。)