Как создать или использовать готовые прокладки для переноса из.net-структуры в.net core/standard?

Как создать или использовать готовые Shims для .net framework 4.6.1 элементов для их переноса (из .net framework 4.6.1) в .net core 2.0/.net standard 2.0?


Некоторые классы интересов: было бы неплохо иметь прокладки для таких классов, как:

System.Windows.Threading.Dispatcher

или же

System.ComponentModel.ItemPropertyInfo.Descriptor

четное

System.Windows.Controls.MenuItem

и многое другое...


Контекст:

Приложение (код) не на 100% хорошо организовано. Бизнес-логика не на 100% отделена от логики пользовательского интерфейса. Ответ "сделать рефакторинг первым" определенно является хорошим ответом. Но в моем случае вещи не на 100%, как они должны быть в идеале.


Примерный пример: попробуйте сделать это на людях:

System.Windows.Threading.Dispatcher не реализован в Core 2.0.

Можно попытаться добавить:

public enum DispatcherShimPriority
{
    Background
    //...
}

public interface DispaicherShim
{
    void Invoke(Action action, DispatcherShimPriority prio);
    void BeginInvoke(Action action, DispatcherShimPriority, prio);
}

Далее следуют две реализации этого интерфейса:

public class DispatcherCore: DispaicherShim;

а также

public class DispatcherFramework: DispaicherShim;

За ним следует класс aa (позвольте назвать Shims) в многозадачном проекте:

public static DispaicherShim CreateDispatcher()
{
#if NETCOREAPP2_0
    return new DispatcherCore();
#else
    return new DispatcherFramework();
#endif       
}

Результатом является прокладка, которая может использоваться в разных API.

Правильно ли это?


На самом деле создание таких прокладок требует много рутинной работы. У меня такое чувство, что эту работу не нужно выполнять. У меня такое чувство, что есть готовое решение для этой проблемы...


Я знаю пакет Microsoft.Windows.Compatibility. Вопрос скорее связан с переносом, когда WPF участвует со многими элементами, специфичными для wpf. Эти элементы не находятся в пакете Microsoft.Windows.Compatibility, но, к сожалению, они используются в моих сборках, которые являются кандидатами на перенацеливание на .Net Core 2.0. Я имею в виду экранирование тех классов, которые не находятся в Microsoft.Windows.Compatibility.

Хорошо, у нас есть этот Microsoft.Windows.Compatibility.Shims, но я не уверен, что это полезно в моем случае; особенно после прочтения следующего текста:

Microsoft.Windows.Compatibility.Shims: этот пакет предоставляет инфраструктурные услуги и не должен ссылаться непосредственно из вашего кода....


Обновление: подчеркивая, что конечной целью является .net core 2.0

Upd2: вся задача состоит в том, чтобы перенести основную часть приложения WPF в .net core (оставив рабочее приложение WPF) для потенциального веб-клиента. Основная часть содержит элементы .net framework которые не реализованы для .net core.

Upd3: пара слов о полной стратегии: более полная стратегия - это совлокальные проекты, первый подход в этой статье (#if). В моей стратегии есть два основных шага: один - постепенный перенос кода, начиная с базовых библиотек и заканчивая верхними библиотеками, но с интенсивным использованием заглушек и PlatformNotSupportedException. Вторым шагом является переход от лучших библиотек к базовым библиотекам, заменяющим заглушки и исключения с помощью реализаций ядра.net, по запросу (!) - нет необходимости подставлять все заглушки и исключения.

Upd4 Мы уже разделили переносные тесты из непереносимых тестов (в две библиотеки). Очень важно, чтобы мы запускали тесты во время процесса переноса.

Ответ 1

Переход от стандартного.Net к.NET Core - это не просто обновление, вы могли бы назвать его переходом на новую платформу, учитывая, как все складывается. Переход к ядру.Net означает обучение и создание новой структуры, в которой может быть скопирован существующий код.

Из-за больших различий между.Net core 1, 1.1, 2.0 и 2.1. Форма процесса миграции сильно изменилась, поэтому не существует 1 размера, подходящего для всех "прокладок", и с помощью какого-то инструмента обертки или переноса устарели. Необходимо выполнить работу по переносу кода.

Некоторые основные API-интерфейсы ОС аналогичны, но многие каркасные коды были перемещены или изменены, поэтому преследование, как и для свопов, также может быть затруднено. Его действительно стоит делать некоторые исследования и разработки, чтобы увидеть, где различия не упоминаются в использовании сторонних библиотек и т.д.

Ответ 2

Ниже приведены, по крайней мере, удовлетворительные подходы:

Спасибо Фирда из Чехии. Это его ответ

1) Для меня достаточно общей прокладки (могут помочь фрагменты)

