צ'קליסט לפני migration מ-.NET Framework ל-.NET
· עודכן בתאריך: · Go Komura · .NET, .NET Framework, C#, modernization, פיתוח Windows, migration
היסטוריית עדכונים (גרסה ראשונה, פורסמה בתאריך 15 Mar 2026)
- פרסום ראשון
לצטט את המאמר הזה(DOI: 10.5281/zenodo.22173485)
מאמר זה מאוחסן בארכיון Zenodo. להלן גם ה-DOI שתמיד מפנה לגרסה האחרונה וגם ה-DOI המקובע לגרסה שאתם קוראים.
Go Komura (2026). צ'קליסט לפני migration מ-.NET Framework ל-.NET. KomuraSoft LLC. https://doi.org/10.5281/zenodo.22173485 https://comcomponent.com/he/blog/dotnet-framework-to-dotnet-premigration-checklist/
- DOI (הגרסה האחרונה)
- 10.5281/zenodo.22173485
- DOI (הגרסה הזו)
- 10.5281/zenodo.22173486
הורידו את הצ’קליסט ב-Excel עם גיליון יפני ואנגלי
הקובץ הזה בנוי משני גיליונות, Checklist-ja ו-Checklist-en, ומוריד את צ’קליסט 13 הפרקים שלפני תחילת העבודה ל-7 קטגוריות ו-34 סעיפים: מדיניות / הכנת צד .NET Framework הנוכחי / סוג האפליקציה ובחירת טכנולוגיה / טכנולוגיות לא נתמכות ו-API-ים שדורשים תשומת לב / תלות Windows-only / shared library וגישה לנתונים / תפעול ו-build. עמודות Status ו-Notes נשארות ריקות, כך שאפשר להשתמש בקובץ ישירות כטבלת inventory לכל פרויקט. כדאי לעבור עליו לפני קריאת המאמר — כך קל יותר לתפוס את הזרימה עד פרק 13.
משנים את TargetFramework ב-.csproj ל-net10.0, מעדכנים כמה חבילות NuGet, וכש-build עובר — סיימנו.
……אם זה היה ה-migration, זה היה די שליו. בפועל, בדרך כלל זה לא ככה.
בסביבת .NET Framework בשטח יש לא מעט הנחות שבדרך כלל לא שמים לב אליהן: System.Web, WCF, Web Forms, packages.config ישן, web.config.install.xdt, native DLL, COM / ActiveX, בקרות צד שלישי שרצות רק ב-design-time, הנחה שקטה של x86, ResX שתלוי ב-designer, ו-serializer ישן.
לכן, מה שבאמת חשוב ב-migration מ-.NET Framework ל-.NET הוא ה-inventory לפני שמתחילים לממש. אם מפרקים את הסוגיות לפני שמתחילים, ה-migration נהיה לא “הימור גדול” אלא “עבודה שסוגרים סעיף אחרי סעיף”.
flowchart TB
accTitle: inventory משנה את אופי ה-migration
accDescr: אם עושים inventory לפני שמתחילים לממש ומפרקים את הסוגיות, ה-migration נהיה עבודה לפי סדר. דילוג על ה-inventory הופך אותו להימור גדול.
hid1["הנחות סמויות ישנות"] --> inv1["inventory לפני שמתחילים מפרק סוגיות"]
inv1 --> tsk1["עבודה שסוגרים סעיף אחרי סעיף"]
hid1 -.->|"אם מדלגים על ה-inventory"| bet1["ה-migration הופך להימור גדול"]
איור 1: היכולת לפרק סוגיות לפני שמתחילים קובעת אם ה-migration יהיה הימור או עבודה מסודרת.
המאמר הזה מסכם מה כדאי לבדוק לפני שמעבירים אפליקציה עסקית קיימת ב-.NET Framework 4.x ל-.NET הנוכחי. היעד העיקרי הוא סוגי האפליקציות הבאים:
- class library
- console
- Windows Service
- WinForms / WPF
- ASP.NET Framework (MVC / Web API / Web Forms)
- אפליקציות שמשתמשות ב-WCF
- אפליקציות שמשתמשות ב-EF6
זמן הכתיבה הוא 2026-03-15. תקופות התמיכה והמלצות הכלים הרשמיים משתנות, ולכן אם קוראים את המאמר בפער זמן, כדאי לבדוק גם את המידע הרשמי.
1. קודם המסקנות
תחילה, רק את המסקנות שלא כדאי לפספס:
- קודם מכינים את צד .NET Framework לפני ה-migration. גם במדריך הרשמי של Microsoft ממליצים, לפני ה-port, לעלות ל-.NET Framework 4.7.2 ומעלה, להמיר ל-
PackageReference, לעבור ל-SDK-style, ולעדכן תלויות קודם. - הקושי נקבע לפי app model, לא לפי כמות הקוד. class library ו-console יחסית קלים, ואילו ASP.NET Framework, Web Forms, WCF server ו-WF (Workflow Foundation) נוטים להיות כבדים.
- WinForms / WPF יכולים לעבור ל-.NET אבל נשארים Windows-only. טעות בהבנה כאן מובילה למוקש קלאסי — עשיתם migration ועדיין לא רצים על Linux container.
- המעבר מ-ASP.NET Framework ל-ASP.NET Core הוא בפועל מעבר ארכיטקטורה. באפליקציה קטנה אפשר לפעמים לעבור בבת אחת, אבל במערכת production גדולה בטוח יותר להניח incremental migration.
- WCF ו-EF6 לפעמים אפשר להפריד מ-migration של ה-runtime. ל-WCF client יש חבילה נתמכת ל-.NET, ו-EF6 אפשר להעביר בנפרד ל-EF Core אחרי המעבר ל-modern .NET.
- מצד שני, יצירת AppDomain, .NET Remoting, CAS (Code Access Security), COM+, Workflow Foundation, ותלות ב-BinaryFormatter הם red flags. אם לא מוצאים אותם קודם, נפח העבודה מתפוצץ בהמשך.
packages.config/install.ps1/XDT(XML Document Transform, מנגנון שהופךweb.configוכדומה) / נכסיcontent/ native DLL / COM / ActiveX / הנחת x86 — כל אלה נוטים ליפול ב-runtime או ב-design-time גם כש-build עובר, ולכן סורקים אותם לפני שמתחילים.- נכון ל-2026-03, .NET 10 הוא LTS. נקודת היעד ל-migration חדש טבעי לחשוב עליה בהתבסס על ה-LTS הנוכחי.
- migration בלי בדיקות, מדידה והכנה ל-rollback הוא מסוכן. ה-migration הוא לא בעצם עבודת מימוש, אלא עבודה של חשיפת תנאי היסוד אחד-אחד.
ב-diagram, solid line מציינת relation שתמיד מתקיים ו-dashed line מציינת relation מותנה (התנאים מופיעים בהסבר של כל relation ב-detail page). הרשימה המלאה של ה-relations (סה”כ 30, כולל evidence ו-certainty) וההגדרות של ה-concepts המרכזיים נמצאות ב-detail page של ה-knowledge map (ביפנית). Data: JSON-LD / Turtle
2. קודם קובעים אם באמת צריך לעבור עכשיו
הדבר הראשון שכדאי לקבוע הוא לא “איך עושים migration”, אלא האם באמת צריך להעביר את האפליקציה הזו עכשיו.
אם זה נשאר מעורפל, נוטים לקבל החלטה שנכונה טכנית אבל כבדה מדי עסקית, או להפך, לדחות migration שברור שכדאי לעשות אותו.
flowchart TB
accTitle: השאלה שקובעים ראשונה
accDescr: לפני שקובעים איך עושים migration, צריך לקבוע האם באמת צריך לעבור עכשיו. מעורפלות כאן נוטה להוביל ל-migration כבד מדי או לדחייה מוגזמת.
q1["האם באמת צריך לעבור עכשיו"] -->|"קובעים תחילה"| how1["איך עושים migration"]
q1 -.->|"אם ממשיכים במעורפל"| bad1["migration כבד מדי או דחייה מוגזמת"]
איור 2: בלי לקבוע “האם צריך לעבור עכשיו” לפני “איך”, ההחלטה נוטה להיסחף לכיוון אחד.
2.1 גם החלטה להישאר על .NET Framework היא סבירה לגמרי
.NET Framework 4.8.1 ממשיך לקבל תמיכה כל עוד הוא רץ על Windows נתמך. כלומר, זה לא סיפור פשוט של “אם לא נעביר הכול מיד ל-modern .NET, זה מסוכן”.
עם זאת, להישארות יש מגבלות ברורות.
- אי אפשר לצאת מ-Windows-only
- ממשיכים לשאת ASP.NET Web Forms או שכבת שרת ישנה
- קשה ליהנות משיפורי ביצועים, תכונות שפה ואקוסיסטם של .NET חדש
- קל להיסחף מהנחות עדכניות של cloud, container ו-CI/CD
מצד שני, אם התלות הזו חזקה, הגיוני להישאר בינתיים על .NET Framework 4.8.1 עם תפעול יציב, ולתכנן החלפה בקו נפרד.
flowchart TB
accTitle: הישארות היא בחירה סבירה
accDescr: אם יש תלות legacy חזקה כמו Web Forms, תאימות WCF server או COM+, הגיוני להישאר בינתיים על .NET Framework 4.8.1 עם תפעול יציב, ולתכנן החלפה בקו נפרד.
dep1["יש תלות legacy חזקה"] --> stay1["בינתיים, תפעול יציב על 4.8.1"]
stay1 --> plan1["תכנון החלפה בקו נפרד"]
stay1 -.-> lim1["מגבלות כמו Windows-only נשארות"]
איור 3: כשהתלות עמוקה, שילוב של תפעול יציב על 4.8.1 ותכנון החלפה נפרד הוא ריאלי.
- יש כמות גדולה של מסכי Web Forms
- צריך לשמור בקפדנות על תאימות WCF server
- תלות עמוקה ב-Workflow Foundation או ב-COM+
- רכיב design-time של צד שלישי לא נתמך ב-modern .NET
- לא ניתן לאשר עסקית שינוי אפיון גדול
2.2 מה משתנה לפי כל בחירה
| בחירה | מה משתפר | מה נשאר / מה נאבד | מתי מתאים |
|---|---|---|---|
| להישאר על .NET Framework 4.8.1 | קל יותר לשמור על נכסים קיימים ולתפעל ביציבות | Windows-only, app model ישן, מגבלת modernization | תלות legacy חזקה, כרגע עדיפות עסקית עם תפעול יציב |
| migration ל-modern .NET, נשארים ב-Windows | אפשר להביא runtime ו-toolchain מודרניים. תועלת גדולה בביצועים, חוויית פיתוח ו-SDK-style | תלות ב-Windows API נשארת. לא הופך ל-cross-platform | אפליקציה עסקית שמשתמשת ב-WinForms / WPF, Windows Service, Windows API |
| migration ל-modern .NET, עם ראייה לעתיד ל-Linux / container / cloud | חופש רב יותר ב-deployment. קל יותר גם לחדש את מודל התפעול | צריך להסיר קודם API-ים ו-app model ייעודיים ל-Windows | רוצים להעביר את צד השרת ל-cloud, וגם לחדש את התשתית |
חשוב לקבוע קודם לא האם רוצים לעשות migration, אלא לאן רוצים להגיע אחריו.
flowchart TB
accTitle: קובעים את נקודת היעד
accDescr: החשוב הוא לקבוע לא האם רוצים לעשות migration, אלא לאן רוצים להגיע אחריו, וזה משפיע על ההחלטה.
ask1["האם רוצים לעשות migration"] -.->|"שאלה לא מספקת"| dec1["מה קובעים קודם"]
land1["לאן רוצים להגיע אחרי ה-migration"] -->|"זה מה שקובעים"| dec1
dec1 --> path1["האפשרויות והנקודות לבדיקה מתבררות"]
איור 4: אם משנים את השאלה מ”רוצים לעבור?” ל”לאן רוצים להגיע?”, האפשרויות מתבררות.
3. ארבע מדיניות שכדאי לקבוע מראש
3.1 גרסת .NET של נקודת היעד
בזמן הכתיבה, לפי מדיניות התמיכה של Microsoft, .NET 10 הוא LTS. מצד שני, .NET 8 LTS ו-.NET 9 STS שניהם מתוכננים לסיים תמיכה באותו התאריך, 2026-11-10. הסיבה ש-STS מקבל את אותו תאריך כמו LTS היא שתקופת ה-STS הוארכה מ-18 חודשים ל-24 חודשים. אם סופרים לפי ההנחה הישנה ש”STS זה 18 חודשים”, נראה שסיום התמיכה של .NET 9 הוא ב-2026-05-12, ולכן כשמתבססים על תאריך, כדאי לוודא מול מידע מחזור החיים הרשמי.
לכן, כשעוברים מ-.NET Framework מחדש, אלא אם יש נסיבות מיוחדות, טבעי לקבוע את ה-LTS הנוכחי כנקודת היעד.
הגישה המעשית כאן פשוטה:
- לmigration קטן שרוצים לסיים מהר, מגיעים ישירות ל-LTS הנוכחי
- גם במערכת ליבה עם תפעול ארוך-טווח, חושבים ראשית על ה-LTS הנוכחי
- “בגלל נוחות ספרייה קיימת רוצים LTS קודם” יכול להיות נסיבה סבירה, אבל בודקים עד מתי היא נתמכת לפי תאריך
flowchart TB
accTitle: הגישה לבחירת גרסת היעד
accDescr: גם migration קטן וגם מערכת ליבה בוחרים בבסיס ב-LTS הנוכחי. אם רוצים LTS קודם בגלל ספרייה קיימת, בודקים את תאריך סיום התמיכה.
tgt1["בוחרים .NET לנקודת היעד"] -->|"בסיס"| lts1["מגיעים ל-LTS הנוכחי"]
tgt1 -->|"נוחות ספרייה קיימת"| old1["שוקלים גם LTS קודם"]
old1 --> dt1["בודקים תאריך סיום תמיכה ומחליטים"]
איור 5: נקודת היעד הבסיסית היא ה-LTS הנוכחי, ואם בוחרים ב-LTS ישן, מבססים על תאריך תפוגת התמיכה.
3.2 להישאר Windows-only, או לשאוף ל-cross-platform בעתיד
ההחלטה הזו משנה משמעותית מה בודקים.
- אם נשארים Windows-only, אפשר לנקוט מסלול מעשי של modernization קודם ל-runtime, בעזרת WPF / WinForms ו-Windows Compatibility Pack.
- אם שואפים בעתיד גם ל-Linux / containerization, צריך לעשות inventory מוקדם ל-API-ים שמניחים Windows כמו
System.Drawing.Common, Registry, WMI, EventLog, Windows Service ו-Office Interop.
אם לא מחליטים על זה ומתחילים migration, בהמשך הדיון מתפתל לכיוון “האם באמת נכון היה להישאר על Windows” או “לא, רצינו לשים על container”.
flowchart TB
accTitle: ההסתעפות בין Windows-only ל-cross-platform
accDescr: אם נשארים Windows-only אפשר לנקוט מסלול מעשי של modernization ל-runtime עם Windows Compatibility Pack. אם שואפים בעתיד ל-container או ל-Linux, צריך inventory מוקדם של API-ים שמניחים Windows.
aim1["באיזה כיוון שואפים"] -->|"נשארים Windows-only"| wn1["modernization של runtime עם Windows Compatibility Pack"]
aim1 -->|"בעתיד גם container"| xp1["inventory מוקדם של API-ים שמניחים Windows"]
aim1 -.->|"מתחילים בלי להחליט"| twist1["הדיון מתפתל באמצע"]
איור 6: בלי להחליט על ההסתעפות הזו מראש, מה שצריך לבדוק לא מתברר, והדיון מתפתל באמצע.
3.3 לעשות הכול בבת אחת, או incremental migration
יש שלושה סוגים עיקריים ל-migration:
- migration מרוכז שקרוב ל-in-place
- migration שבו ישן וחדש רצים side-by-side זה לצד זה
- incremental migration שמתקדם בהדרגה לפי route / library
בפרט, באפליקציות ASP.NET Framework, גם המדריך של Microsoft מנחה בבירור ל-incremental migration. אם לא רוצים לעצור את ה-production, יש הרבה תכונות, והתלויות סביב רבות, טבעי יותר לתכנן מראש מתוך הנחת incremental migration.
flowchart TB
accTitle: שלושת הסוגים של ה-migration
accDescr: יש שלושה סוגי migration: מרוכז קרוב ל-in-place, side-by-side עם ישן וחדש במקביל, ו-incremental לפי route או library. בתנאי שלא רוצים לעצור production, ההדרגתי טבעי יותר.
typ1["בוחרים סוג migration"] --> t1["migration מרוכז (קרוב ל-in-place)"]
typ1 --> t2["side-by-side עם ישן וחדש במקביל"]
typ1 --> t3["incremental לפי route / library"]
t3 -.-> fit1["אם לא רוצים לעצור production, זו ההנחה הטבעית"]
איור 7: יש שלושה סוגי migration, ובמערכת production עם הרבה תכונות שלא ניתן לעצור, incremental הוא ההנחה הטבעית.
3.4 מה מוציאים מהיקף ה-migration הזה
migration נוטה להיכשל כי לוקחים על עצמם יותר מדי.
לדוגמה, לעשות בו-זמנית את הבאים נוטה להיות כבד:
- .NET Framework → .NET
- ASP.NET Framework → ASP.NET Core
- EF6 → EF Core
- Windows Server → Linux container
- שינוי תשתית authentication
- שינוי תשתית logs / monitoring
- migration של database
כמובן שכולם עשויים להידרש בשלב כלשהו. אבל האם צריך לעשות אותם בו-זמנית זה עניין אחר.
בפועל, ההפרדה הבאה עובדת יותר טוב:
- קודם, modernization של runtime ומבנה הפרויקט
- על גביה, העברת app model
- לבסוף, עדכון ORM, authentication, cloud ו-monitoring
flowchart TB
accTitle: סדר ההפרדה שלא לוקחים יותר מדי
accDescr: קודם עושים modernization ל-runtime ולמבנה, אחר כך מעבירים את ה-app model, ולבסוף מעדכנים ORM, authentication, cloud ו-monitoring. סדר הפרדה שמונע מה-migration להיות כבד.
d1["modernization של runtime ומבנה"] --> d2["העברת app model"]
d2 --> d3["עדכון ORM, authentication, cloud ו-monitoring"]
d1 -.->|"אם לוקחים הכול בו-זמנית"| hv1["ה-migration נהיה כבד ונוטה להיכשל"]
איור 8: בלי לעשות הכול בו-זמנית, אלא בהפרדה של runtime, app model והשוליים, ה-migration לא נהיה כבד.
4. הבסיס שמכינים לפני שמתחילים
המדריך המקדים ל-port של Microsoft מעשי מאוד. בקיצור, מדובר בהכנת פרויקט .NET Framework הנוכחי לכיוון כניסה מודרנית לפני ה-migration.
4.1 עלייה ל-.NET Framework 4.7.2 ומעלה
במדריך הרשמי מומלץ, לפני ה-port, לכוון ל-.NET Framework 4.7.2 ומעלה. הסיבה היא שגם כש-.NET Standard לא מחזיק ישירות את ה-API הקיים, קל יותר להתקרב לחלופת API עדכנית יותר.
בשטח, אם אפשר, ברור יותר לחשוב מנקודת מוצא של 4.8.1.
- ברור יותר מבחינת נקודת מבט התמיכה
- קל יותר להתייחס לזה כנקודת יציבות אחרונה בצד .NET Framework
- קל יותר לגבש מדיניות של “קודם מכינים בצד ה-Framework הנוכחי”
מה משתנה אם עושים את זה קודם
- קל יותר לייצב את הטיפול ב-shared library של
.NET Standard 2.0 - אפשר לצמצם מראש רעש שמקורו ב-runtime ישן
- קל יותר להפריד אם בעיית תאימות מגיעה מ-Framework ישן או מהמעבר ל-modern .NET
4.2 מעבר ל-PackageReference
במדריך המקדים מומלץ להתקדם צורת ההפניה ל-PackageReference.
אם עושים את זה קודם, התצוגה הכוללת של ניהול התלויות משתפרת משמעותית.
מה משתנה כשעוברים ל-PackageReference
- הפניות החבילות מרוכזות ב-
csproj - קל יותר לראות תלויות טרנזיטיביות
- הנחת ה-restore מתאימה לצד modern .NET
- תאימות טובה יותר עם CLI / CI
עם זאת, יש כאן נקודה שנופלים בה.
ה-gotcha הטיפוסי
בתיעוד הרשמי של NuGet כתובות המגבלות הבאות במעבר מ-packages.config ל-PackageReference:
- פונקציית ההמרה המובנית של Visual Studio לא זמינה בפרויקטי ASP.NET
- חבילות שתלויות ב-
install.ps1/uninstall.ps1עלולות לא לפעול כצפוי - נכסים בתיקיית
contentעלולים להיות מתעלמים - המרות XDT כמו
web.config.install.xdtלא מוחלות - חבילות עם מבנה assembly ישן תחת
libעלולות לא להסתדר כראוי
כלומר, לא כדאי לחשוב שמדובר רק בשינוי צורת החבילה. בפרט, ל-classic ASP.NET הייתה תרבות משמעותית שבה התקנת חבילת NuGet כותבת מחדש את web.config, ולכן בזמן ה-migration קל שהנחות סמויות יעלו לפני השטח.
flowchart TB
accTitle: הנחות סמויות שנחשפות במעבר ל-PackageReference
accDescr: במעבר מ-packages.config ל-PackageReference, install.ps1 והמרות XDT ונכסי content עלולים לא להיות מוחלים, ולכן הנחות סמויות שהתבססו על כתיבה מחדש של web.config בזמן התקנה נחשפות.
cv1["המרה ל-PackageReference"] --> np1["install.ps1 עלול לא לפעול"]
cv1 --> nx1["המרות XDT לא מוחלות"]
cv1 --> nc1["נכסי content עלולים להתעלם"]
np1 --> exp1["ההנחות הסמויות נחשפות"]
nx1 --> exp1
nc1 --> exp1
איור 9: גם אם חושבים שזו רק המרת צורה, ההסתמכות על מנגנוני ההתקנה נחשפת בבת אחת.
באילו כלים ממירים
זה לא רק עבודה ידנית. עם זאת, לכל כלי טווח שונה.
| כלי | מה אפשר לעשות | הנחות והערות |
|---|---|---|
| פונקציית ההמרה של Visual Studio | ב-Solution Explorer, לחיצה ימנית על “References” או על packages.config ובחירה ב-Migrate packages.config to PackageReference... מבצעת המרה |
Visual Studio 2017 15.7 ומעלה. לא זמין בפרויקטי ASP.NET ו-C++. אם לא מופיע בתפריט, פותחים פעם את NuGet restore או את Package Manager ואז לוחצים ימנית שוב |
| .NET Upgrade Assistant | מבצע ניתוח ושדרוג של הפרויקט יחד | בתיעוד הרשמי הוא כבר לא מומלץ, ומופנים להשתמש ב-GitHub Copilot app modernization |
| GitHub Copilot app modernization | מסייע עד להערכה, תכנון, תיקון קוד ואימות | ליבת ההנחיה הרשמית הנוכחית. כפי שבפרק 4.5, מניח Visual Studio, Copilot וקוד C# |
| המרה ידנית ל-SDK-style | יוצרים csproj חדש ומעבירים רק את הפריטים הנדרשים |
בפרויקט עם מעט קבצים, לעיתים זה המהיר ביותר |
פונקציית ההמרה של Visual Studio יוצרת גיבוי של הפרויקט לפני ההרצה, ומוציאה בסוף דוח המרה (תלויות ברמה עליונה, תלויות טרנזיטיביות, בעיות תאימות שזוהו). בתיעוד כתוב שאם רוצים לחזור אחורה, משחזרים את csproj ואת packages.config מתיקיית הגיבוי, ומריצים update-package -reinstall ב-Package Manager Console.
כלומר, אפשר לנסות בצורה שניתנת לביטול. באומדן לפני שמתחילים, כדאי להמיר קודם פרויקט אחד בלבד ולקרוא את הדוח.
flowchart TB
accTitle: מנסים המרה בצורה שניתנת לביטול
accDescr: פונקציית ההמרה של Visual Studio יוצרת גיבוי לפני ההמרה ומוציאה דוח, ולכן ממירים תחילה פרויקט אחד בלבד וקוראים את הדוח, ואם יש בעיה משחזרים מהגיבוי.
bk1["נוצר גיבוי"] --> cv2["המרת פרויקט אחד בלבד"]
cv2 --> rp1["קריאת דוח ההמרה"]
rp1 -->|"אם יש בעיה"| rv1["שחזור והתקנה מחדש"]
rp1 -->|"אם אין בעיה"| go1["הרחבה לפרויקטים נוספים"]
איור 10: ההמרה ניתנת לניסיון עם גיבוי ודוח, ולכן קודם בודקים על פרויקט אחד.
4.3 מעבר ל-SDK-style
במדריך המקדים מומלצת גם המרה לצורת פרויקט SDK-style.
זה משפיע משמעותית.
מה משתנה עם SDK-style
- ה-
csprojנעשה פשוט משמעותית - תאימות טובה עם
PackageReference - קל יותר לבצע multi-targeting
- קל יותר להתאים ל-CI/CD שמתבסס על
dotnet build/dotnet test/dotnet publish - הקרבה למבנה של צד modern .NET מצמצמת את ה-gap בשלב השני
מנקודת מבט הפוכה, זה אומר שאם קופצים ישר ל-modern .NET עם csproj ישן וניהול NuGet ישן, ה-gap גדול מדי.
flowchart TB
accTitle: מעבר ל-SDK-style מצמצם gap
accDescr: קפיצה ישירה ל-modern .NET עם csproj ישן וניהול NuGet ישן יוצרת gap גדול מדי. מעבר קודם ל-SDK-style מקרב למבנה הצד המודרני ומצמצם את ה-gap בהמשך.
oldp["csproj ישן + ניהול NuGet ישן"] -.->|"קפיצה ישירה"| big1["gap גדול מדי"]
oldp -->|"קודם מעבר ל-SDK-style"| sdk1["התקרבות למבנה הצד המודרני"]
sdk1 --> small1["ה-gap בהמשך מצטמצם"]
איור 11: אם משלבים מעבר ל-SDK-style, ה-gap בקפיצה ל-modern .NET מצטמצם.
4.4 עדכון תלויות קודם
גם זה לפי המדריך הרשמי, אבל את התלויות מקדמים להגרסה העדכנית הזמינה, ואם אפשר, לגרסה שתומכת ב-.NET Standard.
המשמעות בעשיית זה קודם
- מהר יותר יודעים “האם אפשר להשתמש בחבילה הזו ב-modern .NET”
- מונעים מתלויות ישנות להפוך לרעש
- קל יותר להמיר shared library ל-
netstandard2.0 - קל יותר לרכז את עבודת ה-migration בשלב הבא ב”העברת הקוד”
flowchart TB
accTitle: המשמעות בעדכון התלויות קודם
accDescr: עדכון התלויות מראש לגרסה עדכנית מאפשר לדעת מהר אם הן תומכות ב-modern .NET, מפחית רעש מתלויות ישנות, ומאפשר לרכז את עבודת ה-migration בשלב הבא בהעברת הקוד.
upd1["עדכון התלויות מראש"] --> know1["מהר יותר יודעים אם תומך ב-modern .NET"]
upd1 --> noise1["מפחית רעש מתלויות ישנות"]
know1 --> foc1["השלב הבא מתמקד בהעברת הקוד"]
noise1 --> foc1
איור 12: ככל שעדכון התלויות מוקדם יותר, גוף ה-migration יכול להתמקד בהעברה עצמה.
4.5 בודקים גם את הנחות הכלים הרשמיים
נכון ל-2026-03, מרכז הכובד של הנחיית Microsoft עבר לכיוון GitHub Copilot app modernization. במקום להתבסס רק על כלי migration ותיקים, מעשי יותר להתייחס לזה כזרם תמיכה שכולל הערכה, תכנון, תיקון קוד ואימות.
עם זאת, בתיעוד הנוכחי, ההנחה היא Visual Studio 2026 או Visual Studio 2022 בגרסה נתמכת, GitHub Copilot, וגם קוד C#.
למה הבדיקה הזו נדרשת
- מה אפשר לצפות מהכלי הרשמי משתנה
- קל יותר להתאים בין הצוות את הנחות ה-IDE / build agent / התוספים
- אפשר להגיע להחלטה שלא לצפות ליותר מדי אוטומציה ב-solution-ים של VB.NET
לא נדיר לראות סביבת עבודה שיש בה גם VB.NET. לכן, שווה לבדוק מראש “עד כמה הכלי הרשמי העדכני יעזור”.
flowchart TB
accTitle: המצב הנוכחי וההנחות של הכלים הרשמיים
accDescr: .NET Upgrade Assistant נהיה לא מומלץ ומרכז הכובד של ההנחיה עבר ל-GitHub Copilot app modernization, אבל ההנחה היא Visual Studio, Copilot וקוד C#, ולכן בסביבה עם VB.NET לא כדאי לצפות ליותר מדי אוטומציה.
ua1[".NET Upgrade Assistant"] -->|"לא מומלץ, מפנה ל"| cp1["GitHub Copilot app modernization"]
cp1 --> pre1["הנחה: Visual Studio + Copilot + C#"]
pre1 -.->|"אם יש VB.NET"| vb1["לא לצפות ליותר מדי אוטומציה"]
איור 13: מרכז הכובד של הכלים עבר ל-Copilot app modernization, וסביבה שלא תואמת את ההנחות שלו דורשת התייחסות נפרדת.
5. אומדים את הקושי לפי סוג הפרויקט
נוטים לדבר על ה-migration מ-.NET Framework ל-.NET כעל דבר אחד, אבל בפועל כל סוג פרויקט הוא משחק בפני עצמו.
5.1 תחושת קושי גסה
| סוג | תחושת קושי | הנקודות המרכזיות |
|---|---|---|
| class library | נמוך~בינוני | תאימות API, תלויות, חלוקת target |
| console / batch / חלק מ-Windows Service | נמוך~בינוני | שיטת הפצה, תלות native, תצורה |
| WinForms / WPF | בינוני | נשאר Windows-only, designer, UI צד שלישי, סביבת BinaryFormatter |
| ASP.NET MVC / Web API | בינוני~גבוה | מעבר app model ל-ASP.NET Core, authentication, session, תצורה, DI |
| ASP.NET Web Forms | גבוה | הפרש גדול במודל המסך, הנחת החלפת שכבת UI |
| WCF client | בינוני | החלפת חבילה, חוזה, תצורה |
| WCF server | גבוה | CoreWCF או תכנון מחדש עם gRPC / HTTP API |
| ביצוע בו-זמני של EF6 → EF Core | גבוה | ORM שונה לגמרי, הבדל התנהגות, היסטוריית migration |
5.2 המפתח ב-class library הוא איך חותכים את הגבול המשותף
class library יחסית קלה להעברה. עם זאת, זה נכון רק כשהספרייה באמת מופרדת כמו ספרייה.
תלויות מהסוג הבא מעלות את הקושי:
- נוגעים ב-
System.Web - מסתכלים ישירות ב-
HttpContext.Current - כוללים טיפוסי WPF / WinForms ב-API הציבורי
- נשענים יתר על המידה על Windows API כמו Registry, WMI, EventLog
- תלויים ב-
AppDomainאו ב-Remoting
אם אפשר להוציא רק business logic, זה קל, ואם נושאים גם את ה-app model, זה כבד — כך קל יותר להבין.
flowchart TB
accTitle: איך רואים את כובד ה-class library
accDescr: ספרייה שבה אפשר להוציא רק business logic קלה ל-migration, ואילו ספרייה שנושאת גם app model דרך System.Web, טיפוסי UI, Windows API ו-AppDomain כבדה.
lib1["בוחנים class library"] -->|"אפשר להוציא רק לוגיקה"| lt1["migration קל"]
lib1 -->|"נושאת app model"| hv2["migration כבד"]
hv2 -.-> sm1["תלות ב-System.Web, טיפוסי UI, Windows API וכדומה"]
איור 14: הקושי של הספרייה נקבע לא לפי מספר השורות, אלא לפי כמות ה-app model שהיא נושאת.
5.3 WinForms / WPF קלים ל-migration, אבל נשארים Windows-only
WinForms ו-WPF יכולים לעבור ל-.NET. עם זאת, שניהם נשארים framework-ים Windows-only.
טעות בציפייה כאן מסוכנת.
- מה שמשתפר
- עולים על runtime, שפה ו-SDK-style של modern .NET
- קל יותר להתאים CI/CD וניהול חבילות לימינו
- זוכים לחלק משיפורי הביצועים והתחזוקה
- מה שלא משתנה
- היות Windows-only
- נשארות בעיות תאימות של בקרות UI ורכיבים ל-design-time
- בעיות ActiveX / COM / native DLL לא נעלמות
בנוסף, יש מקרים שבהם WinForms / WPF דורשים בדיקת השפעת BinaryFormatter. בפרט, כשקשור ל-clipboard, drag & drop, ResX, וסריאליזציה ב-design-time עם custom type, זה נוטה לצוף כשמעלים את ה-target ל-.NET 9 ומעלה.
flowchart TB
accTitle: הציפייה ל-migration של WinForms / WPF
accDescr: WinForms ו-WPF יכולים לעבור ל-.NET ולעלות על הזרימה של ה-runtime, השפה וה-SDK-style המודרני, אבל נשארים Windows-only, וגם נדרשת לפעמים בדיקת BinaryFormatter.
ui1["migration של WinForms / WPF"] --> gain1["עולים על היתרונות של modern .NET"]
ui1 --> keep1["נשארים Windows-only"]
ui1 -.-> bfc1["נדרשת לפעמים בדיקת BinaryFormatter"]
איור 15: ה-UI השולחני יכול לעבור, אבל התכונה של להיות Windows-only ממשיכה איתו.
5.4 ASP.NET Framework זה לא “migration של runtime” אלא “migration של app model”
המעבר מ-ASP.NET Framework ל-ASP.NET Core מוגדר גם במדריך של Microsoft כ-non-trivial. זה לא רק כי שמות ה-API משתנים, אלא כי הארכיטקטורה שמונחת ביסוד שונה.
המקומות שבהם ההבדל בולט הם בערך אלה:
- Hosting model
- Middleware pipeline
- Request processing model
- Session / Cache
- Authentication / Authorization
- Configuration
- Dependency Injection
- Logging / Monitoring
מה שכדאי לבדוק קודם באפליקציית ASP.NET Framework הוא בערך אלה:
- מאיזה route / endpoint אפשר להעביר קודם
- האם אפשר להסיר תלות ב-
System.Webמ-shared library - איך מתאימים authentication / session / טיפול ב-exceptions / logs
- האם עוברים incremental בלי לעצור את ה-production
בפרט, באפליקציה גדולה, מעשי יותר לתכנן מראש בהנחת incremental migration.
flowchart TB
accTitle: migration של ASP.NET הוא migration של app model
accDescr: המעבר מ-ASP.NET Framework ל-Core הוא לא החלפת שמות API אלא migration של app model עם שינוי בארכיטקטורה שביסוד: hosting, middleware, authentication, configuration, DI. באפליקציה גדולה מעשי יותר להניח incremental migration.
an1["ASP.NET Framework → Core"] --> am1["הארכיטקטורה שביסוד משתנה"]
am1 --> pts1["hosting, middleware, authentication, configuration, DI"]
am1 -->|"באפליקציה גדולה"| inc1["תכנון בהנחת incremental migration"]
איור 16: ה-migration ב-ASP.NET הוא לא migration של runtime אלא של app model, וככל שההיקף גדול, ההנחה ההדרגתית טבעית יותר.
5.5 Web Forms נכנסים דרך פירוק אחריות, לא דרך “העברת נכסים”
Web Forms לא באותו app model כמו ASP.NET Core. לכן, באומדן, בטוח יותר לא להניח שאפשר להעביר את מסכי ה-UI כמו שהם.
בשטח, לרוב מתחילים מהפירוק הבא:
- הפרדה בין לוגיקת מסך ל-business logic
- פירוק האחריות שקבורה ב-
Page/UserControl/ViewState - הוצאת business logic וגישה לנתונים ל-shared library
- הרכבה מחדש של ה-UI במודל אחר כמו Razor Pages / MVC / Blazor
כלומר, בפרויקט Web Forms, מה שחשוב הוא האם יש תכנית פירוק אחריות לפני migration של ה-runtime.
flowchart TB
accTitle: Web Forms נכנסים דרך פירוק אחריות
accDescr: Web Forms אינם באותו app model כמו ASP.NET Core, ולכן מפרידים בין לוגיקת מסך ל-business logic, מוציאים לוגיקה ל-shared library, ומרכיבים מחדש את ה-UI במודל אחר.
wf1["מסכי Web Forms"] --> sep1["הפרדה בין לוגיקת מסך ל-business logic"]
sep1 --> mv1["הוצאת הלוגיקה ל-shared library"]
mv1 --> re1["הרכבה מחדש של ה-UI במודל אחר"]
wf1 -.->|"הנחה של העברה כמו שזה"| ngw1["האומדן קורס"]
איור 17: מסתכלים על Web Forms לא כהעברת נכסים אלא כפירוק אחריות ואז הרכבה מחדש במודל אחר.
5.6 מפרידים בין חשיבה על WCF client ל-WCF server
כאן בטוח יותר לא לכרוך יחד.
WCF client
ל-WCF Client יש חבילת NuGet נתמכת ל-modern .NET. לכן, יש מקרים שבהם אם רק קוראים ל-WCF, זה לא כבד כמו שנראה.
WCF server
מצד שני, צד ה-host של שירות WCF שונה. במדריך של Microsoft מנחים בעיקר בשני מסלולים לצורך modernization:
- כיוון של שימוש ב-CoreWCF לשמירת תאימות ללקוחות קיימים
- כיוון של מעבר ל-RPC / HTTP מודרני כמו gRPC
עם זאת, CoreWCF לא מביא את כל WCF כמו שהוא — זו תת-קבוצה. מתאים לשמירת תאימות ללקוחות קיימים, אבל שינוי קוד ובדיקה הם הנחת יסוד.
flowchart TB
accTitle: WCF מוערך בנפרד ללקוח ולשרת
accDescr: ל-WCF client יש חבילה נתמכת ל-modern .NET ולעיתים זה לא כבד כמו שנראה, ואילו בצד השרת צריך לבחור מסלול: שמירת תאימות עם CoreWCF, שהוא תת-קבוצה, או תכנון מחדש עם gRPC.
wcf1["באיזה צד משתמשים ב-WCF"] -->|"client"| cl1["migration עם חבילה נתמכת"]
wcf1 -->|"server"| sv1["בוחרים מסלול"]
sv1 -->|"שמירת תאימות"| cw1["CoreWCF (תת-קבוצה)"]
sv1 -->|"תכנון מחדש"| gr1["gRPC / HTTP API"]
איור 18: לצד הקורא ולצד ה-host יש קושי ואפשרויות שונים, ולכן לא אומדים אותם יחד.
6. סורקים טכנולוגיות שאי אפשר להשתמש בהן ב-.NET, או שקל להיתקע בהן כמו שהן
זו נקודה שחייבים לבצע לפני שמתחילים. ל-Microsoft יש רשימה של טכנולוגיות שהיו זמינות ב-.NET Framework אבל לא זמינות ב-.NET 6 ומעלה.
6.1 טכנולוגיות שנוטות להיות red flag
| טכנולוגיה | המצב ב-.NET | איך לחשוב על זה |
|---|---|---|
AppDomain.CreateDomain וכדומה — יצירת AppDomain |
לא נתמך | חושבים על isolation דרך process נפרד / container / AssemblyLoadContext |
| .NET Remoting | לא נתמך | תכנון מחדש ל-IPC, HTTP, gRPC, Socket, Pipe וכדומה |
| CAS / Security Transparency | לא נתמך כגבול אבטחה | חושבים על OS / container / הפרדת הרשאות |
System.EnterpriseServices (COM+) |
לא נתמך | מפרידים או מחליפים את התכנון שמניח COM+ |
| Workflow Foundation | לא נתמך | חושבים באומדן נפרד, כולל חלופות כמו CoreWF |
| WCF server | לא built-in כמו שהוא | בוחרים בין CoreWCF ל-gRPC |
| BinaryFormatter | מ-.NET 9 ואילך, המימוש תמיד זורק exception | מעבר ל-serializer אחר, audit של ResX / clipboard / drag & drop |
6.2 AppDomain הוא “גם אם חלק מה-API נשאר, היצירה עניין נפרד”
סביבת AppDomain קצת מסובכת. גם ב-.NET, חלק ממשטח ה-API נשאר, אבל השימוש של יצירת AppDomain חדש ו-isolation בו לא נתמך.
לכן, אם השתמשו ב-AppDomain לצרכים הבאים, נדרש תכנון מחדש:
- isolation של plugin
- ביטול טעינה דינמית
- isolation של קוד באמון חלקי
- הפרדת סביבת הרצה זמנית
מה שכדאי לבדוק לפני ה-migration הוא לא רק האם המילה AppDomain מופיעה, אלא לשם מה השתמשו ב-AppDomain.
flowchart TB
accTitle: השימוש ב-AppDomain קובע את ההחלטה
accDescr: למרות שחלק מה-API נשאר, השימוש ביצירת AppDomain חדש ו-isolation בו לא נתמך, ולכן מבחינים בין שימוש בטיפוס בלבד לבין isolation בפועל. ל-isolation נדרש תכנון מחדש ב-process נפרד או AssemblyLoadContext.
ad1["AppDomain מופיע"] -->|"רק שימוש בטיפוס או במידע"| okc1["ברוב המקרים אפשר להמשיך כמו שזה"]
ad1 -->|"יצירה ו-isolation"| rd1["נדרש תכנון מחדש"]
rd1 --> alt1["process נפרד / container / AssemblyLoadContext"]
איור 19: לא לפי הימצאות המילה אלא לפי “לשם מה השתמשו בזה” נקבע אם נדרש תכנון מחדש.
6.3 Remoting עמוק ממה שנראה
מלבד Remoting עצמו, גם delegate אסינכרוני כמו קריאה ל-BeginInvoke() / EndInvoke() עלול להיכנס להיקף ההשפעה. זה לא Remoting עצמו, אבל הוא לא נתמך ב-modern .NET, ולכן צריך לעשות לו inventory לפני ה-migration.
לכן, בעת החיפוש, בטוח יותר לבדוק גם אלה יחד:
System.Runtime.RemotingMarshalByRefObjectRealProxyBeginInvoke(/EndInvoke(
6.4 BinaryFormatter צף לפתע לפי target version
BinaryFormatter, ככל שבסיס הקוד ישן יותר, נוטה להשתמש בו בלי מודעות.
- נתונים שנשמרים
- cache
- שמירת session
- מצב plugin
- clipboard / drag & drop
- ResX
- סביבת ה-designer של WinForms / WPF
מ-.NET 9 ואילך, BinaryFormatter לא כולל מימוש ב-runtime, וה-API תמיד זורק PlatformNotSupportedException.
כלומר, זה לא נושא ל”נחשוב על זה אחר כך”, אלא נקודה שדורשת audit מוקדם ברגע שקובעים את ה-target version.
flowchart TB
accTitle: המסלול שבו BinaryFormatter צף
accDescr: BinaryFormatter נוטה להיות בשימוש בלי מודעות דרך נתונים שנשמרים, ResX וכדומה, ומ-.NET 9 ואילך ה-API זורק תמיד exception, ולכן זו נקודה שדורשת audit מוקדם ברגע שקובעים את ה-target version.
hid2["שימוש בלי מודעות (ResX, clipboard וכדומה)"] --> tg1["קביעת target ל-.NET 9 ואילך"]
tg1 --> ex1["ה-API תמיד זורק exception"]
ex1 --> aud1["דורש audit מוקדם ברגע ההחלטה"]
איור 20: BinaryFormatter משפיע ברגע שקובעים את ה-target, ולכן לא דוחים אותו — מבצעים audit מוקדם.
6.5 מונחי חיפוש שכדאי לעשות עליהם grep קודם
עוד לפני שמתחילים, חיפוש ה-solution כולו לפי המונחים הבאים כבר משנה משמעותית את התמונה.
System.Web
HttpContext.Current
System.Runtime.Remoting
MarshalByRefObject
AppDomain
BinaryFormatter
ServiceHost
ChannelFactory
System.EnterpriseServices
Workflow
packages.config
web.config.install.xdt
install.ps1
DllImport
AxInterop
Microsoft.Office.Interop
זה לא אומר שאם נמצא ולו מונח אחד, זה כישלון מיידי. זו מפה לדעת היכן אפשר לעבור במסלול הסטנדרטי, והיכן זה מסלול נפרד.
פקודת החיפוש בפועל
הכלי יכול להיות rg (ripgrep) או PowerShell.
# PowerShell. חיפוש מרוכז בכל ה-solution
Get-ChildItem -Recurse -Include *.cs,*.vb,*.config,*.csproj,*.vbproj |
Select-String -Pattern 'BinaryFormatter|System\.Runtime\.Remoting|MarshalByRefObject|AppDomain\.CreateDomain' |
Select-Object Path, LineNumber, Line
# ripgrep. כשרוצים קודם לקבל תחושה לפי מספר התוצאות
rg -n --stats "BinaryFormatter|System\.Runtime\.Remoting|AppDomain\.CreateDomain" --glob "*.cs" --glob "*.vb"
מה עושים אחרי שנמצא
החיפוש הוא רק הכניסה, ההחלטה שאחריו היא העבודה האמיתית. לכל מונח נקבע מקום שממנו ממשיכים לבדוק.
| מונח שנמצא | מה בודקים אחר כך | הפתרון |
|---|---|---|
System.Web / HttpContext.Current |
האם אפשר להוציא את זה מחוץ ל-shared library. האם צד הקריאה יכול לארוז ל-DTO | הסרה עם האסטרטגיה בפרק 8.5 |
System.Runtime.Remoting / MarshalByRefObject / BeginInvoke |
לשם מה נועדה קריאת ה-remote. בין processes, בין מכונות, או רק קריאה אסינכרונית פשוטה | תכנון מחדש עם IPC, HTTP, gRPC, מבוסס Task |
AppDomain |
האם משתמשים רק בשם הטיפוס, או מבודדים עם CreateDomain |
אם המטרה isolation, process נפרד או AssemblyLoadContext (6.2) |
BinaryFormatter |
האם צריך לקרוא אחר כך את הנתונים שנכתבו. האם משתמשים בזה בעקיפין דרך ResX או clipboard | מעבר ל-serializer אחר, ותכנון לקריאה מחדש של הנתונים הקיימים (6.4) |
ServiceHost / ChannelFactory |
צד ה-host או צד ה-client | הערכה נפרדת לפי 5.6 |
packages.config / install.ps1 / web.config.install.xdt |
מה נכתב מחדש בזמן ההתקנה | ה-gotcha מ-4.2. מחזיקים את ההגדרה במפורש |
DllImport / AxInterop / Microsoft.Office.Interop |
הנחת מספר הביטים וכל קובצי ה-runtime הנדרשים | בדיקת bitness בפרק 9.4 |
עד כאן, מקבלים לא “האם אפשר לעבור”, אלא רשימה של “מה במסלול הסטנדרטי, ומה דורש אומדן נפרד”. זה בדיוק סוג הרשימה שרוצים לפני שמתחילים.
flowchart TB
accTitle: מהחיפוש לרשימה
accDescr: החיפוש הוא הכניסה, ולכל מונח שנמצא בודקים את מקום ההמשך, וכך זה הופך לרשימה של מסלול סטנדרטי מול אומדן נפרד. מציאה בלבד אינה כישלון מיידי.
grep1["חיפוש ה-solution לפי מונחים"] --> nxt1["בדיקת המשך לכל מונח"]
nxt1 --> map1["הופך לרשימה של מסלול סטנדרטי מול אומדן נפרד"]
grep1 -.-> note2["מציאה בלבד אינה כישלון מיידי"]
איור 21: החיפוש עצמו הוא הכניסה, וכשמקדמים אותו עד ההחלטה, מתקבלת בדיוק הרשימה הרצויה לפני שמתחילים.
7. קובעים עד כמה מקבלים הנחת Windows-only
טעות נפוצה ב-migration היא “אם עברנו ל-.NET, זה נהיה cross-platform”. אין קסם כזה. אם האפליקציה קשורה עמוק ל-Windows, גם אחרי ה-migration היא נשארת Windows-only כרגיל.
7.1 להישאר Windows-only ב-migration הוא ריאלי לגמרי
ל-Microsoft יש Windows Compatibility Pack, שמאפשר לגשת מ-modern .NET להרבה Windows API כמו Registry, WMI, EventLog, Windows Service ו-Directory Services.
הקיום שלו משמעותי בשטח.
- רוצים קודם לעבור ל-modern .NET
- אבל בינתיים לא יוצאים מ-Windows
- אז רוצים לקבל בינתיים את תלות ה-Windows API
בסביבה כזו, זו אפשרות חזקה.
המטרה הראשונה של ה-migration לא חייבת להיות הפיכה ל-cross-platform.
7.2 עם זאת, גם Windows-only API הוא חוב שישפיע בהמשך
עצם קיומו של Windows Compatibility Pack לא אומר שהכול בטוח.
- רוצים לשים על Linux container
- רוצים להריץ בהנחת Kubernetes
- גם מפתחי macOS / Linux רוצים להריץ את אותו build
- בעתיד, רוצים להפחית Windows VM בענן
אם יש מטרות כאלה, בטוח יותר לחשוף כבר עכשיו את תלות ה-Windows API.
flowchart TB
accTitle: מקום השימוש ב-Windows Compatibility Pack והחוב שמאחוריו
accDescr: אם בינתיים נשארים על Windows, Windows Compatibility Pack מאפשר לקבל בינתיים את תלות ה-Windows API ולעבור ל-modern .NET. אם שואפים בעתיד ל-container או cloud, התלות היא חוב שישפיע בהמשך ולכן חושפים אותו כבר עכשיו.
now1["רוצים קודם לעבור ל-modern .NET"] -->|"בינתיים נשארים על Windows"| pack1["Windows Compatibility Pack מקבל בינתיים את התלות"]
pack1 -.->|"אם שואפים בעתיד ל-container / cloud"| debt1["התלות היא חוב שישפיע בהמשך"]
debt1 --> vis1["חושפים אותו כבר עכשיו"]
איור 22: Windows Compatibility Pack הוא גשר ריאלי, אבל התלות שלו עלולה להיות חוב לפי המטרה, ולכן חושפים אותה מראש.
7.3 System.Drawing.Common קל במיוחד לטעות בהבנתו
System.Drawing.Common הוא, מ-.NET 6 ואילך, ספרייה Windows-only.
אם יש קוד שמשתמש בעיבוד תמונות או ציור טקסט, צריך לקבוע קודם באיזה כיוון הולכים.
- ממשיכים לתפעל על Windows
- בעתיד רוצים להריץ גם על Linux / macOS
באפשרות הראשונה יש מקרים שאפשר להישאר כמו שזה בינתיים. באפשרות השנייה, צריך לכלול מראש בתכנון ה-migration החלפה כמו SkiaSharp או ImageSharp.
flowchart TB
accTitle: ההסתעפות של System.Drawing.Common
accDescr: System.Drawing.Common הוא Windows-only מ-.NET 6 ואילך. אם ממשיכים על Windows אפשר להישאר כמו שזה בינתיים, ואם רוצים להריץ גם על Linux או macOS צריך לכלול מראש החלפה כמו SkiaSharp או ImageSharp.
sd1["משתמשים ב-System.Drawing.Common"] -->|"ממשיכים על Windows"| ok4["אפשר להישאר כמו שזה בינתיים"]
sd1 -->|"רוצים גם Linux / macOS"| repl1["החלפה ל-SkiaSharp / ImageSharp"]
repl1 --> plan2["כוללים זאת מראש בתכנון ה-migration"]
איור 23: מכיוון ש-System.Drawing.Common הוא Windows-only, אופן הטיפול בו נחלק כבר בהתחלה לפי נקודת היעד.
7.4 סימנים ייצוגיים לקיבוע ל-Windows
כשיש הפניות או API-ים מהסוג הבא, בטוח יותר לאמוד “לפחות בהתחלה נשארים Windows-only”.
Microsoft.Win32.RegistrySystem.ManagementSystem.Diagnostics.EventLogSystem.ServiceProcessSystem.DirectoryServicesSystem.DrawingDllImport/ P/Invoke- הפניית COM
AxInterop.*Microsoft.Office.Interop.*
8. אופן הוצאת ה-shared library קובע את הקושי
ב-solution גדול, אין הגזמה לומר שהצלחת ה-migration נקבעת לפי איך חותכים את ה-shared library.
8.1 קודם מסווגים
נוח יותר לסווג ספריות לשלושה סוגים עיקריים:
- business logic / domain logic טהורה
- שכבת ביניים שתלויה מעט ב-app model
- שכבה שצמודה ל-UI / Web / Windows API
מבין אלה, מה שכדאי להעביר קודם הוא 1.
- חישוב
- הכרעת חוקים
- DTO / חוזה
- domain service
- המרת נתונים פשוטה
אם מוציאים את זה נקי, הקושי הכולל יורד בבת אחת.
flowchart TB
accTitle: שלוש הקטגוריות של הספריות וסדר ההעברה
accDescr: מחלקים ספריות ל-business logic טהורה, שכבת ביניים תלויית app model, ושכבה צמודה ל-UI ול-Windows API. מה שכדאי להעביר קודם הוא ה-business logic הטהורה.
c1["business logic טהורה"] -->|"מעבירים ראשונה"| ez1["אם מוציאים נקי, הקושי יורד"]
c2["שכבת ביניים תלויית app model"] -->|"אחריה"| ez1
c3["שכבה צמודה ל-UI / Windows API"] -->|"אחרונה"| ez1
איור 24: מחלקים את הספריות לשלוש שכבות, ומצילים בהדרגה מהלוגיקה הטהורה.
8.2 netstandard2.0 הוא עדיין גשר תקף
לפי ההנחיה של Microsoft, אם יש shared library שצריכה להתקיים גם בצד .NET Framework, הבסיס הוא לחשוב קודם על .NET Standard 2.0.
חשוב כאן שתי נקודות:
- .NET Framework לא תומך ב-
.NET Standard 2.1 - אם רוצים להפנות ל-shared library גם מהישן וגם מהחדש, 2.0 הוא הפתרון המעשי ברוב המקרים
flowchart TB
accTitle: הגשר בשם netstandard2.0
accDescr: .NET Framework לא תומך ב-.NET Standard 2.1, ולכן אם רוצים להפנות ל-shared library גם מהישן וגם מהחדש, .NET Standard 2.0 הוא הפתרון המעשי.
fw1["צד .NET Framework"] -->|"יכול להפנות"| ns1["shared library ב-netstandard2.0"]
mn1["צד modern .NET"] -->|"יכול להפנות"| ns1
ns21["netstandard2.1"] -.->|"Framework לא תומך"| fw1
איור 25: כגשר שנגיש גם מהישן וגם מהחדש, הפתרון המעשי הוא 2.0 ולא 2.1.
8.3 מה משתנה כשעוברים ל-netstandard2.0
| מדיניות | מה משתנה | מתי מתאים | הערה |
|---|---|---|---|
המרה ל-netstandard2.0 |
קל להפנות גם מהישן וגם מהחדש | business logic טהורה, חוזה משותף, utilities | לא ניתן לטעון API ייחודי ל-app model |
multi-target (למשל: net48;net10.0) |
שומרים על קוד משותף ובכל זאת מחזיקים הפרש לפי סביבה | ספרייה עם הפרש קטן בין סביבות | מוסיף התניות וניהול build |
המרה ישירה ל-net10.0 בלבד |
הכי נקי בעתיד | שכבה חדשה שלא צריכה תלות בין ישן לחדש | לא ניתן להפניה מ-.NET Framework |
8.4 מצב תאימות אינו כל-יכול
ל-.NET Standard 2.0 יש מצב תאימות שמאפשר להפנות לספריות .NET Framework. עם זאת, זה לא קסם שגורם לכול לפעול באופן שקוף.
לדוגמה, ספרייה שמניחה API ייחודי ל-app model כמו WPF קשה כרגיל. כלומר, גם אם אומרים shared library, חשוב להצר רק לאחריות שבאמת ניתנת לשיתוף.
8.5 בספריות מסוג ASP.NET, המפתח הוא האם אפשר להסיר את System.Web
כשמבצעים incremental migration ל-ASP.NET Framework, אם ל-shared library יש תלות ישירה ב-HttpContext.Current או ב-System.Web, זה די קשה.
האסטרטגיה הבסיסית כאן היא אחת מאלה:
- דחיפת התלות ב-
System.Webאל מחוץ לממשק - שינוי לקבלת מידע שמקורו ב-
HttpContextכ-DTO - שימוש ב-adapter בתקופת המעבר
- אם עדיין קשה, הסרה הדרגתית עם multi-target
8.6 מעלים ספריות בשיטת leaf-first
במדריך ה-incremental migration של ASP.NET כתוב במפורש שמעלים supporting library ב-postorder depth-first, כלומר מהעלים ראשון.
זה לא מוגבל ל-Web, אלא יעיל מאוד גם ב-solution-ים רגילים.
- מכיוון שהיעדים שהם תלות עלו קודם, קל יותר לראות את השכבה העליונה
- קל יותר לבודד בעיות תאימות
- קל יותר לבדוק ברמת ספרייה
flowchart TB
accTitle: מעלים בשיטת leaf-first
accDescr: אם מעלים את ספריות התמיכה מהעלים של התלות ראשון, שכבת הביניים אחריהן, ולבסוף שכבת האפליקציה העליונה, קל יותר לבודד בעיות ולבדוק ברמת ספרייה.
lf1["מעלים ראשית את ספריות העלים"] --> lf2["מעלים את שכבת הביניים שסיימה"]
lf2 --> lf3["לבסוף שכבת האפליקציה העליונה"]
lf1 -.-> loc1["אפשר לבודד ולבדוק בעיות"]
איור 26: כשמעלים מהעלים ראשון, עד שמגיעים לשכבה העליונה, הבסיס כבר מוכן.
9. סורקים NuGet / תלויות חיצוניות / רכיבי צד שלישי
אם עושים את זה ברשלנות, זה יכאב הכי הרבה בשלב השני של ה-migration.
9.1 נוח יותר לחלק את התלויות לארבעה סוגים
- חבילת NuGet ציבורית
- חבילה פרטית פנימית / internal library
- הפניית DLL מקומית
- COM / ActiveX / native DLL / SDK
מתוכם, לא מספיק להסתכל רק על 1. מה שבאמת מסוכן הם 3 ו-4.
flowchart TB
accTitle: ארבע קטגוריות התלות ורמת הסיכון
accDescr: התלויות מחולקות לחבילת NuGet ציבורית, חבילה פנימית, הפניית DLL מקומית, ו-COM/ActiveX/native DLL. המסוכנים באמת הם ההפניה המקומית וה-COM/native.
dp1["inventory של תלויות"] --> k1["NuGet ציבורית / חבילה פנימית"]
dp1 --> k3["הפניית DLL מקומית"]
dp1 --> k4["COM / ActiveX / native DLL"]
k3 --> dg1["המסוכן באמת הוא הצד הזה"]
k4 --> dg1
איור 27: לא מספיק לספור NuGet ציבורי — קודם סורקים DLL מקומי ו-COM / native.
9.2 מה בודקים בכל תלות
לגבי כל תלות, לפחות בודקים עד כאן:
- האם היא ממוקדת ל-modern .NET
- האם היא תומכת ב-
PackageReference - האם אין בעיה עם SDK-style
- האם יש מגבלה ל-x86 / x64 / ARM64
- האם היא תלויה בכלי design-time או בתוסף Visual Studio
- האם היא מניחה install script / config transform
- האם התמיכה עדיין נמשכת
9.3 UI / דוחות / רכיבי design-time של צד שלישי — מאמדים בנפרד
ב-migration של WinForms / WPF / ASP.NET, זה משפיע משמעותית.
- Grid
- מנוע דוחות
- רכיב פלט PDF
- רכיב גרפים
- ספריית UI משולבת עם designer
- עטיפת ActiveX
אלה קשורים לא רק ב-runtime, אלא גם בתמיכה ב-design-time. אם באומדן ה-migration בודקים רק “האם זה מתקמפל”, בדרך כלל מפספסים אותם.
9.4 בודקים תמיד native DLL ו-bitness
גם אם נראה שרץ ב-AnyCPU בתקופת .NET Framework, ייתכן שבפועל יש תלות בדברים מהסוג הבא:
- COM שקבוע ל-
x86 - ActiveX ייעודי ל-32bit
- גרסה מסוימת של VC++ Runtime
- native DLL חתום
זו לא בעיה חדשה שצמחה עם המעבר ל-modern .NET, אלא מגבלה שהייתה קיימת מהתחלה, וצפה עכשיו. בדיוק בגלל זה שווה לחשוף אותה לפני ה-migration.
flowchart TB
accTitle: הצפת מגבלת ה-bitness
accDescr: גם אם נראה שרץ ב-AnyCPU, ייתכן תלות ב-COM קבוע ל-x86, ActiveX ל-32bit או גרסה מסוימת של VC++ Runtime. אלה מגבלות שהיו קיימות מהתחלה שצפות בגלל ה-migration.
any1["נראה שרץ ב-AnyCPU"] -.-> hid3["תלות ב-COM x86, ActiveX 32bit וכדומה"]
hid3 --> surf1["ה-migration גורם למגבלה הקיימת לצוף"]
surf1 --> pre2["חושפים אותה לפני ה-migration"]
איור 28: בעיית ה-bitness לא נוצרת על ידי ה-migration, אלא מגבלה קיימת שנעשית גלויה.
10. מטפלים ב-EF6, ב-serializer-ים ובנתונים כבעיה נפרדת
עדיף ככל האפשר להתייחס בנפרד ל-migration של ה-runtime ולתכנון מחדש של גישת הנתונים והסריאליזציה.
10.1 המעבר מ-EF6 ל-EF Core הוא לא שדרוג ישיר
גם במדריך ה-EF של Microsoft כתוב ש-EF Core הוא כתיבה מחדש מלאה של EF6, ואין נתיב שדרוג ישיר.
לכן, באפליקציה שמשתמשת ב-EF6, הסדר הבא ריאלי:
- קודם עוברים ל-modern .NET
- אם צריך, ממשיכים להריץ עם EF6 כמו שהוא
- אחר כך מעבירים ל-EF Core כפרויקט נפרד
לא לערבב בין runtime migration ל-ORM migration. רק זה כבר מוריד משמעותית את הקושי.
flowchart TB
accTitle: הפרדת ה-migration של EF6 מזה של runtime
accDescr: EF Core הוא כתיבה מחדש מלאה של EF6 בלי נתיב שדרוג ישיר, ולכן ריאלי לעבור קודם ל-modern .NET, להמשיך עם EF6 אם צריך, ורק אחר כך להעביר ל-EF Core כפרויקט נפרד.
e1["קודם עוברים ל-modern .NET"] --> e2["ממשיכים עם EF6 אם צריך"]
e2 --> e3["אחר כך מעבירים ל-EF Core כפרויקט נפרד"]
e1 -.->|"אם עושים בו-זמנית"| mix1["הבדלי התנהגות ה-ORM מתערבבים ומכבידים"]
איור 29: לא מריצים בו-זמנית runtime ו-ORM — מעשי יותר להשאיר את EF6 קודם ולהעביר את ה-runtime.
10.2 מה משתנה כשמשאירים EF6
- יתרון
- אפשר לדחות את ההפרש בשכבת גישת הנתונים
- אפשר להתמקד בהעברת business logic ו-app model
- “הבדל התנהגות של EF Core” לא מתערבב
- הערה
- מנקודת מבט של פיתוח חדש, EF Core הוא הבחירה העיקרית
- יש מגבלות נפרדות לצורת שימוש עם EF6 Designer / EDMX
10.3 EF6 מבוסס EDMX דורש התייחסות גם ב-design-time
בתיעוד EF6 כתוב ש-EF Designer לא נתמך ישירות בפרויקטי .NET / .NET Standard, או בפרויקטי .NET Framework ב-SDK-style.
באפליקציה מבוססת EDMX, צריך להתייחס בנפרד לשלוש נקודות אלה:
- האם זה פועל ב-runtime
- האם ה-Designer שמיש
- איך מטפלים בקוד שנוצר
אם יש שימוש נרחב ב-EDMX, בטוח יותר לכלול את זה באומדן כבר מההתחלה.
10.4 BinaryFormatter וסריאליזציה עצמית נוטים להיות תלות נסתרת
קל לפספס serializer-ים רק בעזרת חיפוש קוד.
- פורמט נתונים לשמירה
- מסרים
- cache
- חוזה WCF / SOAP ישן
- ResX
- clipboard / drag & drop
אלה קשורים גם לתאימות נתונים. כלומר, לא מספיק “האם ה-build עובר”, אלא נדרשת בדיקה עד להאם אפשר לקרוא נתונים ישנים.
11. כוללים גם תצורה, הפצה, תפעול ו-CI/CD ביעד ה-migration
יעד ה-migration הוא לא רק קוד המקור.
11.1 קובצי תצורה
בצד .NET Framework, לעיתים יש הרבה מוטמע ב-app.config / web.config.
- connection string
- custom config section
- הגדרת endpoint של WCF
- Binding Redirect
- diagnostics
- הגדרות שונות של ASP.NET
- תוצאת ה-transform בזמן התקנת חבילה
בצד modern .NET יש מקרים שבהם אופן החזקת התצורה ומסלול הטעינה משתנים. לכן “נטפל בקובצי התצורה אחר כך” מסוכן.
מה שעושים ראשון הוא inventory של התצורה.
- מה מוטמע בקובץ התצורה
- מה חובה בזמן הפעלת האפליקציה
- מה ההפרש בין סביבות
- מה הוזרק אוטומטית על ידי NuGet או installer
11.2 שיטת ההפצה
גם צורת ההפצה נבדקת לפני שמתחילים.
- תחת IIS
- Windows Service
- Scheduled Task
- ClickOnce / MSI / installer עצמאי
- הנחת שרת מקומי
- מה מתאים יותר — self-contained או framework-dependent
גם אם גוף ההרצה עובר, אם מנגנון ההפצה וההפעלה נשארים בהנחה ישנה, זה נתקע בסוף.
11.3 לוגים, ניטור, נהלי תפעול
גם סביב התפעול קל לפספס.
- האם מבוסס Windows Event Log
- האם מסתכלים על Performance Counter
- האם ניטור מבוסס WMI
- האם חשבון השירות וההרשאות קבועים
- האם יעד פלט הלוגים מניח קובץ מקומי
קורה בהחלט שהקוד רץ אחרי ה-migration, אבל התפעול לא מסתובב.
11.4 CI/CD ו-build agent
לפני ה-migration, בודקים גם את אלה:
- האם ה-
.NET SDKהנדרש נכנס ל-build agent - מה עושים עם pipeline שמניח
nuget.exe/msbuild.exe - האם עוברים בסיס
dotnetCLI - איך מעדכנים את עבודות הריצת הבדיקות, coverage ו-publish
- האם תבנית פנים-ארגונית או reusable pipeline מניחים צורה ישנה
עובד בסביבה המקומית של האדם אבל ה-CI מת זו תופעה נפוצה ב-migration.
flowchart TB
accTitle: יעד ה-migration הוא לא רק הקוד
accDescr: גם אם גוף ההרצה עובר, אם קובצי התצורה, שיטת ההפצה, הלוגים והניטור, וה-CI/CD ו-build agent נשארים בהנחה ישנה, זה נתקע בסוף. לכן כל אלה נכללים ביעד ה-migration.
code1["migration של קוד המקור"] --> also1["זה לא מסתיים בזה בלבד"]
also1 --> cfg1["קובצי תצורה ושיטת הפצה"]
also1 --> ops1["לוגים, ניטור, נהלי תפעול"]
also1 --> ci1["CI/CD ו-build agent"]
ci1 -.-> gotcha1["עובד מקומית אבל ה-CI מת"]
איור 30: רק כשכוללים תצורה, הפצה, תפעול ו-CI/CD, זה נחשב שסופרו כל יעדי ה-migration.
12. אופן ההתקדמות המעשי של ה-migration
לאור הסוגיות עד כאן, האופן המעשי להתקדם נופל בדרך כלל לצורה הבאה.
12.1 קודם מכינים את צד ה-Framework הנוכחי
- עלייה ל-.NET Framework 4.7.2 ומעלה, אם אפשר ל-4.8.1
- עדכון התלויות
- בדיקה מחדש של
packages.config - ככל האפשר, מעבר ל-
PackageReferenceול-SDK-style - אימות שהאפליקציה הנוכחית רצה תקין במצב הזה
רק ביצוע השלב הזה מצמצם משמעותית את ה-gap בשלב הבא.
12.2 מצילים קודם shared library
בשלב הבא, מעבירים business logic וחוזים משותפים ל-netstandard2.0 או ל-multi-target.
סדר ההעלאה הבסיסי הוא leaf-first.
12.3 מחליפים אסטרטגיה לגוף האפליקציה לפי app model
- class library / console / חלק מהשירותים יחסית קל להתקדם ישירות
- WinForms / WPF modernization תוך שמירה על Windows-only
- ASP.NET MVC / Web API אם קטן — בבת אחת, אם כבד — incremental
- Web Forms בהנחת החלפת מסכי ה-UI, מוציאים תחילה לוגיקה משותפת
- WCF server קובעים קודם — שמירת CoreWCF, או תכנון מחדש עם gRPC
12.4 שומרים על “לא לעשות הכול בבת אחת”
בפרט, מה שכדאי להימנע ממנו הוא שילובים מהסוג הבא:
- runtime migration + החלפה כוללת ל-ORM
- runtime migration + שינוי תשתית authentication
- runtime migration + מעבר מלא ל-cloud
- runtime migration + שינוי תשתית monitoring
- runtime migration + חידוש תשתית UI
גם אם כולם נדרשים בסופו של דבר, לא לצבור אותם באותו sprint לרוב מוביל להצלחה.
12.5 פועלים רק אחרי שיש בדיקות ו-baseline
לפחות, כדאי להכין את זה לפני שמתחילים לפעול:
- unit tests
- integration tests לזרימות עסק מרכזיות
- אימות “תמונת מצב” למסכים / API-ים ייצוגיים
- baseline ביצועים
- שיטת אימות לוגים מרכזיים
- נוהל rollback
להתקדם במצב שבו אי אפשר לזהות “מה נשבר” אחרי ה-migration, מסוכן למדי.
flowchart TB
accTitle: מה מכינים לפני שמתחילים לפעול
accDescr: אם מכינים בדיקות ו-baseline, לוגים ונהלי rollback לפני שמתחילים לפעול ב-migration, אפשר לזהות מה נשבר אחריו. התקדמות בלי הכנה מסוכנת.
prep1["הכנת בדיקות ו-baseline"] --> mv2["רק אז מתחילים לפעול ב-migration"]
mv2 --> det1["אפשר לזהות מה נשבר"]
non2["התקדמות בלי הכנה"] -.-> risk1["אי-זיהוי במקרה תקלה — מסוכן"]
איור 31: ה-migration נקבע לפי ההכנה שלפני הפעולה, ורק עם baseline אפשר לזהות מה נשבר.
13. צ’קליסט לפני שמתחילים
מונח כאן בצורה שאפשר להדביק ישירות לניהול פרויקט.
13.1 מדיניות
- אפשר להסביר במשפט אחד למה עושים את ה-migration
- נקבע האם נקודת היעד הפעם היא modern .NET Windows-only, או cross-platform בעתיד
- נקבעה גרסת .NET היעד
- נקבע מה לא נכלל בהיקף הזה (הפיכה ל-EF Core, חידוש authentication, מעבר מלא ל-cloud וכדומה)
13.2 הכנת צד .NET Framework הנוכחי
- הועלה ל-.NET Framework 4.7.2 ומעלה, אם אפשר ל-4.8.1
- התלויות עלו לגרסה עדכנית
- נסרק קיום
packages.config - נבדקה היתכנות המרה ל-
PackageReference - נבדקה היתכנות מעבר ל-SDK-style
- האפליקציה הנוכחית מסוגלת ל-build, הפעלה ובדיקה במצב הזה
13.3 סוג האפליקציה ובחירת טכנולוגיה
- הקושי חולק לפי סוג — class library / desktop / Web / WCF וכדומה
- מובן ש-WinForms / WPF נשארים Windows-only
- מובן ש-ASP.NET Framework הוא migration של app model ל-ASP.NET Core
- הוכללה באומדן החלפת שכבת UI ל-Web Forms
- WCF הוערך בנפרד ל-client ול-server
13.4 טכנולוגיות לא נתמכות / API-ים שדורשים תשומת לב
- נסרקה תלות ב-
AppDomain - נסרקו Remoting /
MarshalByRefObject/BeginInvoke/EndInvoke - נסרקו CAS / Security Transparency / COM+ / WF
- נסרקה תלות ב-BinaryFormatter
- נסרקה תלות ב-
System.Web
13.5 תלות Windows-only
- נסרק שימוש ב-Registry, WMI, EventLog, Windows Service, Directory Services
- נסרק שימוש ב-
System.Drawing.Common - נסרקו COM / ActiveX / Office Interop / P/Invoke / native DLL
- נבדקה מגבלת x86 / x64 / ARM64
13.6 shared library וגישה לנתונים
- shared library סווגו לbusiness logic / שכבה צמודה ל-app model
- נסרק מה ניתן להמיר ל-
netstandard2.0 - נסרקו ספריות שדורשות multi-target
- נקבע האם אפשר להשאיר EF6 ולהעביר קודם רק את ה-runtime
- נבדקה תלות EDMX / Designer
13.7 תפעול ו-build
- בוצע inventory של קובצי תצורה
- נבדקה שיטת ההפצה (IIS / Service / MSI / ClickOnce וכדומה)
- נבדקו הנחות logs / monitoring / הרשאות / חשבון הרצה
- נבדק האם נדרש עדכון ל-CI/CD ול-build agent
- הוכן נוהל rollback
14. סיכום
ב-migration מ-.NET Framework ל-.NET, מה שחשוב הוא לא “באיזו פקודה מעבירים”, אלא לזהות לפני שמתחילים מה עובר כמו שהוא ומה בעיה נפרדת.
אם מרכזים לנקודות, הנה שש אלה:
- מכינים את צד .NET Framework לפני ה-migration
- מחלקים את הקושי לפי app model
- סורקים קודם טכנולוגיות שאי אפשר להשתמש בהן
- קובעים עד כמה מקבלים הנחת Windows-only
- קובעים איך חותכים את ה-shared library
- לא לוקחים יותר מדי בבת אחת מ-ORM, authentication ו-migration ל-cloud
השבוע הראשון של ה-migration משנה משמעותית את דיוק האומדן. במילים אחרות, אם מפרקים את הסוגיות באותו שבוע, המחצית השנייה מתקרבת הרבה יותר לפיתוח רגיל.
flowchart TB
accTitle: השימוש בשבוע הראשון
accDescr: אם בשבוע הראשון מפרקים את הסוגיות של מה עובר כמו שהוא ומה בעיה נפרדת, דיוק האומדן עולה והמחצית השנייה קרובה יותר לפיתוח רגיל.
wk1["פירוק הסוגיות בשבוע הראשון"] --> est1["דיוק האומדן עולה"]
est1 --> nor1["המחצית השנייה קרובה יותר לפיתוח רגיל"]
wk1 -.-> pts2["הפרדה בין מה שעובר כמו שהוא למה שבעיה נפרדת"]
איור 32: האם השבוע הראשון מנוצל ל-inventory, קובע את דיוק ה-migration כולו.
“בואו פשוט ננסה לשנות ל-net10.0” הוא לא רעיון רע לצורך חקירה.
עם זאת, כ-migration בפועל ל-production, יש מה לבדוק לפני זה. זה בדיוק מה שסידרנו במאמר הזה.
15. מקורות
- תנאי סף להעברת קוד
- סקירת ההעברה מ-.NET Framework ל-.NET
- טכנולוגיות .NET Framework שאי אפשר להשתמש בהן מ-.NET 6 ואילך
- מהי GitHub Copilot app modernization
- התקנת GitHub Copilot app modernization
- סקירת .NET Upgrade Assistant - כיום לא מומלץ, ומופנים ל-GitHub Copilot app modernization.
- מדיניות התמיכה הרשמית של .NET
- מחזורי החיים של Microsoft .NET ו-.NET Core - רשימת תאריכי תחילה וסיום תמיכה לכל גרסה.
- מדיניות התמיכה הרשמית של .NET Framework
- מעבר ASP.NET Framework ל-ASP.NET Core בעזרת כלים
- מעבר מ-ASP.NET Framework ל-ASP.NET Core
- Get started with incremental ASP.NET to ASP.NET Core migration
- Use the Windows Compatibility Pack to port code to .NET
- .NET Standard
- Cross-platform targeting for .NET libraries
- מעבר מ-packages.config ל-PackageReference
- PackageReference in project files
- מדריך migration של BinaryFormatter
- מדריך migration של BinaryFormatter עבור Windows Forms
- WCF Client Support Policy
- CoreWCF Support Policy
- Why migrate WCF to ASP.NET Core gRPC
- Port from EF6 to EF Core
- חידושים ב-EF6
מאמרים קשורים
מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.
למה arguments נשברים — כללי command-line arguments ב-Windows
Windows מעביר ל-CreateProcess מחרוזת אחת שהמקבל מפצל. מכסה את כללי CommandLineToArgvW, CRT ו-.NET, ArgumentList, ובניה ב-C++.
WMI/CIM מ-C# ומ-PowerShell — מדריך מעשי למידע חומרה, ניטור process ושאילתות remote
WMI/CIM הוא הדרך הסטנדרטית לשלוף serial number של מחשב, לנטר דיסק פנוי ולזהות process שהתחיל. המאמר מכסה CIM cmdlets כמו Get-CimInstance,...
עד מתי אפשר להשתמש ב-MSMQ? ── החלטת ה-migration של תור legacy ש"אפילו לא מוגדר כ-deprecated"
MSMQ לא מופיע ברשימת ה-deprecated הרשמית, אבל System.Messaging קיים רק ב-.NET Framework ומעכב את ה-migration ל-.NET. המאמר מסדר את השמועו...
Pitfalls באפליקציות serial communication — reconnect ותכנון log
Pitfalls שכדאי להימנע מהן באפליקציית serial לחיבור ציוד ולשליטה במכשירי מדידה: framing, timeout, RTS/CTS, DTR/RTS, reconnect ותכנון log, ...
איך קוראים ל-DLL של C# Native AOT מ-C/C++
איך מפרסמים ספריית מחלקות C# כ-DLL native עם Native AOT, וקוראים לנקודות כניסה מסוג UnmanagedCallersOnly מ-C/C++ — לפי מקום השימוש, דפוסי...
נושאים קשורים
העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.
נושאים טכניים ב-Windows
שער לנושאי פיתוח Windows, חקירת תקלות וניצול נכסים קיימים.
שירותים הקשורים לנושא הזה
המאמר קשור ישירות לשירותים הבאים.
שימוש חוזר והעברה של נכסים קיימים
מדובר ב-inventory של נכסים קיימים שכוללים .NET Framework, Web Forms, WCF, COM / ActiveX ותפעול NuGet ישן, ולכן זה מתאים לייעוץ migration של נכסים קיימים.
ייעוץ טכני וסקירת תכנון
אם רוצים לקבוע לפני שמתחילים את היקף ה-migration, איך מחלקים incremental migration, ועד כמה מקבלים הנחת Windows-only, זה נושא שקל לקדם כייעוץ טכני וכ-design review.
שאלות נפוצות
שאלות נפוצות בפניות בנושא המאמר.
- מה כדאי לעשות קודם לפני migration מ-.NET Framework ל-.NET?
- לפני שנכנסים למימוש, קודם מסיימים את העבודה בצד .NET Framework. גם המדריך הרשמי של Microsoft ממליץ, לפני ה-port, לעלות ל-.NET Framework 4.7.2 ומעלה (בשטח, אם אפשר, 4.8.1), להמיר packages.config ל-PackageReference, לעבור ל-SDK-style project, ולעדכן תלויות לגרסה עדכנית יותר. אם עושים את זה קודם, ה-gap בשלב הבא של המעבר ל-modern .NET מצטמצם משמעותית, וקל יותר להפריד אם בעיית תאימות מגיעה מ-Framework ישן או מהמעבר ל-.NET. במקביל, גם עושים inventory של טכנולוגיות לא נתמכות כמו AppDomain, Remoting ו-BinaryFormatter לפני שמתחילים.
- האם אפשר להחליט להישאר על .NET Framework?
- כן, זו החלטה סבירה לגמרי. .NET Framework 4.8.1 ממשיך לקבל תמיכה כל עוד הוא רץ על Windows נתמך, ולכן זה לא מצב שבו חייבים מיד להעביר הכול ל-modern .NET. אם יש הרבה מסכי Web Forms, צריך לשמור בקפדנות על תאימות WCF server, יש תלות עמוקה ב-Workflow Foundation או ב-COM+, או שרכיבי design-time של צד שלישי לא נתמכים — הגיוני להישאר בינתיים על 4.8.1 עם תפעול יציב, ולתכנן החלפה בקו נפרד. עם זאת, נשארות מגבלות: אי אפשר לצאת מ-Windows-only, וקשה ליהנות מביצועים ותכונות שפה חדשות של .NET.
- אילו טכנולוגיות אי אפשר להעביר ל-.NET, או שקל להיתקע בהן?
- יצירת AppDomain, .NET Remoting, CAS (Code Access Security), COM+ (System.EnterpriseServices) ו-Workflow Foundation לא נתמכים ב-modern .NET, ואלה red flags שדורשים תכנון מחדש. WCF server לא רץ כמו שהוא built-in, ויש לבחור בין תכנון מחדש עם CoreWCF לבין gRPC / HTTP API. BinaryFormatter, מ-.NET 9 ואילך, לא כולל מימוש וזורק תמיד exception, ולכן נדרש audit שכולל נתונים שנשמרו, ResX וגם clipboard / drag & drop. בנוסף, install.ps1 / XDT של packages.config, native DLL, COM / ActiveX, והנחת x86 — כל אלה נקודות שדורשות תשומת לב, כי גם אם ה-build עובר, קל ליפול ב-runtime.
- האם migration של WinForms או WPF ל-.NET הופך אותם ל-cross-platform?
- לא. WinForms / WPF יכולים לעבור ל-.NET, אבל הם נשארים Windows-only. מה שמתקבל מה-migration הוא היתרונות של runtime, תכונות שפה, SDK-style ותאימות ל-CI/CD של modern .NET, אבל זה לא שם אותם על Linux container. אם בעתיד שואפים ל-Linux / containerization, צריך לעשות inventory מוקדם ל-API-ים שמניחים Windows כמו System.Drawing.Common, Registry, WMI, EventLog, Windows Service ו-Office Interop. לעומת זאת, אם ממשיכים Windows-only, אפשר לבחור במסלול מעשי שמודרניזציה קודם את ה-runtime בעזרת Windows Compatibility Pack.