Comment créer ou utiliser les éléments ready Shims for .net framework 4.6.1
pour les porter (de .net framework 4.6.1
) à .net core 2.0
.net standard 2.0
?
_/Quelques classes d’intérêt:, il serait bien d’avoir des cales pour des classes comme:
System.Windows.Threading.Dispatcher
ou
System.ComponentModel.ItemPropertyInfo.Descriptor
même
System.Windows.Controls.MenuItem
et beaucoup plus...
Le contexte:
L'application (le code) n'est pas bien organisée à 100%. La logique métier n'est pas séparée à 100% de la logique de l'interface utilisateur. La réponse "refactoriser d'abord" est certainement une bonne réponse. Mais dans mon cas, les choses ne sont pas à 100% comme elles devraient idéalement être.
Exemple approximatif, essayez de le faire manuellement:
System.Windows.Threading.Dispatcher
n'est pas implémenté dans Core 2.0
.
On pourrait essayer d'ajouter:
public enum DispatcherShimPriority
{
Background
//...
}
public interface DispaicherShim
{
void Invoke(Action action, DispatcherShimPriority prio);
void BeginInvoke(Action action, DispatcherShimPriority, prio);
}
Suivie de deux implémentations de cette interface:
public class DispatcherCore: DispaicherShim;
et
public class DispatcherFramework: DispaicherShim;
Suivi par une classe (appelons-la Shims
) dans un projet à cibles multiples:
public static DispaicherShim CreateDispatcher()
{
#if NETCOREAPP2_0
return new DispatcherCore();
#else
return new DispatcherFramework();
#endif
}
Le résultat est la cale, qui pourrait être utilisée dans différentes API.
Est-ce une approche correcte?
En réalité, créer de telles cales nécessite beaucoup de travail de routine. J'ai le sentiment que ce travail n'est pas nécessaire pour être effectué. J'ai le sentiment qu'il existe une solution prête pour ce problème ...
Je connais le paquet Microsoft.Windows.Compatibility
. La question est plutôt liée au portage lorsque WPF
est impliqué dans de nombreux éléments spécifiques à wpf. Ces éléments ne sont pas dans le package Microsoft.Windows.Compatibility
, mais, malheureusement, ils sont utilisés dans l'ensemble de mes assemblys, candidats au reciblage vers .Net Core 2.0
. Je veux dire ajuster ces classes, qui ne sont pas dans Microsoft.Windows.Compatibility
.
Ok, nous avons ce Microsoft.Windows.Compatibility.Shims
, mais je ne suis pas sûr que cela soit utile dans mon cas; surtout après avoir lu le texte suivant :
Microsoft.Windows.Compatibility.Shims: ce paquet fournit services d’infrastructure et ne doit pas être référencé directement à partir de votre code....
Upd: soulignant que la cible finale est .net core 2.0
Upd2: toute la tâche consiste à porter la majeure partie d'une application WPF vers .net core
(en laissant WPF app) pour le potentiel client Web. La partie majeure contient des éléments .net framework
qui ne sont pas implémentés pour .net core
.
Upd3: Quelques mots sur la stratégie complète: La stratégie la plus complète est Projets partagés, première approche de cet article (#if) . Ma stratégie comporte deux étapes principales: la première consiste à porter progressivement le code, en partant des bibliothèques de base et en terminant dans les bibliothèques supérieures, mais avec un usage intensif des stubs et des PlatformNotSupportedException
s. La deuxième étape consiste à passer des bibliothèques principales aux bibliothèques de base en substituant les stubs et les exceptions par des implémentations .net principales, à la demande (!) - inutile de remplacer tous les stubs et les exceptions.
Upd4 Nous avons déjà divisé les tests portables des tests non portables (en deux bibliothèques). Il est très important que nous exécutions les tests pendant le processus de portage.
Passer de .Net standard à .Net Core n’est pas simplement une mise à niveau, on pourrait presque parler de migration vers une nouvelle plate-forme, en tenant compte de la façon dont les choses sont organisées. Passer au noyau .Net signifie apprendre et créer un nouveau cadre où le code existant peut être copié.
En raison des grandes différences entre .Net Core 1, 1.1, 2.0 et 2.1. Le processus de migration a beaucoup changé, il n'y a donc pas de taille unique, et il serait rapide de créer un type d'outil d'emballage ou de migration obsolète. Il vous reste du travail à faire pour migrer votre code.
Certaines API de système d'exploitation de base sont similaires, mais de nombreux codes d'infrastructure ont été déplacés ou modifiés. Il peut donc s'avérer difficile de procéder de la même manière. Cela vaut vraiment la peine de faire de la R & D pour voir où les différences ne sont pas à mentionner l'utilisation de bibliothèques tierces, etc.
Voici des approches au moins satisfaisantes:
Merci Firda de République tchèque. C'est sa réponse
public abstract class Shim<TImpl>
{
internal TImpl It { get; }
protected Shim(TImpl it) { It = it; }
}
EXEMPLE:
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
{ }
}
Mon fichier .csproj
de style sdk pour préciser les NETFULL
et 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>
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 - pas encore
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(); }
}
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(); }
(En fait, Eto.Forms a une application plus large - ce sont les cales)
Cette infrastructure peut être utilisée pour créer des applications qui s'exécutent sur plusieurs plates-formes à l'aide de leur boîte à outils native, avec une API facile à utiliser. Cela donnera à vos applications une apparence et un fonctionnement natifs sur toutes les plates-formes, en utilisant une seule base de code d'interface utilisateur ...
//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
{ }
}
}