public abstract class Shim<TImpl>
{
    internal TImpl It { get; }
    protected Shim(TImpl it) { It = it; }
}

ПРИМЕР:

public class DispatcherPriorityShim : Shim<
#if NETFULL
    DispatcherPriority
#elif NETCORE
    string
#endif
>
{
    public DispatcherPriorityShim(string it)
#if NETFULL
        : base((DispatcherPriority)Enum.Parse(typeof(DispatcherPriority), it))
#elif NETCORE
        : base(it)
#endif
    { }
}

Мой sdk-style .csproj файл, чтобы прояснить NETFULL и NETCORE:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup><TargetFrameworks>netstandard2.0;netcoreapp2.0;net461</TargetFrameworks></PropertyGroup>

  <PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0' OR '$(TargetFramework)' == 'netstandard2.0'">
    <DefineConstants>NETCORE;</DefineConstants></PropertyGroup>

  <PropertyGroup Condition=" '$(TargetFramework)' == 'net461'">
    <DefineConstants>NETFULL;</DefineConstants></PropertyGroup>
</Project>

1.а) Visual Studio сниппеты

DRV

#if NETFULL

#elif NETCORE

#endif

shimenum

namespace PortabilityLibrary.Shims
{
  public class $enumname$Shim : Shim<
#if NETFULL
    $enumname$
#elif NETCORE
    string
#endif
>
  {
        public $enumname$Shim(string it)
#if NETFULL
        : base(($enumname$)Enum.Parse(typeof($enumname$), it))
#elif NETCORE
          : base(it)
#endif
        { }
  }
}

shimsnip

namespace PortabilityLibrary.Shims
{
  public class $classname$Shim : Shim<
#if NETFULL
    $classname$
#elif NETCORE
    $classname$
//NullObject
#endif
>
  {
        public $classname$Shim()
#if NETFULL
        : base(new $classname$())
#elif NETCORE
        : base(new $classname$())
    //: base(new NullObject())
#endif
        {}
  }
}

shimmeth

        public void $methodname$()
        {
#if NETFULL
        It.$methodname$();
#elif NETCORE
        It.$methodname$();
        //throw new ShimException();
#endif
        }

shimprop - еще нет


2) Случай, когда необходимо наследование.

public interface IShimOne
{
    void MethodOne();
}
public interface IShimTwo: IShimOne
{
    void MethodTwo();
}
#if NETFULL
class One: RealOne, IShimOne {}
class Two: RealTwo, IShimTwo {}
public static class ShimFactory
{
    public static IShimOne CreateOne() { return new One(); }
    public static IShimTwo CreateTwo() { return new Two(); }
}

2.a) Объекты для наследования

public class WrapperOne
{
    protected IShimOne It { get; }
    protected WrapperOne(IShimOne it) { It = it; }
    public WrapperOne() { It = ShimFactory.CreateOne(); }
    public void MethodOne() { It.MethodOne(); }
}
public class WrapperTwo: WrapperOne
{
    protected new IShimTwo It => (IShimTwo)base.It;
    protected WrapperTwo(IShimTwo it): base(it) {}
    public WrapperTwo(): base(ShimFactory.CreateTwo()) {}
    public void MethodTwo() { It.MethodTwo(); }

3) Готовые "аналоги" для элементов управления графическим интерфейсом (Eto.Forms)

(на самом деле, Eto.Forms имеет более широкое применение - это прокладки)

Эта структура может использоваться для создания приложений, которые работают на нескольких платформах с использованием собственного набора инструментальных средств, с простым в использовании API. Это заставит ваши приложения выглядеть и работать как родное приложение на всех платформах, используя единую кодовую базу UI...

//Not fully implemented, just showing the idea:

#if NETFULL
using System.Windows.Controls;
#elif NETCORE
using Eto.Forms;
#endif

namespace PortabilityLibrary.Shims
{
    public class MenuItemShim : Shim<
#if NETFULL
    MenuItem
#elif NETCORE
    MenuItem
#endif
    >
    {
        public MenuItemShim(EventHandler<EventArgs> dlg)
#if NETFULL
        : base(new MenuItem(/*not implemented*/))
#elif NETCORE
        : base(new ButtonMenuItem(dlg))
#endif
        { }
    }
}