Как создать или использовать готовые прокладки для переноса из.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
{ }
}
}