רשימת בדיקה לפני מעבר מ-.NET Framework ל-.NET
· עודכן בתאריך: · Go Komura · .NET, .NET Framework, C#, מודרניזציה, פיתוח Windows, מעבר
הורידו את רשימת הבדיקה בגרסת Excel עם גליון יפני ואנגלי
הקובץ הזה בנוי משני גליונות, Checklist-ja ו-Checklist-en, ומרכז את רשימת הבדיקה שלפני התחלת העבודה מ-13 הפרקים ל-7 קטגוריות ו-34 סעיפים: מדיניות / סידור צד .NET Framework הנוכחי / סוג היישום ובחירת טכנולוגיה / טכנולוגיות לא נתמכות ו-API-ים שדורשים תשומת לב / תלות ייעודית ל-Windows / shared library וגישה לנתונים / תפעול ו-build. עמודות Status ו-Notes נשארות ריקות, כך שאפשר להשתמש בקובץ ישירות כטבלת מיפוי לכל פרויקט. כדאי לעבור עליו לפני קריאת המאמר — כך קל יותר לתפוס את זרימת 13 הפרקים.
משנים את TargetFramework ב-.csproj ל-net10.0, מעדכנים כמה חבילות NuGet, וכש-build עובר — סיימנו.
……אם זה היה המעבר, זה היה די שליו. בפועל, בדרך כלל זה לא ככה.
בסביבת .NET Framework בשטח, ישנן לא מעט הנחות יסוד שבדרך כלל לא מודעים אליהן — System.Web, WCF, Web Forms, packages.config ישן, web.config.install.xdt, DLL native, COM / ActiveX, בקרות צד שלישי שרצות רק בזמן עיצוב, הנחה שקטה של x86, ResX תלוי-designer, ו-serializer ישן.
לכן, מה שבאמת חשוב במעבר מ-.NET Framework ל-.NET הוא המיפוי לפני תחילת המימוש. אם מפרקים את הסוגיות לפני תחילת העבודה, המעבר נהיה לא “הימור גדול” אלא “עבודה שמתקדמת סעיף אחר סעיף”.
flowchart TB
accTitle: המיפוי משנה את אופי המעבר
accDescr: תרשים המראה שאם מבצעים מיפוי לפני תחילת המימוש ומפרקים את הסוגיות, המעבר נהיה עבודה שמתקדמת בסדר, ואילו דילוג על המיפוי הופך את המעבר להימור גדול.
hid1["הנחות סמויות ישנות"] --> inv1["מיפוי לפני תחילת העבודה מפרק סוגיות"]
inv1 --> tsk1["עבודה שמתקדמת סעיף אחר סעיף"]
hid1 -.->|"אם מדלגים על המיפוי"| bet1["המעבר הופך להימור גדול"]
איור 1: היכולת לפרק סוגיות לפני תחילת העבודה קובעת אם המעבר יהיה הימור או עבודה מסודרת.
המאמר הזה מסכם מה כדאי לבדוק לפני מעבר יישום עסקי קיים ב-.NET Framework 4.x ל-.NET הנוכחי. היעד העיקרי הוא סוגי היישומים הבאים:
- ספריות מחלקות
- יישומי מסוף
- שירותי Windows
- WinForms / WPF
- ASP.NET Framework (MVC / Web API / Web Forms)
- יישומים שמשתמשים ב-WCF
- יישומים שמשתמשים ב-EF6
זמן הכתיבה הוא 2026-03-15. תקופות התמיכה והמלצות הכלים הרשמיים משתנות עם הזמן, ולכן אם קוראים את המאמר בפער זמן, כדאי לבדוק גם את המידע הרשמי.
1. קודם המסקנה
תחילה, רק את המסקנות שלא כדאי לפספס:
- קודם מסדרים את צד .NET Framework לפני המעבר. גם במדריך הרשמי של Microsoft ממליצים, לפני ההעברה, לעלות ל-.NET Framework 4.7.2 ומעלה, להפוך ל-
PackageReference, לעבור לסגנון SDK, ולעדכן תלויות קודם. - הקושי נקבע לפי מודל היישום, ולא כמות הקוד. ספריות מחלקות ומסופים יחסית קלים, ואילו ASP.NET Framework, Web Forms, שרת WCF ו-WF (Workflow Foundation) נוטים להיות כבדים.
- WinForms / WPF יכולים לעבור ל-.NET אבל נשארים ייעודיים ל-Windows. טעות בהבנה כאן מובילה למוקש קלאסי — עברו את המעבר אבל היישום עדיין לא רץ על מכולת Linux.
- המעבר מ-ASP.NET Framework ל-ASP.NET Core הוא בפועל מעבר ארכיטקטורה. ביישום קטן אפשר לפעמים לעבור בבת אחת, אבל במערכת ייצור גדולה בטוח יותר להניח מעבר הדרגתי.
- WCF ו-EF6 לפעמים אפשר להפריד ממעבר זמן הריצה. ללקוח WCF יש חבילה נתמכת ל-.NET, ו-EF6 אפשר להעביר בנפרד ל-EF Core אחרי המעבר ל-.NET מודרני.
- מצד שני, יצירת AppDomain, .NET Remoting, CAS (Code Access Security, אבטחת גישה לקוד), COM+, Workflow Foundation, ותלות ב-BinaryFormatter הם תמרורי אזהרה. אם לא מגלים אותם קודם, נפח העבודה מתפוצץ בהמשך.
packages.config/install.ps1/XDT(XML Document Transform, מנגנון שהופךweb.configוכדומה) / נכסיcontent/ DLL native / COM / ActiveX / הנחת x86 — כל אלה נוטים ליפול בזמן ריצה או בזמן עיצוב גם כש-build עובר, ולכן סורקים אותם לפני תחילת העבודה.- נכון ל-2026-03, .NET 10 הוא LTS. נקודת ההגעה למעבר חדש טבעי לחשוב עליה בהתבסס על ה-LTS הנוכחי.
- מעבר בלי בדיקות, מדידה והכנה ל-rollback הוא מסוכן. המעבר הוא לא בעצם עבודת מימוש, אלא עבודה של חשיפת תנאי היסוד אחד-אחד.
מפת הידע של המאמר
המעבר מ-.NET Framework ל-.NET דורש בדיקת מלאי לפני שמתחילים לממש. המרה ל-PackageReference (היורש של packages.config) ולפרויקט בסגנון SDK היא עבודה שכדאי לסיים לפני המעבר עצמו, לא במסגרתו. יצירת AppDomain חדש, .NET Remoting, COM+ ו-Workflow Foundation אינם תואמים ל-.NET, וגם BinaryFormatter עלול שלא להיות תואם בהתאם לנסיבות. ל-WCF יש מצב שונה בצד הלקוח ובצד השרת — בצד השרת נדרש תכנון מחדש לכיוון CoreWCF או gRPC. ASP.NET Framework הוא היורש של ASP.NET Core, אך Web Forms אינו אותו app model, וכדאי להפריד את המעבר מ-EF6 ל-EF Core מהמעבר של ה-runtime ולדחות אותו למועד מאוחר יותר. WinForms/WPF ניתנים למעבר אך נשארים ייעודיים ל-Windows, והתלות ב-API ייעודי ל-Windows ניתנת להקלה באמצעות חבילת התאימות של Windows.
flowchart LR
accTitle: מפת הידע של רשימת התיוג לפני מעבר מ-.NET Framework ל-.NET
accDescr: תרשים המראה את היחסים בין צורת התלויות, טכנולוגיות שאינן נתמכות, הטיפול ב-WCF וב-EF6, והתלות ב-API ייעודי ל-Windows — כרכיבים שיש לסדר לפני מעבר מ-.NET Framework ל-.NET.
framework_to_dotnet_migration["מעבר מ-.NET Framework ל-.NET"]
dotnet_framework[".NET Framework"]
dotnet[".NET (מ-Core ואילך)"]
windows_compat_pack["Windows Compatibility Pack"]
package_reference["PackageReference"]
packages_config["packages.config"]
sdk_style_project["פרויקט בסגנון SDK"]
dotnet_upgrade_assistant[".NET Upgrade Assistant"]
github_copilot_modernization["מודרניזציה בעזרת GitHub Copilot"]
net_standard_2_0[".NET Standard 2.0"]
appdomain["יצירת AppDomain"]
dotnet_remoting[".NET Remoting"]
com_plus["COM+(System.EnterpriseServices)"]
workflow_foundation["Workflow Foundation"]
binaryformatter["BinaryFormatter"]
wcf_server["שרת WCF (אירוח שירות WCF)"]
corewcf["CoreWCF"]
grpc["gRPC"]
ef_core["EF Core"]
ef6["EF6(Entity Framework 6)"]
asp_net_core["ASP.NET Core"]
asp_net_framework["ASP.NET Framework(MVC/Web API)"]
web_forms["ASP.NET Web Forms"]
system_web["System.Web / HttpContext.Current"]
wpf["WPF"]
windows_forms["Windows Forms"]
windows_only_api_dependency["תלות ב-API ייעודי ל-Windows"]
wcf_client["לקוח WCF"]
framework_to_dotnet_migration -->|"מחייב"| dotnet_framework
dotnet -->|"יורש את"| dotnet_framework
framework_to_dotnet_migration -.->|"משתמש ב"| windows_compat_pack
package_reference -->|"יורש את"| packages_config
sdk_style_project -->|"צריך לקדום ל"| framework_to_dotnet_migration
package_reference -->|"צריך לקדום ל"| framework_to_dotnet_migration
dotnet_upgrade_assistant -->|"שימוש לא מומלץ ל"| framework_to_dotnet_migration
github_copilot_modernization -->|"מענה מומלץ ל"| framework_to_dotnet_migration
framework_to_dotnet_migration -.->|"משתמש ב"| net_standard_2_0
appdomain -->|"אינו מתיישב עם"| dotnet
dotnet_remoting -->|"אינו מתיישב עם"| dotnet
com_plus -->|"אינו מתיישב עם"| dotnet
workflow_foundation -->|"אינו מתיישב עם"| dotnet
binaryformatter -.->|"אינו מתיישב עם"| dotnet
wcf_server -.->|"מחייב"| corewcf
wcf_server -.->|"מחייב"| grpc
corewcf -->|"מממש את"| wcf_server
ef_core -->|"יורש את"| ef6
framework_to_dotnet_migration -->|"צריך לקדום ל"| ef_core
asp_net_core -->|"יורש את"| asp_net_framework
web_forms -.->|"אינו מתיישב עם"| asp_net_core
system_web -->|"אינו מתיישב עם"| dotnet
wpf -.->|"משתמש ב"| binaryformatter
windows_forms -.->|"משתמש ב"| binaryformatter
wpf -.->|"משתמש ב"| windows_only_api_dependency
windows_forms -.->|"משתמש ב"| windows_only_api_dependency
windows_compat_pack -.->|"מצמצם"| windows_only_api_dependency
dotnet -.->|"משתמש ב"| wcf_client
wcf_server -.->|"מחייב"| dotnet_framework
asp_net_framework -->|"מחייב"| dotnet_framework
בתרשים, קו מלא מציין קשר שמתקיים תמיד וקו מקווקו מציין קשר מותנה (תנאי ההתקיימות מפורטים בהסבר של כל קשר בעמוד המפורט). רשימת כל הקשרים (סך הכול 30, עם אסמכתה ורמת ודאות) והגדרות המושגים המרכזיים מרוכזות בעמוד המפורט של מפת הידע (ביפנית). נתונים: JSON-LD / Turtle
2. קודם קובעים “האם באמת צריך לעבור עכשיו”
הדבר הראשון שכדאי לקבוע הוא לא “איך לעבור”, אלא האם באמת צריך לעבור את היישום הזה עכשיו.
אם זה נשאר מעורפל, נוטים לקבל החלטה שנכונה טכנית אבל כבדה מדי מבחינה עסקית, או להפך, לדחות מעבר שברור שכדאי לעשות אותו.
flowchart TB
accTitle: השאלה שקובעים ראשונה
accDescr: תרשים המראה שלפני שקובעים איך לעבור, צריך לקבוע האם באמת צריך לעבור עכשיו, ושמעורפלות כאן נוטה להוביל למעבר כבד מדי או לדחייה מוגזמת.
q1["האם באמת צריך לעבור עכשיו"] -->|"קובעים תחילה"| how1["איך לעבור"]
q1 -.->|"אם ממשיכים במעורפל"| bad1["מעבר כבד מדי או דחייה מוגזמת"]
איור 2: בלי לקבוע “האם צריך לעבור עכשיו” לפני “איך לעבור”, ההחלטה נוטה להיסחף לכיוון אחד.
2.1 גם החלטה להישאר עם .NET Framework היא סבירה לגמרי
.NET Framework 4.8.1 ממשיך לקבל תמיכה כל עוד הוא רץ על Windows נתמך. כלומר, זה לא סיפור פשוט של “אם לא נעביר הכול מיד ל-.NET מודרני, זה מסוכן”.
עם זאת, יש להישארות מגבלות ברורות.
- לא ניתן לצאת מ-Windows בלבד
- ממשיכים לשאת ASP.NET Web Forms או שכבת שרת ישנה
- קשה ליהנות משיפורי ביצועים, תכונות שפה ומערכת אקולוגית של .NET חדש
- קל להיסחף מהנחות היסוד העדכניות של ענן, מכולות ו-CI/CD
מצד שני, אם התלות הזו חזקה, הגיוני להישאר בינתיים על .NET Framework 4.8.1 עם תפעול יציב, ולתכנן החלפה בקו נפרד.
flowchart TB
accTitle: הישארות היא בחירה סבירה
accDescr: תרשים המראה שאם יש תלות רגרסית חזקה כמו Web Forms, תאימות שרת WCF או COM+, הגיוני להישאר בינתיים על .NET Framework 4.8.1 עם תפעול יציב, ולתכנן החלפה בקו נפרד.
dep1["יש תלות רגרסית חזקה"] --> stay1["בינתיים, תפעול יציב על 4.8.1"]
stay1 --> plan1["תכנון החלפה בקו נפרד"]
stay1 -.-> lim1["מגבלות כמו Windows בלבד נשארות"]
איור 3: כשהתלות עמוקה, שילוב של תפעול יציב על 4.8.1 ותכנון החלפה נפרד הוא ריאלי.
- יש כמות גדולה של נכסי מסך ב-Web Forms
- צריך לשמור בקפדנות על תאימות שרת WCF
- תלות עמוקה ב-Workflow Foundation או ב-COM+
- רכיב צד שלישי לזמן עיצוב לא נתמך ב-.NET מודרני
- לא ניתן לאשר מבחינה עסקית שינוי אפיון גדול
2.2 מה משתנה לפי כל בחירה
| בחירה | מה משתפר | מה נשאר / מה נאבד | מתי מתאים |
|---|---|---|---|
| להישאר עם .NET Framework 4.8.1 | קל יותר לשמור על נכסים קיימים ולתפעל ביציבות | Windows בלבד, מודל יישום ישן, מגבלת מודרניזציה | תלות רגרסית חזקה, כרגע עדיפות עסקית עם תפעול יציב |
| מעבר ל-.NET מודרני, נשארים ב-Windows | אפשר להביא לזמן ריצה ושרשרת כלים מודרנית. תועלת גדולה בביצועים, חוויית פיתוח וסגנון SDK | תלות ב-API של Windows נשארת. לא הופך לחוצה-פלטפורמות | יישום עסקי שמשתמש ב-WinForms / WPF, שירות Windows, API של Windows |
| מעבר ל-.NET מודרני, עם ראייה לעתיד ל-Linux / מכולה / ענן | חופש רב יותר במיקום פריסה. קל יותר גם לחדש את מודל התפעול | צריך להסיר קודם API-ים ומודל יישום ייעודיים ל-Windows | רוצים להעביר את צד השרת לענן, וגם לחדש את התשתית |
חשוב לקבוע קודם לא האם רוצים לעבור, אלא היכן רוצים להגיע אחרי המעבר.
flowchart TB
accTitle: קובעים את נקודת ההגעה
accDescr: תרשים המראה שהחשוב הוא לקבוע לא האם רוצים לעבור, אלא היכן רוצים להגיע אחרי המעבר, וזה משפיע על ההחלטה.
ask1["האם רוצים לעבור"] -.->|"שאלה לא מספקת"| dec1["מה קובעים קודם"]
land1["היכן רוצים להגיע אחרי המעבר"] -->|"זה מה שקובעים"| 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 הנוכחי כנקודת ההגעה.
הגישה המעשית כאן פשוטה:
- למעבר קטן שרוצים לסיים מהר, מגיעים ישירות ל-LTS הנוכחי
- גם במערכת ליבה עם תפעול ארוך-טווח, חושבים ראשית על ה-LTS הנוכחי
- “בגלל נוחות ספרייה קיימת רוצים LTS קודם” יכול להיות נסיבה סבירה, אבל בודקים עד מתי היא נתמכת לפי תאריך
flowchart TB
accTitle: הגישה לבחירת גרסת ההגעה
accDescr: תרשים המראה שגם מעבר קטן וגם מערכת ליבה בוחרים בבסיסיות ב-LTS הנוכחי, ואם רוצים LTS קודם בגלל ספרייה קיימת בודקים את תאריך סיום התמיכה.
tgt1["בוחרים .NET לנקודת ההגעה"] -->|"בסיסי"| lts1["מגיעים ל-LTS הנוכחי"]
tgt1 -->|"נוחות ספרייה קיימת"| old1["שוקלים גם LTS קודם"]
old1 --> dt1["בודקים תאריך סיום תמיכה ומחליטים"]
איור 5: נקודת ההגעה הבסיסית היא ה-LTS הנוכחי, ואם בוחרים ב-LTS ישן, מבססים על תאריך תפוגת התמיכה.
3.2 להישאר ייעודי ל-Windows, או לשאוף לחוצה-פלטפורמות בעתיד
ההחלטה הזו משנה משמעותית מה בודקים.
- אם נשארים ייעודיים ל-Windows, אפשר לנקוט מסלול מעשי של מודרניזציה תחילה של זמן הריצה, בעזרת WPF / WinForms ו-Windows Compatibility Pack.
- אם שואפים בעתיד גם ל-Linux / הכלה במכולה, צריך למפות מוקדם API-ים שמניחים Windows כמו
System.Drawing.Common, רישום המערכת, WMI, EventLog, שירות Windows ו-Office Interop.
אם לא מחליטים על זה ומתחילים מעבר, בהמשך הדיון מתפתל לכיוון “האם באמת נכון היה להישאר על Windows” או “לא, רצינו להכניס למכולה”.
flowchart TB
accTitle: ההסתעפות בין ייעודי ל-Windows לחוצה-פלטפורמות
accDescr: תרשים המראה שאם נשארים ייעודיים ל-Windows אפשר לנקוט מסלול מעשי של מודרניזציה של זמן הריצה עם חבילת התאימות, ואילו אם שואפים לעתיד למכולה או ל-Linux צריך למפות מוקדם API-ים שמניחים Windows.
aim1["באיזה כיוון שואפים"] -->|"נשארים ייעודיים ל-Windows"| wn1["מודרניזציה של זמן הריצה עם חבילת התאימות"]
aim1 -->|"בעתיד גם מכולה"| xp1["מיפוי מוקדם של API-ים שמניחים Windows"]
aim1 -.->|"מתחילים בלי להחליט"| twist1["הדיון מתפתל באמצע"]
איור 6: בלי להחליט על ההסתעפות הזו מראש, מה שצריך לבדוק לא מתברר, והדיון מתפתל באמצע.
3.3 לעשות הכול בבת אחת, או מעבר הדרגתי
יש שלושה סוגים עיקריים למעבר:
- מעבר מרוכז שקרוב ל-in-place
- מעבר שבו ישן וחדש רצים side-by-side זה לצד זה
- מעבר הדרגתי שמתקדם בהדרגה לפי route / ספרייה
בפרט, ביישומי ASP.NET Framework, גם המדריך של Microsoft מנחה בבירור ל-incremental migration. אם לא רוצים לעצור את הייצור, יש הרבה תכונות, והתלויות סביב רבות, טבעי יותר לתכנן מראש מתוך הנחת מעבר הדרגתי.
flowchart TB
accTitle: שלושת הסוגים של המעבר
accDescr: תרשים המראה שיש שלושה סוגי מעבר - מרוכז קרוב ל-in-place, side-by-side עם ישן וחדש במקביל, ומעבר הדרגתי לפי route או ספרייה - ובתנאי שלא רוצים לעצור את הייצור, ההדרגתי טבעי יותר.
typ1["בוחרים סוג מעבר"] --> t1["מעבר מרוכז (קרוב ל-in-place)"]
typ1 --> t2["side-by-side עם ישן וחדש במקביל"]
typ1 --> t3["מעבר הדרגתי לפי route / ספרייה"]
t3 -.-> fit1["אם לא רוצים לעצור ייצור, זו ההנחה הטבעית"]
איור 7: יש שלושה סוגי מעבר, ובמערכת ייצור עם הרבה תכונות שלא ניתן לעצור, ההדרגתי הוא ההנחה הטבעית.
3.4 מה “מוציאים מהיקף המעבר הזה”
מעבר נוטה להיכשל כי לוקחים על עצמם יותר מדי.
לדוגמה, לעשות בו-זמנית את הבאים נוטה להיות כבד:
- .NET Framework → .NET
- ASP.NET Framework → ASP.NET Core
- EF6 → EF Core
- שרת Windows → מכולת Linux
- שינוי תשתית אימות
- שינוי תשתית לוגים / ניטור
- מעבר מסד נתונים
כמובן שכולם עשויים להידרש בשלב כלשהו. אבל האם צריך לעשות אותם בו-זמנית זה עניין אחר.
בפועל, ההפרדה הבאה עובדת יותר טוב:
- קודם, מודרניזציה של זמן הריצה ומבנה הפרויקט
- על גביה, העברת מודל היישום
- לבסוף, עדכון ORM, אימות, ענן וניטור
flowchart TB
accTitle: סדר ההפרדה שלא לוקחים יותר מדי
accDescr: תרשים המראה שקודם מבצעים מודרניזציה לזמן הריצה ולמבנה, אחר כך מעבירים את מודל היישום, ולבסוף מעדכנים ORM, אימות, ענן וניטור - סדר הפרדה שמונע מהמעבר להיות כבד.
d1["מודרניזציה של זמן הריצה והמבנה"] --> d2["העברת מודל היישום"]
d2 --> d3["עדכון ORM, אימות, ענן וניטור"]
d1 -.->|"אם לוקחים הכול בו-זמנית"| hv1["המעבר נהיה כבד ונוטה להיכשל"]
איור 8: בלי לעשות הכול בו-זמנית, אלא בהפרדה של זמן ריצה, מודל היישום והשוליים, המעבר לא נהיה כבד.
4. הבסיס שמכינים לפני תחילת העבודה
המדריך המקדים למעבר של Microsoft מעשי מאוד. בקיצור, מדובר בהכנת פרויקט .NET Framework הנוכחי לכיוון כניסה מודרנית לפני המעבר.
4.1 עלייה ל-.NET Framework 4.7.2 ומעלה
במדריך הרשמי מומלץ, לפני ההעברה, לכוון ל-.NET Framework 4.7.2 ומעלה. הסיבה היא שגם כש-.NET Standard לא מחזיק ישירות את ה-API הקיים, קל יותר להתקרב לחלופת API עדכנית יותר.
בעבודה מעשית, אם אפשר, ברור יותר לחשוב מנקודת מוצא של 4.8.1.
- ברור יותר מבחינת נקודת מבט התמיכה
- קל יותר להתייחס לזה כנקודת יציבות אחרונה בצד .NET Framework
- קל יותר לגבש מדיניות של “קודם מסדרים בצד ה-Framework הנוכחי”
מה משתנה אם עושים את זה קודם
- קל יותר לייצב את הטיפול בספריות משותפות של
.NET Standard 2.0 - אפשר לצמצם מראש רעש שמקורו בזמן ריצה ישן
- קל יותר להבחין אם מקור בעיית תאימות הוא “ה-Framework הישן” או “ההפיכה ל-.NET מודרני”
4.2 מעבר ל-PackageReference
במדריך המקדים מומלץ להתקדם צורת ההפניה ל-PackageReference.
אם עושים את זה קודם, התצוגה הכוללת של ניהול התלויות משתפרת משמעותית.
מה משתנה כשעוברים ל-PackageReference
- הפניות החבילות מרוכזות ב-
csproj - קל יותר לראות תלויות טרנזיטיביות
- הנחת ה-restore מתאימה לצד .NET מודרני
- תאימות טובה יותר עם CLI / CI
עם זאת, יש כאן מוקש.
המוקש הטיפוסי
בתיעוד הרשמי של 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, ולכן בזמן המעבר קל שהנחות סמויות יעלו לפני השטח.
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 | בסייר הפתרונות, לחיצה ימנית על “הפניות” או על packages.config ובחירה ב-Migrate packages.config to PackageReference... מבצעת המרה |
Visual Studio 2017 15.7 ומעלה. לא זמין בפרויקטי ASP.NET ו-C++. אם לא מופיע בתפריט, פותחים פעם את שחזור ה-NuGet או את מנהל החבילות ואז לוחצים ימנית שוב |
| .NET Upgrade Assistant | מבצע ניתוח ושדרוג של הפרויקט יחד | בתיעוד הרשמי הוא כבר לא מומלץ, ומופנים להשתמש במודרניזציה בעזרת GitHub Copilot |
| מודרניזציה בעזרת GitHub Copilot | מסייע עד להערכה, תכנון, תיקון קוד ואימות | ליבת ההנחיה הרשמית הנוכחית. כפי שבפרק 4.5, מניח Visual Studio, Copilot וקוד C# |
| הפיכה לסגנון SDK ידנית | יוצרים csproj חדש ומעבירים רק את הפריטים הנדרשים |
בפרויקט עם מעט קבצים, לעיתים זה המהיר ביותר |
פונקציית ההמרה של Visual Studio יוצרת גיבוי של הפרויקט לפני ההרצה, ומוציאה בסוף דוח המרה (תלויות ברמה עליונה, תלויות טרנזיטיביות, בעיות תאימות שזוהו). בתיעוד כתוב שאם רוצים לחזור אחורה, משחזרים את csproj ואת packages.config מתיקיית הגיבוי, ומריצים update-package -reinstall במסוף מנהל החבילות.
כלומר, אפשר לנסות בצורה שניתנת לביטול. באומדן לפני תחילת העבודה, כדאי להמיר קודם פרויקט אחד בלבד ולקרוא את הדוח.
flowchart TB
accTitle: מנסים המרה בצורה שניתנת לביטול
accDescr: תרשים המראה שפונקציית ההמרה של Visual Studio יוצרת גיבוי לפני ההמרה ומוציאה דוח, ולכן ממירים תחילה פרויקט אחד בלבד וקוראים את הדוח, ואם יש בעיה משחזרים מהגיבוי.
bk1["נוצר גיבוי"] --> cv2["המרת פרויקט אחד בלבד"]
cv2 --> rp1["קריאת דוח ההמרה"]
rp1 -->|"אם יש בעיה"| rv1["שחזור והתקנה מחדש"]
rp1 -->|"אם אין בעיה"| go1["הרחבה לפרויקטים נוספים"]
איור 10: ההמרה ניתנת לניסיון עם גיבוי ודוח, ולכן קודם בודקים על פרויקט אחד.
4.3 מעבר לסגנון SDK
במדריך המקדים מומלצת גם המרה לצורת פרויקט בסגנון SDK.
זה משפיע משמעותית.
מה משתנה עם סגנון SDK
- ה-
csprojנעשה פשוט משמעותית - תאימות טובה עם
PackageReference - קל יותר לבצע multi-targeting
- קל יותר להתאים ל-CI/CD שמתבסס על
dotnet build/dotnet test/dotnet publish - הקרבה למבנה של צד .NET מודרני מצמצמת את ההפרש בשלב השני
מנקודת מבט הפוכה, זה אומר שאם קופצים ישר ל-.NET מודרני עם csproj ישן וניהול NuGet ישן, ההפרש גדול מדי.
flowchart TB
accTitle: מעבר לסגנון SDK מצמצם הפרש
accDescr: תרשים המראה שקפיצה ישירה ל-.NET מודרני עם csproj ישן וניהול NuGet ישן יוצרת הפרש גדול מדי, ואילו מעבר קודם לסגנון SDK מקרב למבנה הצד המודרני ומצמצם את ההפרש בהמשך.
oldp["csproj ישן + ניהול NuGet ישן"] -.->|"קפיצה ישירה"| big1["הפרש גדול מדי"]
oldp -->|"קודם מעבר לסגנון SDK"| sdk1["התקרבות למבנה הצד המודרני"]
sdk1 --> small1["הפרש בהמשך מצטמצם"]
איור 11: אם משלבים מעבר לסגנון SDK, ההפרש בקפיצה ל-.NET מודרני מצטמצם.
4.4 עדכון תלויות קודם
גם זה לפי המדריך הרשמי, אבל את התלויות מקדמים להגרסה העדכנית הזמינה, ואם אפשר, לגרסה שתומכת ב-.NET Standard.
המשמעות בעשיית זה קודם
- מהר יותר יודעים “האם החבילה הזו אפשר להשתמש בה ב-.NET מודרני”
- מונעים מתלויות ישנות להפוך לרעש
- קל יותר להפוך shared library ל-
netstandard2.0 - קל יותר לרכז את עבודת המעבר בשלב הבא ב”העברת הקוד”
flowchart TB
accTitle: המשמעות בעדכון התלויות קודם
accDescr: תרשים המראה שעדכון התלויות מראש לגרסה עדכנית מאפשר לדעת מהר אם הן תומכות ב-.NET מודרני, מפחית רעש מתלויות ישנות, ומאפשר לרכז את עבודת המעבר בשלב הבא בהעברת הקוד.
upd1["עדכון התלויות מראש"] --> know1["מהר יותר יודעים אם תומך ב-.NET מודרני"]
upd1 --> noise1["מפחית רעש מתלויות ישנות"]
know1 --> foc1["השלב הבא מתמקד בהעברת הקוד"]
noise1 --> foc1
איור 12: ככל שעדכון התלויות מוקדם יותר, גוף המעבר יכול להתמקד בהעברה עצמה.
4.5 בודקים גם את הנחות הכלים הרשמיים
נכון ל-2026-03, מרכז הכובד של הנחיית Microsoft עבר לכיוון מודרניזציה בעזרת GitHub Copilot. במקום להתבסס רק על כלי מעבר ותיקים, מעשי יותר להתייחס לזה כזרם תמיכה שכולל הערכה, תכנון, תיקון קוד ואימות.
עם זאת, בתיעוד הנוכחי, ההנחה היא Visual Studio 2026 או Visual Studio 2022 בגרסה נתמכת, GitHub Copilot, וגם קוד C#.
למה הבדיקה הזו נדרשת
- מה אפשר לצפות מהכלי הרשמי משתנה
- קל יותר להתאים בין הצוות את הנחות ה-IDE / build agent / התוספים
- אפשר להגיע להחלטה שלא לצפות ליותר מדי אוטומציה בפתרונות VB.NET
לא נדיר לראות סביבת עבודה שיש בה גם VB.NET. לכן, שווה לבדוק מראש “עד כמה הכלי הרשמי העדכני יעזור”.
flowchart TB
accTitle: המצב הנוכחי וההנחות של הכלים הרשמיים
accDescr: תרשים המראה ש-.NET Upgrade Assistant נהיה לא מומלץ ומרכז הכובד של ההנחיה עבר למודרניזציה בעזרת GitHub Copilot, אבל ההנחה היא Visual Studio, Copilot וקוד C#, ולכן בסביבה עם VB.NET לא כדאי לצפות ליותר מדי אוטומציה.
ua1[".NET Upgrade Assistant"] -->|"לא מומלץ, מפנה ל"| cp1["מודרניזציה בעזרת GitHub Copilot"]
cp1 --> pre1["הנחה: Visual Studio + Copilot + C#"]
pre1 -.->|"אם יש VB.NET"| vb1["לא לצפות ליותר מדי אוטומציה"]
איור 13: מרכז הכובד של הכלים עבר למודרניזציה בעזרת Copilot, וסביבה שלא תואמת את ההנחות שלו דורשת התייחסות נפרדת.
5. אומדים את הקושי לפי סוג הפרויקט
נוטים לדבר על המעבר מ-.NET Framework ל-.NET כעל דבר אחד, אבל בפועל כל סוג פרויקט הוא משחק בפני עצמו.
5.1 תחושת קושי גסה
| סוג | תחושת קושי | הנקודות המרכזיות |
|---|---|---|
| ספריית מחלקות | נמוך〜בינוני | תאימות API, תלויות, חלוקת יעד |
| מסוף / batch / חלק משירותי Windows | נמוך〜בינוני | שיטת הפצה, תלות native, תצורה |
| WinForms / WPF | בינוני | נשאר ייעודי ל-Windows, designer, UI צד שלישי, סביבת BinaryFormatter |
| ASP.NET MVC / Web API | בינוני〜גבוה | מעבר מודל יישום ל-ASP.NET Core, אימות, session, תצורה, DI |
| ASP.NET Web Forms | גבוה | הפרש גדול במודל המסך, הנחת החלפת שכבת UI |
| לקוח WCF | בינוני | החלפת חבילה, חוזה, תצורה |
| שרת WCF | גבוה | CoreWCF או תכנון מחדש עם gRPC / HTTP API |
| ביצוע בו-זמני של EF6 → EF Core | גבוה | ORM שונה לגמרי, הבדל התנהגות, היסטוריית מעברים |
5.2 המפתח בספריית מחלקות הוא איך חותכים את “הגבול המשותף”
ספרייית מחלקות יחסית קלה להעברה. עם זאת, זה נכון רק כשהספרייה באמת מופרדת כמו ספרייה.
תלויות מהסוג הבא מעלות את הקושי:
- נוגעים ב-
System.Web - מסתכלים ישירות ב-
HttpContext.Current - כוללים סוגי WPF / WinForms ב-API הציבורי
- נשענים יתר על המידה על API-ים של Windows כמו רישום המערכת, WMI, EventLog
- תלויים ב-
AppDomainאו ב-Remoting
אם אפשר להוציא רק את לוגיקת העסק, זה קל, ואם נושאים גם את מודל היישום, זה כבד — כך קל יותר להבין.
flowchart TB
accTitle: איך רואים את כובד ספריית המחלקות
accDescr: תרשים המראה שספרייה שבה אפשר להוציא רק את לוגיקת העסק קלה למעבר, ואילו ספרייה שנושאת גם מודל יישום דרך System.Web, סוגי UI, API של Windows ו-AppDomain כבדה.
lib1["בוחנים ספריית מחלקות"] -->|"אפשר להוציא רק לוגיקה"| lt1["מעבר קל"]
lib1 -->|"נושאת מודל יישום"| hv2["מעבר כבד"]
hv2 -.-> sm1["תלות ב-System.Web, סוגי UI, API של Windows וכדומה"]
איור 14: הקושי של הספרייה נקבע לא לפי מספר השורות, אלא לפי כמות מודל היישום שהיא נושאת.
5.3 WinForms / WPF קלים למעבר, אבל נשארים ייעודיים ל-Windows
WinForms ו-WPF יכולים לעבור ל-.NET. עם זאת, שניהם נשארים תשתיות ייעודיות ל-Windows.
טעות בציפייה כאן מסוכנת.
- מה שמשתפר
- עולים על זמן ריצה, שפה וסגנון SDK של .NET מודרני
- קל יותר להתאים CI/CD וניהול חבילות לימינו
- זוכים לחלק משיפורי הביצועים והתחזוקה
- מה שלא משתנה
- היות ייעודי ל-Windows
- נשארות בעיות תאימות של בקרות UI ורכיבים לזמן עיצוב
- בעיות ActiveX / COM / DLL native לא נעלמות
בנוסף, יש מקרים שבהם WinForms / WPF דורשים בדיקת השפעת BinaryFormatter. בפרט, כשקשור ל-clipboard, drag & drop, ResX, וסריאליזציה בזמן עיצוב עם custom type, זה נוטה לצוף כשמעלים את היעד ל-.NET 9 ומעלה.
flowchart TB
accTitle: הציפייה למעבר WinForms / WPF
accDescr: תרשים המראה ש-WinForms ו-WPF יכולים לעבור ל-.NET ולעלות על הזרימה של הזמן ריצה, השפה והסגנון המודרני, אבל נשארים ייעודיים ל-Windows, וגם נדרשת לפעמים בדיקת BinaryFormatter.
ui1["מעבר WinForms / WPF"] --> gain1["עולים על ההטבות של .NET מודרני"]
ui1 --> keep1["נשארים ייעודיים ל-Windows"]
ui1 -.-> bfc1["נדרשת לפעמים בדיקת BinaryFormatter"]
איור 15: ה-UI השולחני יכול לעבור, אבל התכונה של להיות ייעודי ל-Windows ממשיכה איתו.
5.4 ASP.NET Framework זה לא “מעבר זמן ריצה” אלא “מעבר מודל יישום”
המעבר מ-ASP.NET Framework ל-ASP.NET Core מוגדר גם במדריך של Microsoft כ-non-trivial. זה לא רק כי שמות ה-API משתנים, אלא כי הארכיטקטורה שמונחת ביסוד שונה.
המקומות שבהם ההבדל בולט הם בערך אלה:
- מודל האירוח (Hosting)
- צינור ה-middleware
- מודל עיבוד הבקשות
- Session / Cache
- אימות / הרשאה
- תצורה
- הזרקת תלויות (DI)
- לוגים / ניטור
מה שכדאי לבדוק קודם ביישום ASP.NET Framework הוא בערך אלה:
- מאיזה route / endpoint אפשר להעביר קודם
- האם אפשר להסיר תלות ב-
System.Webמ-shared library - איך מתאימים אימות / session / טיפול בחריגות / לוגים
- האם עוברים בהדרגה בלי לעצור את הייצור
בפרט, ביישום גדול, מעשי יותר לתכנן מראש בהנחת incremental migration.
flowchart TB
accTitle: מעבר ASP.NET הוא מעבר מודל יישום
accDescr: תרשים המראה שהמעבר מ-ASP.NET Framework ל-Core הוא לא החלפת שמות API אלא מעבר מודל יישום עם שינוי בארכיטקטורה שביסוד - hosting, middleware, אימות, תצורה, DI - ושביישום גדול מעשי יותר להניח מעבר הדרגתי.
an1["ASP.NET Framework → Core"] --> am1["הארכיטקטורה שביסוד משתנה"]
am1 --> pts1["hosting, middleware, אימות, תצורה, DI"]
am1 -->|"ביישום גדול"| inc1["תכנון בהנחת incremental migration"]
איור 16: המעבר ב-ASP.NET הוא לא מעבר זמן ריצה אלא מעבר מודל יישום, וככל שההיקף גדול, ההנחה ההדרגתית טבעית יותר.
5.5 Web Forms נכנסים דרך “פירוק אחריות” ולא דרך “מעבר נכסים”
Web Forms לא באותו מודל יישום כמו ASP.NET Core. לכן, באומדן, בטוח יותר לא להניח שאפשר להעביר את נכסי המסך כמו שהם.
בעבודה מעשית, לרוב מתחילים מהפירוק הבא:
- הפרדה בין לוגיקת מסך ללוגיקת עסק
- פירוק האחריות שקבורה ב-
Page/UserControl/ViewState - הוצאת לוגיקה עסקית וגישה לנתונים ל-shared library
- הרכבה מחדש של ה-UI במודל אחר כמו Razor Pages / MVC / Blazor
כלומר, בפרויקט Web Forms, מה שחשוב הוא האם יש תכנית פירוק אחריות לפני מעבר זמן הריצה.
flowchart TB
accTitle: Web Forms נכנסים דרך פירוק אחריות
accDescr: תרשים המראה ש-Web Forms אינם באותו מודל יישום כמו ASP.NET Core, ולכן מפרידים בין לוגיקת מסך לעסק, מוציאים לוגיקה ל-shared library, ומרכיבים מחדש את ה-UI במודל אחר.
wf1["נכסי המסך של Web Forms"] --> sep1["הפרדה בין לוגיקת מסך ללוגיקה עסקית"]
sep1 --> mv1["הוצאת הלוגיקה ל-shared library"]
mv1 --> re1["הרכבה מחדש של ה-UI במודל אחר"]
wf1 -.->|"הנחה של העברה כמו שזה"| ngw1["האומדן קורס"]
איור 17: מסתכלים על Web Forms לא כמעבר נכסים אלא כפירוק אחריות ואז הרכבה מחדש במודל אחר.
5.6 מפרידים בין חשיבה על לקוח WCF לשרת WCF
כאן בטוח יותר לא לכרוך יחד.
לקוח WCF
ל-WCF Client יש חבילת NuGet נתמכת ל-.NET מודרני. לכן, יש מקרים שבהם אם רק קוראים ל-WCF, זה לא כבד כמו שנראה.
שרת WCF
מצד שני, צד המארח של שירות WCF שונה. במדריך של Microsoft מנחים בעיקר בשני מסלולים לצורך מודרניזציה:
- כיוון של שימוש ב-CoreWCF לשמירת תאימות ללקוחות קיימים
- כיוון של מעבר ל-RPC / HTTP מודרני כמו gRPC
עם זאת, CoreWCF לא מביא את כל WCF כמו שהוא — זו תת-קבוצה. מתאים לשמירת תאימות ללקוחות קיימים, אבל שינוי קוד ובדיקה הם הנחת יסוד.
flowchart TB
accTitle: WCF מוערך בנפרד ללקוח ולשרת
accDescr: תרשים המראה שללקוח WCF יש חבילה נתמכת ל-.NET מודרני ולעיתים זה לא כבד כמו שנראה, ואילו בצד השרת צריך לבחור מסלול - שמירת תאימות עם CoreWCF, שהוא תת-קבוצה, או תכנון מחדש עם gRPC.
wcf1["באיזה צד משתמשים ב-WCF"] -->|"לקוח"| cl1["מעבר עם חבילה נתמכת"]
wcf1 -->|"שרת"| sv1["בוחרים מסלול"]
sv1 -->|"שמירת תאימות"| cw1["CoreWCF (תת-קבוצה)"]
sv1 -->|"תכנון מחדש"| gr1["gRPC / HTTP API"]
איור 18: לצד הקורא ולצד המארח יש קושי ואפשרויות שונים, ולכן לא אומדים אותם יחד.
6. סורקים טכנולוגיות שלא ניתן להשתמש בהן ב-.NET, או שקל להיתקע בהן כמו שהן
זו נקודה שחייבים לבצע לפני תחילת העבודה. ל-Microsoft יש רשימה של טכנולוגיות שהיו זמינות ב-.NET Framework אבל לא זמינות ב-.NET 6 ומעלה.
6.1 טכנולוגיות שנוטות להיות תמרור אזהרה
| טכנולוגיה | המצב ב-.NET | איך לחשוב על זה |
|---|---|---|
AppDomain.CreateDomain וכדומה — יצירת AppDomain |
לא נתמך | חושבים על בידוד דרך תהליך נפרד / מכולה / AssemblyLoadContext |
| .NET Remoting | לא נתמך | תכנון מחדש ל-IPC, HTTP, gRPC, Socket, Pipe וכדומה |
| CAS / Security Transparency | לא נתמך כגבול אבטחה | חושבים על מערכת ההפעלה / מכולה / הפרדת הרשאות |
System.EnterpriseServices (COM+) |
לא נתמך | מפרידים או מחליפים את התכנון שמניח COM+ |
| Workflow Foundation | לא נתמך | חושבים באומדן נפרד, כולל חלופות כמו CoreWF |
| שרת WCF | לא built-in כמו שהוא | בוחרים בין CoreWCF ל-gRPC |
| BinaryFormatter | מ-.NET 9 ואילך, המימוש תמיד זורק חריגה | מעבר לסריאלייזר אחר, ביקורת ResX / clipboard / drag & drop |
6.2 AppDomain הוא “גם אם חלק מה-API נשאר, היצירה עניין נפרד”
סביבת AppDomain קצת מסובכת. גם ב-.NET, חלק ממשטח ה-API נשאר, אבל השימוש של יצירת AppDomain חדש ובידוד בו לא נתמך.
לכן, אם השתמשו ב-AppDomain לצרכים הבאים, נדרש תכנון מחדש:
- בידוד plugin
- ביטול טעינה דינמית
- בידוד קוד באמון חלקי
- הפרדת סביבת הרצה זמנית
מה שכדאי לבדוק לפני המעבר הוא לא רק האם המילה AppDomain מופיעה, אלא לשם מה השתמשו ב-AppDomain.
flowchart TB
accTitle: השימוש ב-AppDomain קובע את ההחלטה
accDescr: תרשים המראה שלמרות שחלק מה-API נשאר, השימוש ביצירת AppDomain חדש ובידוד בו לא נתמך, ולכן מבחינים בין שימוש בסוג בלבד לבין בידוד בפועל, ולבידוד נדרש תכנון מחדש בתהליך נפרד או AssemblyLoadContext.
ad1["AppDomain מופיע"] -->|"רק שימוש בסוג או במידע"| okc1["ברוב המקרים אפשר להמשיך כמו שזה"]
ad1 -->|"יצירה ובידוד"| rd1["נדרש תכנון מחדש"]
rd1 --> alt1["תהליך נפרד / מכולה / AssemblyLoadContext"]
איור 19: לא לפי הימצאות המילה אלא לפי “לשם מה השתמשו בזה” נקבע אם נדרש תכנון מחדש.
6.3 Remoting “עמוק ממה שנראה”
מלבד Remoting עצמו, גם delegate אסינכרוני כמו קריאה ל-BeginInvoke() / EndInvoke() עלול להיכנס להיקף ההשפעה. זה לא Remoting עצמו, אבל הוא לא נתמך ב-.NET מודרני, ולכן צריך למפות אותו לפני המעבר.
לכן, בעת החיפוש, בטוח יותר לבדוק גם אלה יחד:
System.Runtime.RemotingMarshalByRefObjectRealProxyBeginInvoke(/EndInvoke(
6.4 BinaryFormatter צף לפתע לפי target version
BinaryFormatter, ככל שבסיס הקוד ישן יותר, נוטה להשתמש בו “בלי מודעות”.
- נתונים שנשמרים
- מטמון
- שמירת session
- מצב plugin
- clipboard / drag & drop
- ResX
- סביבת ה-designer של WinForms / WPF
מ-.NET 9 ואילך, BinaryFormatter לא כולל מימוש בזמן הריצה, וה-API תמיד זורק PlatformNotSupportedException.
כלומר, זה לא נושא ל”נחשוב על זה אחר כך”, אלא נקודה שדורשת ביקורת מוקדמת ברגע שקובעים את ה-target version.
flowchart TB
accTitle: המסלול שבו BinaryFormatter צף
accDescr: תרשים המראה ש-BinaryFormatter נוטה להיות בשימוש בלי מודעות דרך נתונים שנשמרים, ResX וכדומה, ומ-.NET 9 ואילך ה-API זורק תמיד חריגה, ולכן זו נקודה שדורשת ביקורת מוקדמת ברגע שקובעים את ה-target version.
hid2["שימוש בלי מודעות (ResX, clipboard וכדומה)"] --> tg1["קביעת target ל-.NET 9 ואילך"]
tg1 --> ex1["ה-API תמיד זורק חריגה"]
ex1 --> aud1["דורש ביקורת מוקדמת ברגע ההחלטה"]
איור 20: BinaryFormatter משפיע ברגע שקובעים את ה-target, ולכן לא דוחים אותו — מבצעים ביקורת מוקדמת.
6.5 מונחי חיפוש שכדאי לבצע grep עליהם קודם
עוד לפני תחילת העבודה, חיפוש הפתרון כולו לפי המונחים הבאים כבר משנה משמעותית את התמונה.
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. חיפוש מרוכז בכל הפתרון
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. בין תהליכים, בין מכונות, או רק קריאה אסינכרונית פשוטה | תכנון מחדש עם IPC, HTTP, gRPC, מבוסס Task |
AppDomain |
האם משתמשים רק בשם הטיפוס, או מבודדים עם CreateDomain |
אם המטרה בידוד, תהליך נפרד או AssemblyLoadContext (6.2) |
BinaryFormatter |
האם צריך לקרוא אחר כך את הנתונים שנכתבו. האם משתמשים בזה בעקיפין דרך ResX או clipboard | מעבר לסריאלייזר אחר, ותכנון לקריאה מחדש של הנתונים הקיימים (6.4) |
ServiceHost / ChannelFactory |
צד המארח או צד הלקוח | הערכה נפרדת לפי 5.6 |
packages.config / install.ps1 / web.config.install.xdt |
מה נכתב מחדש בזמן ההתקנה | המוקש מ-4.2. מחזיקים את ההגדרה מפורשות |
DllImport / AxInterop / Microsoft.Office.Interop |
הנחת מספר הביטים וכל קובצי הריצה הנדרשים | בדיקת bitness בפרק 9.4 |
עד כאן, מקבלים לא “האם אפשר לעבור”, אלא רשימה של “מה במסלול הסטנדרטי, ומה דורש אומדן נפרד”. זה בדיוק סוג הרשימה שרוצים לפני תחילת העבודה.
flowchart TB
accTitle: מהחיפוש לרשימה
accDescr: תרשים המראה שהחיפוש הוא הכניסה, ולכל מונח שנמצא בודקים את מקום ההמשך, וכך זה הופך לרשימה של מסלול סטנדרטי מול אומדן נפרד, כשמציאה בלבד אינה כישלון מיידי.
grep1["חיפוש הפתרון לפי מונחים"] --> nxt1["בדיקת המשך לכל מונח"]
nxt1 --> map1["הופך לרשימה של מסלול סטנדרטי מול אומדן נפרד"]
grep1 -.-> note2["מציאה בלבד אינה כישלון מיידי"]
איור 21: החיפוש עצמו הוא הכניסה, וכשמקדמים אותו עד ההחלטה, מתקבלת בדיוק הרשימה הרצויה לפני תחילת העבודה.
7. קובעים עד כמה מקבלים הנחת יסוד ייעודית ל-Windows
טעות נפוצה במעבר היא “אם עברנו ל-.NET, זה נהיה חוצה-פלטפורמות”. אין קסם כזה. אם היישום קשור עמוק ל-Windows, גם אחרי המעבר הוא נשאר ייעודי ל-Windows כרגיל.
7.1 להישאר ייעודיים ל-Windows במעבר הוא ריאלי לגמרי
ל-Microsoft יש Windows Compatibility Pack, שמאפשר לגשת מ-.NET מודרני להרבה API-ים של Windows כמו רישום המערכת, WMI, EventLog, שירות Windows ו-Directory Services.
הקיום שלו משמעותי בעבודה מעשית.
- רוצים קודם לעבור ל-.NET מודרני
- אבל בינתיים לא יוצאים מ-Windows
- אז רוצים לקבל בינתיים את תלות ה-API של Windows
בסביבה כזו, זו אפשרות חזקה.
המטרה הראשונה של המעבר לא חייבת להיות הפיכה לחוצה-פלטפורמות.
7.2 עם זאת, גם API ייעודי ל-Windows הוא “חוב שישפיע בהמשך”
עצם קיומו של Windows Compatibility Pack לא אומר שהכול בטוח.
- רוצים להכניס למכולת Linux
- רוצים להריץ בהנחת Kubernetes
- גם מפתחי macOS / Linux רוצים להריץ את אותו build
- בעתיד, רוצים להפחית מכונות Windows וירטואליות בענן
אם יש מטרות כאלה, בטוח יותר לחשוף כבר עכשיו את תלות ה-API של Windows.
flowchart TB
accTitle: מקום השימוש בחבילת התאימות והחוב שמאחוריה
accDescr: תרשים המראה שאם בינתיים נשארים על Windows, חבילת התאימות מאפשרת לקבל בינתיים את תלות ה-API של Windows ולעבור ל-.NET מודרני, אבל אם שואפים בעתיד למכולה או ענן, התלות היא חוב שישפיע בהמשך ולכן חושפים אותו כבר עכשיו.
now1["רוצים קודם לעבור ל-.NET מודרני"] -->|"בינתיים נשארים על Windows"| pack1["חבילת התאימות מקבלת בינתיים את התלות"]
pack1 -.->|"אם שואפים בעתיד למכולה / ענן"| debt1["התלות היא חוב שישפיע בהמשך"]
debt1 --> vis1["חושפים אותו כבר עכשיו"]
איור 22: חבילת התאימות היא גשר ריאלי, אבל התלות שלה עלולה להיות חוב לפי המטרה, ולכן חושפים אותה מראש.
7.3 System.Drawing.Common קל במיוחד לטעות בהבנתו
System.Drawing.Common הוא, מ-.NET 6 ואילך, ספרייה ייעודית ל-Windows.
אם יש קוד שמשתמש בעיבוד תמונות או ציור טקסט, צריך לקבוע קודם באיזה כיוון הולכים.
- ממשיכים לתפעל על Windows
- בעתיד רוצים להריץ גם על Linux / macOS
באפשרות הראשונה יש מקרים שאפשר להישאר כמו שזה בינתיים. באפשרות השנייה, צריך לכלול מראש בתכנון המעבר החלפה כמו SkiaSharp או ImageSharp.
flowchart TB
accTitle: ההסתעפות של System.Drawing.Common
accDescr: תרשים המראה ש-System.Drawing.Common ייעודי ל-Windows מ-.NET 6 ואילך, ולכן אם ממשיכים על Windows אפשר להישאר כמו שזה בינתיים, ואם רוצים להריץ גם על Linux או macOS צריך לכלול מראש החלפה כמו SkiaSharp או ImageSharp.
sd1["משתמשים ב-System.Drawing.Common"] -->|"ממשיכים על Windows"| ok4["אפשר להישאר כמו שזה בינתיים"]
sd1 -->|"רוצים גם Linux / macOS"| repl1["החלפה ל-SkiaSharp / ImageSharp"]
repl1 --> plan2["כוללים זאת מראש בתכנון המעבר"]
איור 23: מכיוון ש-System.Drawing.Common ייעודי ל-Windows, אופן הטיפול בו נחלק כבר בהתחלה לפי נקודת ההגעה.
7.4 סימנים ייצוגיים לקיבוע ל-Windows
כשיש הפניות או API-ים מהסוג הבא, בטוח יותר לאמוד “לפחות בהתחלה נשארים ייעודיים ל-Windows”.
Microsoft.Win32.RegistrySystem.ManagementSystem.Diagnostics.EventLogSystem.ServiceProcessSystem.DirectoryServicesSystem.DrawingDllImport/ P/Invoke- הפניית COM
AxInterop.*Microsoft.Office.Interop.*
8. אופן הוצאת הספרייה המשותפת קובע את הקושי
בפתרון גדול, אין הגזמה לומר שהצלחת המעבר נקבעת לפי אופן חיתוך ה-shared library.
8.1 קודם מסווגים
נוח יותר לסווג ספריות לשלושה סוגים עיקריים:
- לוגיקה עסקית / דומיין טהורה
- שכבת ביניים שתלויה מעט במודל היישום
- שכבה שצמודה ל-UI / Web / API של Windows
מבין אלה, מה שכדאי להעביר קודם הוא 1.
- חישוב
- הכרעת חוקים
- DTO / חוזה
- שירות דומיין
- המרת נתונים פשוטה
אם מוציאים את זה נקי, הקושי הכולל יורד בבת אחת.
flowchart TB
accTitle: שלוש הקטגוריות של הספריות וסדר ההעברה
accDescr: תרשים המראה שמחלקים ספריות ללוגיקה עסקית טהורה, שכבת ביניים תלויית מודל יישום, ושכבה צמודה ל-UI ול-API של Windows, ושמה שכדאי להעביר קודם הוא הלוגיקה העסקית הטהורה.
c1["לוגיקה עסקית טהורה"] -->|"מעבירים ראשונה"| ez1["אם מוציאים נקי, הקושי יורד"]
c2["שכבת ביניים תלויית מודל יישום"] -->|"אחריה"| ez1
c3["שכבה צמודה ל-UI / API של Windows"] -->|"אחרונה"| 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["צד .NET מודרני"] -->|"יכול להפנות"| ns1
ns21["netstandard2.1"] -.->|"Framework לא תומך"| fw1
איור 25: כגשר שנגיש גם מהישן וגם מהחדש, הפתרון המעשי הוא 2.0 ולא 2.1.
8.3 מה משתנה כשעוברים ל-netstandard2.0
| מדיניות | מה משתנה | מתי מתאים | הערה |
|---|---|---|---|
הפיכה ל-netstandard2.0 |
קל להפנות גם מהישן וגם מהחדש | לוגיקה עסקית טהורה, חוזה משותף, כלי עזר | לא ניתן לטעון API ייחודי למודל יישום |
multi-target (למשל: net48;net10.0) |
שומרים על קוד משותף ובכל זאת מחזיקים הפרש לפי סביבה | ספרייה עם הפרש קטן בין סביבות | מוסיף התניות וניהול build |
הפיכה ישירה ל-net10.0 בלבד |
הכי נקי בעתיד | שכבה חדשה שלא צריכה תלות בין ישן לחדש | לא ניתן להפניה מ-.NET Framework |
8.4 מצב תאימות אינו כל-יכול
ל-.NET Standard 2.0 יש מצב תאימות שמאפשר להפנות לספריות .NET Framework. עם זאת, זה לא קסם שגורם לכול לפעול באופן שקוף.
לדוגמה, ספרייה שמניחה API ייחודי למודל יישום כמו WPF קשה כרגיל. כלומר, גם אם אומרים shared library, חשוב להצר רק לאחריות שבאמת ניתנת לשיתוף.
8.5 בספריות מסוג ASP.NET, המפתח הוא האם אפשר להסיר את System.Web
כשמבצעים מעבר הדרגתי ל-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, אלא יעיל מאוד גם בפתרונות רגילים.
- מכיוון שהיעדים שהם תלות עלו קודם, קל יותר לראות את השכבה העליונה
- קל יותר לבודד בעיות תאימות
- קל יותר לבדוק ברמת ספרייה
flowchart TB
accTitle: מעלים בשיטת leaf-first
accDescr: תרשים המראה שאם מעלים את ספריות התמיכה מהעלים של התלות ראשון, שכבת הביניים אחריהן, ולבסוף שכבת היישום העליונה, קל יותר לבודד בעיות ולבדוק ברמת ספרייה.
lf1["מעלים ראשית את ספריות העלים"] --> lf2["מעלים את שכבת הביניים שסיימה"]
lf2 --> lf3["לבסוף שכבת היישום העליונה"]
lf1 -.-> loc1["אפשר לבודד ולבדוק בעיות"]
איור 26: כשמעלים מהעלים ראשון, עד שמגיעים לשכבה העליונה, הבסיס כבר מוכן.
9. סורקים NuGet / תלויות חיצוניות / רכיבי צד שלישי
אם עושים את זה ברשלנות, זה יכאב הכי הרבה בשלב השני של המעבר.
9.1 נוח יותר לחלק את התלויות לארבעה סוגים
- חבילת NuGet ציבורית
- חבילה פרטית פנימית / ספרייה internal
- הפניית DLL מקומית
- COM / ActiveX / DLL native / SDK
מתוכם, לא מספיק להסתכל רק על 1. מה שבאמת מסוכן הם 3 ו-4.
flowchart TB
accTitle: ארבע קטגוריות התלות ורמת הסיכון
accDescr: תרשים המראה שהתלויות מחולקות לחבילת NuGet ציבורית, חבילה פנימית, הפניית DLL מקומית, ו-COM/ActiveX/DLL native, ושהמסוכנים באמת הם ההפניה המקומית וה-COM/native.
dp1["ממפים תלויות"] --> k1["NuGet ציבורית / חבילה פנימית"]
dp1 --> k3["הפניית DLL מקומית"]
dp1 --> k4["COM / ActiveX / DLL native"]
k3 --> dg1["המסוכן באמת הוא הצד הזה"]
k4 --> dg1
איור 27: לא מספיק לספור NuGet ציבורי — קודם סורקים DLL מקומי ו-COM / native.
9.2 מה בודקים בכל תלות
לגבי כל תלות, לפחות בודקים עד כאן:
- האם היא ממוקדת ל-.NET מודרני
- האם היא תומכת ב-
PackageReference - האם אין בעיה עם סגנון SDK
- האם יש מגבלה ל-x86 / x64 / ARM64
- האם היא תלויה בכלי זמן עיצוב או בתוסף Visual Studio
- האם היא מניחה install script / config transform
- האם התמיכה עדיין נמשכת
9.3 UI / דוחות / רכיבי צד שלישי לזמן עיצוב — מאמדים בנפרד
במעבר WinForms / WPF / ASP.NET, זה משפיע משמעותית.
- Grid
- מנוע דוחות
- רכיב פלט PDF
- רכיב גרפים
- ספריית UI משולבת עם designer
- עטיפת ActiveX
אלה קשורים לא רק בזמן ריצה, אלא גם בתמיכה בזמן עיצוב. אם באומדן המעבר בודקים רק “האם זה מתקמפל”, בדרך כלל מפספסים אותם.
9.4 בודקים תמיד DLL native ו-bitness
גם אם נראה שרץ ב-AnyCPU בתקופת .NET Framework, ייתכן שבפועל יש תלות בדברים מהסוג הבא:
- COM שקבוע ל-
x86 - ActiveX ייעודי ל-32bit
- גרסה מסוימת של VC++ Runtime
- DLL native חתום
זו לא בעיה חדשה שצמחה עם ההפיכה ל-.NET מודרני, אלא מגבלה שהייתה קיימת מהתחלה, וצפה עכשיו. בדיוק בגלל זה שווה לחשוף אותה לפני המעבר.
flowchart TB
accTitle: הצפת מגבלת ה-bitness
accDescr: תרשים המראה שגם אם נראה שרץ ב-AnyCPU, ייתכן תלות ב-COM קבוע ל-x86, ActiveX ל-32bit או גרסה מסוימת של VC++ Runtime, ושאלה מגבלות שהיו קיימות מהתחלה שצפות בגלל המעבר.
any1["נראה שרץ ב-AnyCPU"] -.-> hid3["תלות ב-COM x86, ActiveX 32bit וכדומה"]
hid3 --> surf1["המעבר גורם למגבלה הקיימת לצוף"]
surf1 --> pre2["חושפים אותה לפני המעבר"]
איור 28: בעיית ה-bitness לא נוצרת על ידי המעבר, אלא מגבלה קיימת שנעשית גלויה.
10. מטפלים ב-EF6, בסריאלייזרים ובנתונים כבעיה נפרדת
עדיף ככל האפשר להתייחס בנפרד למעבר זמן הריצה ולתכנון מחדש של גישת הנתונים והסריאליזציה.
10.1 המעבר מ-EF6 ל-EF Core הוא לא שדרוג ישיר
גם במדריך ה-EF של Microsoft כתוב ש-EF Core הוא כתיבה מחדש מלאה של EF6, ואין נתיב שדרוג ישיר.
לכן, ביישום שמשתמש ב-EF6, הסדר הבא ריאלי:
- קודם עוברים ל-.NET מודרני
- אם צריך, ממשיכים להריץ עם EF6 כמו שהוא
- אחר כך מעבירים ל-EF Core כפרויקט נפרד
לא לערבב בין runtime migration ל-ORM migration. רק זה כבר מוריד משמעותית את הקושי.
flowchart TB
accTitle: הפרדת המעבר של EF6 מזה של runtime
accDescr: תרשים המראה ש-EF Core הוא כתיבה מחדש מלאה של EF6 בלי נתיב שדרוג ישיר, ולכן ריאלי לעבור קודם ל-.NET מודרני, להמשיך עם EF6 אם צריך, ורק אחר כך להעביר ל-EF Core כפרויקט נפרד.
e1["קודם עוברים ל-.NET מודרני"] --> e2["ממשיכים עם EF6 אם צריך"]
e2 --> e3["אחר כך מעבירים ל-EF Core כפרויקט נפרד"]
e1 -.->|"אם עושים בו-זמנית"| mix1["הבדלי התנהגות ה-ORM מתערבבים ומכבידים"]
איור 29: לא מריצים בו-זמנית runtime ו-ORM — מעשי יותר להשאיר את EF6 קודם ולהעביר את זמן הריצה.
10.2 מה משתנה כשמשאירים EF6
- יתרון
- אפשר לדחות את ההפרש בשכבת גישת הנתונים
- אפשר להתמקד בהעברת לוגיקת העסק ומודל היישום
- “הבדל התנהגות של EF Core” לא מתערבב
- הערה
- מנקודת מבט של פיתוח חדש, EF Core הוא הבחירה העיקרית
- יש מגבלות נפרדות לצורת שימוש עם EF6 Designer / EDMX
10.3 EF6 מבוסס EDMX דורש התייחסות גם “בזמן עיצוב”
בתיעוד EF6 כתוב ש-EF Designer לא נתמך ישירות בפרויקטי .NET / .NET Standard, או בפרויקטי .NET Framework בסגנון SDK.
ביישום מבוסס EDMX, צריך להתייחס בנפרד לשלוש נקודות אלה:
- האם זה פועל בזמן ריצה
- האם ה-Designer שמיש
- איך מטפלים בקוד שנוצר
אם יש שימוש נרחב ב-EDMX, בטוח יותר לכלול את זה באומדן כבר מההתחלה.
10.4 BinaryFormatter וסריאליזציה עצמית נוטים להיות “תלות נסתרת”
קל לפספס סריאלייזרים רק בעזרת חיפוש קוד.
- פורמט נתונים לשמירה
- מסרים
- מטמון
- חוזה WCF / SOAP ישן
- ResX
- clipboard / drag & drop
אלה קשורים גם לתאימות נתונים. כלומר, לא מספיק “האם ה-build עובר”, אלא נדרשת בדיקה עד להאם אפשר לקרוא נתונים ישנים.
11. כוללים גם תצורה, הפצה, תפעול ו-CI/CD ביעד המעבר
יעד המעבר הוא לא רק קוד המקור.
11.1 קובצי תצורה
בצד .NET Framework, לעיתים יש הרבה מוטמע ב-app.config / web.config.
- מחרוזת חיבור
- section תצורה מותאם
- הגדרת endpoint של WCF
- binding redirect
- אבחון
- הגדרות שונות של ASP.NET
- תוצאת ה-transform בזמן התקנת חבילה
בצד .NET מודרני יש מקרים שבהם אופן החזקת התצורה ומסלול הטעינה משתנים. לכן “נטפל בקבצי התצורה אחר כך” מסוכן.
מה שעושים ראשון הוא מיפוי התצורה.
- מה מוטמע בקובץ התצורה
- מה חובה בזמן הפעלת היישום
- מה הפרש שבין סביבות
- מה הוזרק אוטומטית על ידי NuGet או installer
11.2 שיטת ההפצה
גם צורת ההפצה נבדקת לפני תחילת העבודה.
- תחת IIS
- שירות Windows
- Scheduled Task
- ClickOnce / MSI / installer עצמאי
- הנחת שרת מקומי
- מה מתאים יותר — self-contained או framework-dependent
גם אם גוף ההרצה עובר, אם מנגנון ההפצה וההפעלה נשארים בהנחה ישנה, זה נתקע בסוף.
11.3 לוגים, ניטור, נהלי תפעול
גם סביב התפעול קל לפספס.
- האם מבוסס Windows Event Log
- האם מסתכלים על Performance Counter
- האם ניטור מבוסס WMI
- האם חשבון השירות וההרשאות קבועים
- האם יעד פלט הלוגים מניח קובץ מקומי
קורה בהחלט שהקוד רץ אחרי המעבר, אבל התפעול לא מסתובב.
11.4 CI/CD ו-build agent
לפני המעבר, בודקים גם את אלה:
- האם ה-
.NET SDKהנדרש נכנס ל-build agent - מה עושים עם pipeline שמניח
nuget.exe/msbuild.exe - האם עוברים בסיס
dotnetCLI - איך מעדכנים את עבודות הריצת הבדיקות, coverage ו-publish
- האם תבנית פנים-ארגונית או reusable pipeline מניחים צורה ישנה
עובד בסביבה המקומית של האדם אבל ה-CI מת זו תופעה נפוצה במעבר.
flowchart TB
accTitle: יעד המעבר הוא לא רק הקוד
accDescr: תרשים המראה שגם אם גוף ההרצה עובר, אם קובצי התצורה, שיטת ההפצה, הלוגים והניטור, וה-CI/CD ו-build agent נשארים בהנחה ישנה, זה נתקע בסוף, ולכן כל אלה נכללים ביעד המעבר.
code1["מעבר קוד המקור"] --> also1["זה לא מסתיים בזה בלבד"]
also1 --> cfg1["קובצי תצורה ושיטת הפצה"]
also1 --> ops1["לוגים, ניטור, נהלי תפעול"]
also1 --> ci1["CI/CD ו-build agent"]
ci1 -.-> gotcha1["עובד מקומית אבל ה-CI מת"]
איור 30: רק כשכוללים תצורה, הפצה, תפעול ו-CI/CD, זה נחשב שסופרו כל יעדי המעבר.
12. אופן ההתקדמות המעשי של המעבר
לאור הסוגיות עד כאן, האופן המעשי להתקדם נופל בדרך כלל לצורה הבאה.
12.1 קודם מסדרים את צד ה-Framework הנוכחי
- עלייה ל-.NET Framework 4.7.2 ומעלה, אם אפשר ל-4.8.1
- עדכון התלויות
- בדיקה מחדש של
packages.config - ככל האפשר, מעבר ל-
PackageReferenceולסגנון SDK - אימות שהיישום הנוכחי רץ תקין במצב הזה
רק ביצוע השלב הזה מצמצם משמעותית את ההפרש בשלב הבא.
12.2 מצילים קודם shared library
בשלב הבא, מעבירים לוגיקה עסקית וחוזים משותפים ל-netstandard2.0 או ל-multi-target.
סדר ההעלאה הבסיסי הוא leaf-first.
12.3 מחליפים אסטרטגיה לגוף היישום לפי מודל היישום
- ספריית מחלקות / מסוף / חלק מהשירותים יחסית קל להתקדם ישירות
- WinForms / WPF מודרניזציה תוך שמירה על ייעודיות ל-Windows
- ASP.NET MVC / Web API אם קטן — בבת אחת, אם כבד — הדרגתי
- Web Forms בהנחת החלפת נכסי המסך, מוציאים תחילה לוגיקה משותפת
- שרת WCF קובעים קודם — שמירת CoreWCF, או תכנון מחדש עם gRPC
12.4 שומרים על “לא לעשות הכול בבת אחת”
בפרט, מה שכדאי להימנע ממנו הוא שילובים מהסוג הבא:
- מעבר runtime + החלפה כוללת ל-ORM
- מעבר runtime + שינוי תשתית אימות
- מעבר runtime + מעבר מלא לענן
- מעבר runtime + שינוי תשתית ניטור
- מעבר runtime + חידוש תשתית UI
גם אם כולם נדרשים בסופו של דבר, לא לצבור אותם באותו ספרינט לרוב מוביל להצלחה.
12.5 פועלים רק אחרי שיש בדיקות וקו בסיס
לפחות, כדאי להכין את זה לפני שמתחילים לפעול:
- בדיקות יחידה
- בדיקות אינטגרציה לזרימות עסק מרכזיות
- אימות “תמונת מצב” למסכים / API-ים ייצוגיים
- קו בסיס ביצועים
- שיטת אימות לוגים מרכזיים
- נוהל rollback
להתקדם במצב שבו אי אפשר לזהות “מה נשבר” אחרי המעבר, מסוכן למדי.
flowchart TB
accTitle: מה מכינים לפני שמתחילים לפעול
accDescr: תרשים המראה שאם מכינים בדיקות וקו בסיס, לוגים ונהלי rollback לפני שמתחילים לפעול במעבר, אפשר לזהות מה נשבר אחריו, ואילו התקדמות בלי הכנה מסוכנת.
prep1["הכנת בדיקות וקו בסיס"] --> mv2["רק אז מתחילים לפעול במעבר"]
mv2 --> det1["אפשר לזהות מה נשבר"]
non2["התקדמות בלי הכנה"] -.-> risk1["אי-זיהוי במקרה תקלה — מסוכן"]
איור 31: המעבר נקבע לפי ההכנה שלפני הפעולה, ורק עם קו בסיס אפשר לזהות מה נשבר.
13. רשימת בדיקה לפני תחילת העבודה
מונח כאן בצורה שאפשר להדביק ישירות לניהול פרויקט.
13.1 מדיניות
- אפשר להסביר במשפט אחד למה עושים את המעבר
- נקבע האם נקודת ההגעה הפעם היא .NET מודרני ייעודי ל-Windows, או חוצה-פלטפורמות בעתיד
- נקבעה גרסת .NET היעד
- נקבע מה לא נכלל בהיקף הזה (הפיכה ל-EF Core, חידוש אימות, מעבר מלא לענן וכדומה)
13.2 סידור צד .NET Framework הנוכחי
- הועלה ל-.NET Framework 4.7.2 ומעלה, אם אפשר ל-4.8.1
- התלויות עלו לגרסה עדכנית
- נסרק קיום
packages.config - נבדקה היתכנות הפיכה ל-
PackageReference - נבדקה היתכנות מעבר לסגנון SDK
- היישום הנוכחי מסוגל ל-build, הפעלה ובדיקה במצב הזה
13.3 סוג היישום ובחירת טכנולוגיה
- הקושי חולק לפי סוג — ספריית מחלקות / שולחני / Web / WCF וכדומה
- מובן ש-WinForms / WPF נשארים ייעודיים ל-Windows
- מובן ש-ASP.NET Framework הוא מעבר מודל יישום ל-ASP.NET Core
- הוכללה באומדן החלפת שכבת UI ל-Web Forms
- WCF הוערך בנפרד ללקוח ולשרת
13.4 טכנולוגיות לא נתמכות / API-ים שדורשים תשומת לב
- נסרקה תלות ב-
AppDomain - נסרקו Remoting /
MarshalByRefObject/BeginInvoke/EndInvoke - נסרקו CAS / Security Transparency / COM+ / WF
- נסרקה תלות ב-BinaryFormatter
- נסרקה תלות ב-
System.Web
13.5 תלות ייעודית ל-Windows
- נסרק שימוש ברישום המערכת, WMI, EventLog, שירות Windows, Directory Services
- נסרק שימוש ב-
System.Drawing.Common - נסרקו COM / ActiveX / Office Interop / P/Invoke / DLL native
- נבדקה מגבלת x86 / x64 / ARM64
13.6 shared library וגישה לנתונים
- shared library סווגו ללוגיקה עסקית / שכבה צמודה למודל יישום
- נסרק מה ניתן להפוך ל-
netstandard2.0 - נסרקו ספריות שדורשות multi-target
- נקבע האם אפשר להשאיר EF6 ולהעביר קודם רק את זמן הריצה
- נבדקה תלות EDMX / Designer
13.7 תפעול ו-build
- בוצע מיפוי קובצי תצורה
- נבדקה שיטת ההפצה (IIS / Service / MSI / ClickOnce וכדומה)
- נבדקו הנחות לוגים / ניטור / הרשאות / חשבון הרצה
- נבדק האם נדרש עדכון ל-CI/CD ול-build agent
- הוכן נוהל rollback
14. סיכום
במעבר מ-.NET Framework ל-.NET, מה שחשוב הוא לא “באיזו פקודה מעבירים”, אלא לזהות לפני תחילת העבודה מה עובר כמו שהוא ומה בעיה נפרדת.
אם מרכזים לנקודות, הנה שש אלה:
- מסדרים את צד .NET Framework לפני המעבר
- מחלקים את הקושי לפי מודל היישום
- סורקים קודם טכנולוגיות שלא ניתן להשתמש בהן
- קובעים עד כמה מקבלים הנחת יסוד ייעודית ל-Windows
- קובעים איך חותכים את ה-shared library
- לא לוקחים יותר מדי בבת אחת מ-ORM, אימות ומעבר לענן
השבוע הראשון של המעבר משנה משמעותית את דיוק האומדן. במילים אחרות, אם מסדרים את הסוגיות באותו שבוע, המחצית השנייה מתקרבת הרבה יותר לפיתוח רגיל.
flowchart TB
accTitle: השימוש בשבוע הראשון
accDescr: תרשים המראה שאם בשבוע הראשון מסדרים את הסוגיות של מה עובר כמו שהוא ומה בעיה נפרדת, דיוק האומדן עולה והמחצית השנייה קרובה יותר לפיתוח רגיל.
wk1["סידור הסוגיות בשבוע הראשון"] --> est1["דיוק האומדן עולה"]
est1 --> nor1["המחצית השנייה קרובה יותר לפיתוח רגיל"]
wk1 -.-> pts2["הפרדה בין מה שעובר כמו שהוא למה שבעיה נפרדת"]
איור 32: האם השבוע הראשון מנוצל למיפוי, קובע את דיוק המעבר כולו.
“בואו פשוט ננסה לשנות ל-net10.0” הוא לא רעיון רע לצורך חקירה.
עם זאת, כמעבר בפועל לייצור, יש מה לבדוק לפני זה. זה בדיוק מה שסידרנו במאמר הזה.
15. מקורות
- תנאי סף להעברת קוד
- סקירת ההעברה מ-.NET Framework ל-.NET
- טכנולוגיות .NET Framework שלא ניתן להשתמש בהן מ-.NET 6 ואילך
- מהי מודרניזציה בעזרת GitHub Copilot
- התקנת מודרניזציה בעזרת GitHub Copilot
- סקירת .NET Upgrade Assistant - כיום לא מומלץ, ומופנים למודרניזציה בעזרת GitHub Copilot.
- מדיניות התמיכה הרשמית של .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
- מדריך מעבר BinaryFormatter
- מדריך מעבר 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
מאמרים קשורים
מאמרים עדכניים עם אותן תגיות, להעמקה בנושאים קרובים.
איך קוראים ל-DLL של C# Native AOT מ-C/C++
פרסום ספריית מחלקות C# כ-DLL נייטיבי באמצעות Native AOT, וקריאה לנקודות הכניסה מסוג UnmanagedCallersOnly מ-C/C++ — סקירה לפי מקום השימוש...
למה להשתמש ב-Generic Host וב-BackgroundService של .NET באפליקציית desktop
בכלי Windows ובאפליקציות שרצות ברקע, מסודר כאן איך להשתמש ב-Generic Host וב-BackgroundService כדי לסדר הפעלה, עיבוד תקופתי, סיום, לוג, הג...
איך מתייחסים היום ל-ActiveX / OCX — טבלת החלטה: לשמר, לעטוף או להחליף
כשמוצאים ActiveX / OCX, מסודר כאן איך לבחור בין לשמר, לעטוף או להחליף — כולל 32bit / 64bit, רישום, תלות בדפדפן ותחזוקת ספקים.
מדריך מעשי ל-FileSystemWatcher — התמודדות עם פספוסים וכפילויות
המאמר מסדר את אופן השימוש ואת נקודות התשומת לב של FileSystemWatcher, מנקודת המבט של פספוסים, התראות כפולות, מלכודות בקביעת סיום, סריקה חו...
רשימת בדיקה לטיפול בטוח בתהליכי ילד ביישום Windows
כדי לטפל בבטחה בתהליכי ילד ביישום Windows, תכנון הבעלות על עץ התהליכים ונוהל הסיום חשוב יותר מבחירת ה-API להפעלה. המאמר מסדר את Job Objec...
נושאים קשורים
העמודים האלה ממקמים את הנושא בהקשר רחב יותר של שירותים והחלטות.
נושאים טכניים ב-Windows
שער לנושאי פיתוח Windows, חקירת תקלות וניצול נכסים קיימים.
שירותים הקשורים לנושא הזה
המאמר קשור ישירות לשירותים הבאים.
שימוש חוזר והעברה של נכסים קיימים
מדובר בסידור נכסים קיימים שכולל .NET Framework, Web Forms, WCF, COM / ActiveX ותפעול NuGet ישן, ולכן זה נושא שמתאים היטב לייעוץ מעבר נכסים קיימים.
ייעוץ טכני וסקירת תכנון
אם רוצים לסדר לפני התחלת העבודה את היקף המעבר, אופן חלוקת המעבר ההדרגתי, ועד כמה מקבלים הנחת יסוד של Windows בלבד, זה נושא שקל להתקדם בו כייעוץ טכני וסקירת תכנון.
שאלות נפוצות
שאלות נפוצות בפניות בנושא המאמר.
- מה כדאי לעשות קודם לפני מעבר מ-.NET Framework ל-.NET?
- לפני שנכנסים למימוש, קודם מסיימים את הסידור בצד .NET Framework. גם המדריך הרשמי של Microsoft ממליץ, לפני ההעברה, לעלות ל-.NET Framework 4.7.2 ומעלה (בעבודה מעשית, אם אפשר, 4.8.1), להפוך את packages.config ל-PackageReference, לעבור לסגנון פרויקט SDK, ולעדכן תלויות לגרסה עדכנית יותר. אם עושים את זה קודם, ההפרש בשלב הבא של המעבר ל-.NET מודרני מצטמצם משמעותית, וקל יותר להבחין אם מקור בעיית תאימות הוא 'ה-Framework הישן' או 'המעבר ל-.NET'. במקביל, גם מבצעים מיפוי של טכנולוגיות לא נתמכות כמו AppDomain, Remoting ו-BinaryFormatter לפני תחילת העבודה.
- האם אפשר להחליט להישאר עם .NET Framework?
- כן, זו החלטה סבירה לגמרי. .NET Framework 4.8.1 ממשיך לקבל תמיכה כל עוד הוא מותקן על Windows נתמך, ולכן זה לא מצב שבו חייבים מיד להעביר הכול ל-.NET מודרני. אם יש נכסי מסך רבים ב-Web Forms, צריך לשמור בקפדנות על תאימות שרת WCF, יש תלות עמוקה ב-Workflow Foundation או ב-COM+, או שרכיבי צד שלישי לזמן עיצוב לא נתמכים — הגיוני להישאר בינתיים על 4.8.1 עם תפעול יציב, ולתכנן החלפה בקו נפרד. עם זאת, נשארות מגבלות כמו אי-יכולת לצאת מ-Windows בלבד, וקושי ליהנות מביצועים ותכונות שפה חדשות של .NET.
- אילו טכנולוגיות לא ניתן להעביר ל-.NET, או שקל להיתקע בהן?
- יצירת AppDomain, .NET Remoting, CAS (אבטחת גישה לקוד), COM+ (System.EnterpriseServices) ו-Workflow Foundation לא נתמכים ב-.NET מודרני, ואלה תמרורי אזהרה שדורשים תכנון מחדש. שרת WCF לא רץ כמו שהוא built-in, ויש לבחור בין תכנון מחדש עם CoreWCF ל-gRPC / HTTP API. BinaryFormatter, מ-.NET 9 ואילך, לא כולל מימוש וזורק תמיד חריגה, ולכן נדרש ביקורת שכוללת נתונים שנשמרו, ResX וגם clipboard / drag & drop. בנוסף, המרות install.ps1 / XDT של packages.config, DLL native, COM / ActiveX, והנחת x86 — כל אלה נקודות שדורשות תשומת לב, כי גם אם ה-build עובר, קל ליפול בזמן ריצה.
- האם מעבר יישום WinForms או WPF ל-.NET הופך אותו לחוצה-פלטפורמות?
- לא. WinForms / WPF יכולים לעבור ל-.NET, אבל הם נשארים תשתיות ייעודיות ל-Windows. מה שמתקבל מהמעבר הוא ההטבות של זמן ריצה, תכונות שפה, סגנון SDK ותאימות ל-CI/CD של .NET מודרני, אבל זה לא הופך אותם לתומכים במכולות Linux. אם שואפים בעתיד ל-Linux / הכלה במכולה, צריך לבצע מיפוי מוקדם ל-API-ים שמניחים Windows כמו System.Drawing.Common, רישום המערכת, WMI, EventLog, שירות Windows ו-Office Interop. לעומת זאת, אם ממשיכים להישאר ייעודיים ל-Windows, אפשר לבחור במסלול מעשי שמפעיל תחילה מודרניזציה של זמן הריצה בעזרת Windows Compatibility Pack.