Самостоятельное обновление/теневое копирование с помощью Asp.Net Core

Я пишу приложение Asp.Net Core, которое должно иметь возможность обновлять себя (заменять свои собственные двоичные файлы во время работы).

В этой статье MSDN описывается теневое копирование с помощью классической платформы .Net, которая быть именно тем, что мне нужно. Но весь AppDomain отсутствует в .Net Core.

Итак, мои вопросы:

  • Есть ли альтернативный способ в .Net Core включить теневое копирование сборок?
  • Существуют ли в .Net Core другие механизмы, позволяющие создавать самообновляющиеся приложения?

person Robert Hegner    schedule 14.03.2017    source источник
comment
Вы нашли какую-нибудь работу для этого? К сожалению, прошло 2 года с момента запуска ядра .Net, и похоже, что это все еще не поддерживается. Я также сталкиваюсь с аналогичной проблемой: stackoverflow.com/questions/47895998/   -  person Jay Shah    schedule 20.12.2017
comment
Вы нашли решение для этого?   -  person Marek Urbanowicz    schedule 29.01.2018
comment
@MU Пожалуйста, посмотрите мой новый ответ, где я описываю решение, которое использую сейчас.   -  person Robert Hegner    schedule 30.01.2018


Ответы (4)


Поскольку для этого в .NET Core нет встроенного механизма, в итоге я реализовал свое собственное решение. Это работает примерно так:

  1. Запущенное приложение загружает и извлекает новые двоичные файлы в новую папку.
  2. The running application starts a small updater process. The following parameters are passed to the updater process via command line:
    • Process id of the running application
    • Двоичный путь запущенного приложения
    • Путь к загруженным бинарникам
  3. Работающее приложение завершает работу.
  4. Процесс обновления ждет, пока запущенное приложение завершится (используя идентификатор процесса), или принудительно завершает работающее приложение, если оно не завершается само по себе в течение заданного времени ожидания.
  5. Процесс обновления удаляет существующие двоичные файлы и копирует новые загруженные двоичные файлы.
  6. Процесс обновления запускает новую версию основного приложения.

Старайтесь делать как можно больше в основном приложении (загрузка, распаковка, проверка и т. д.) и максимально упрощайте процесс обновления (минимизируйте риск сбоя).

Этот подход оказался достаточно устойчивым.

person Robert Hegner    schedule 30.01.2018
comment
Привет @Robert, если вы создали и открыли свое решение (поскольку кажется, что оно может быть очень общим), доступно ли ваше решение где-нибудь (например, nuget)? Спасибо! - person Rich O'Kelly; 20.05.2019

В .NET Core нет встроенных средств теневого копирования.

person davidfowl    schedule 16.03.2017

Браузер .Net API указывает, что свойство, необходимое для его настройки в .Net Core, есть, а AppDomainSetup — нет.

Чтобы было ясно, AppDomain был добавлен в .Net Standard 2.0, но создание домена в настоящее время не поддерживается.

person Digital Coyote    schedule 19.07.2018
comment
AppDomain является частью .NET Standard 2.0, но не полностью реализован в .NET Core, см. часть сетевого стандарта" rel="nofollow noreferrer">github.com/dotnet/standard/blob/ - person 0xced; 03.09.2018
comment
Да, теперь AppDomain кажется доступным, но AppDomainSetup, который необходим для настройки теневого копирования, по-прежнему недоступен в .NET Core 2.1. Так что похоже, что теневое копирование все еще не поддерживается. - person Robert Hegner; 04.09.2018

Чтобы избавить кого-то от необходимости делать то, что я только что сделал, и сделать это - это только копирует файлы с другим временем изменения даты. Я проверил, и перестройка вашего приложения меняет это только в нескольких файлах. Это обеспечивает очень быстрый самозагрузчик, который затем запускает исполняемый файл в новом месте и завершает выполнение исполняемого файла, выполняя загрузку, которая выполнялась из старого места. Это может зависеть от нескольких вещей, например, ваша 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 этой библиотеки.)

person John Sikes    schedule 31.03.2021