【问题标题】:Self-update / shadow-copy with Asp.Net Core使用 Asp.Net Core 进行自我更新/卷影复制
【发布时间】:2017-08-04 11:24:51
【问题描述】:

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

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

所以我的问题是:

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

【问题讨论】:

  • 你找到解决办法了吗?不幸的是,.Net core 发布已经 2 年了,似乎仍然没有对此的支持。我也面临类似的问题:stackoverflow.com/questions/47895998/…
  • 你找到解决方案了吗?
  • @MU 请在我描述我现在使用的解决方案的地方查看我的新答案。

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


【解决方案1】:

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

  1. 正在运行的应用程序会下载新的二进制文件并将其解压缩到新文件夹中。
  2. 正在运行的应用程序启动一个小型更新程序进程。以下参数通过命令行传递给更新程序进程:
    • 正在运行的应用程序的进程 ID
    • 正在运行的应用程序的二进制路径
    • 下载的二进制文件的路径
  3. 正在运行的应用程序自行退出。
  4. 更新程序进程会一直等待,直到正在运行的应用程序退出(使用进程 ID),或者如果它在给定的超时时间内没有自行退出,则强制终止正在运行的应用程序。
  5. 更新程序进程会删除现有的二进制文件并复制新下载的二进制文件。
  6. 更新程序进程启动新版本的主应用程序。

确保您在主应用程序中做尽可能多的事情(下载、解包、验证等),并使更新程序过程尽可能简单(将失败风险降至最低)。

这种方法已被证明是相当稳定的。

【讨论】:

  • 嗨@Robert,如果您创建并开源了您的解决方案(因为它看起来可能非常通用),您的解决方案是否可以在任何地方使用(例如nuget)?谢谢!
【解决方案2】:

.NET Core 中没有内置卷影复制工具

【讨论】:

    【解决方案3】:

    .Net API Browser 表示在 .Net Core 中设置它所需的属性是,但 AppDomainSetup 不是。

    需要明确的是,AppDomain 是在 .Net Standard 2.0 中添加的,但目前不支持创建域

    【讨论】:

    • AppDomain 是 .NET Standard 2.0 的一部分,但未在 .NET Core 中完全实现,请参阅 github.com/dotnet/standard/blob/…
    • 是的 AppDomain 现在似乎可用,但配置卷影复制所需的 AppDomainSetup 从 .NET Core 2.1 开始仍然不可用。所以看起来影子复制仍然不受支持。
    【解决方案4】:

    为了避免有人不得不做我刚刚做的事情并制作这个 - 这只会复制具有不同日期修改时间的文件。我检查并重建您的应用程序只会在几个文件上更改此内容。这使得自加载程序非常快速,然后在新位置启动 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:”是网络位置的示例,如果从这些位置运行,它应该将自身复制到本地目录,我使用应用程序数据/漫游/localApps,然后从新目录。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-03
      • 1970-01-01
      • 2014-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多