Skip to content

การทำรองรับหลายเวอร์ชัน (Multi-Version)

ปัญหาโลกแตกของคนทำ Revit Plugin คือ “ผู้ใช้งานในบริษัทใช้ Revit คนละเวอร์ชันกัน”

Revit มีการเปลี่ยน API ในทุกๆ ปี (เช่น โค้ดที่รันบน 2024 ได้ อาจจะพังบน 2026) ดังนั้นเราจึงไม่ควรรวมทุกเวอร์ชันไว้ใน TargetFramework เดียวกัน เราต้องแยกร่างออกเป็นโปรเจ็กต์ย่อยๆ หรือใช้ Shared Project

วิธีที่นิยมในปัจจุบันและได้รับการยอมรับระดับสากล จะมี 2 แนวทางหลักครับ

แนวทางที่ 1: ใช้ Shared Project (เก่าแต่ชัวร์)

Section titled “แนวทางที่ 1: ใช้ Shared Project (เก่าแต่ชัวร์)”

จุดอ่อนของการสร้างโปรเจ็กต์ RevitToolkit2024, RevitToolkit2025, RevitToolkit2026 แยกกัน คือคุณต้อง Copy-Paste โค้ดซ้ำไปซ้ำมา

วิธีแก้คือเอาโค้ดกลางไปใส่ใน “Shared Project”

  1. ให้สร้างโปรเจ็กต์แบบ Shared Project เปล่าๆ ชื่อ RevitToolkit.Shared
  2. ย้ายไฟล์ .cs และโค้ดทั้งหมด (XAML, Config) ไปไว้ข้างในนั้น
  3. ในโปรเจ็กต์เวอร์ชันเฉพาะกาล (เช่น 2026) ให้กด Add Reference > Shared Projects > ติ๊กเลือก RevitToolkit.Shared

แค่นี้เวลาคุณแก้โค้ดใน 2024 ก็จะกระเทือนไปถึง 2026 ด้วยทันที!

แนวทางที่ 2: ใช้ Directive #if (สมัยใหม่ และยอดฮิต)

Section titled “แนวทางที่ 2: ใช้ Directive #if (สมัยใหม่ และยอดฮิต)”

ถ้าคุณรวบทุกเวอร์ชันไว้ใน .csproj เดียวด้วยแท็ก <TargetFrameworks>net48;net8.0-windows</TargetFrameworks> คุณจะเจอปัญหาบรรทัดแดงเวลา API ของ 2 เวอร์ชันตีกัน

เราจึงใช้ Preprocessor Directives เพื่อบอก Compiler ให้ตัดโค้ดบางส่วนทิ้งตอน Build สำหรับเวอร์ชันนั้นๆ

1. ใส่ตัวแปร Revit เข้าไปใน .csproj

Section titled “1. ใส่ตัวแปร Revit เข้าไปใน .csproj”
อัปเดตไฟล์ .csproj
<!-- ถ้ากำลัง Build ของปี 2027 (ใช้ .NET 10) -->
<PropertyGroup Condition="'$(Configuration)' == 'Revit2027'">
<TargetFramework>net10.0-windows</TargetFramework>
<DefineConstants>REVIT2027</DefineConstants>
</PropertyGroup>
<!-- ถ้ากำลัง Build ของปี 2026 (ใช้ .NET 8) -->
<PropertyGroup Condition="'$(Configuration)' == 'Revit2026'">
<TargetFramework>net8.0-windows</TargetFramework>
<DefineConstants>REVIT2026</DefineConstants>
</PropertyGroup>
<!-- ถ้ากำลัง Build ของปี 2024 (ใช้ .NET 4.8) -->
<PropertyGroup Condition="'$(Configuration)' == 'Revit2024'">
<TargetFramework>net48</TargetFramework>
<DefineConstants>REVIT2024</DefineConstants>
</PropertyGroup>

สมมติว่า Revit API ในปี 2024 ใช้คำสั่ง GetUnit() แต่พอมาปี 2026 เปลี่ยนชื่อเป็น GetElementUnit()

คุณสามารถเขียนไฟล์ .cs เดียวกันให้รองรับ 2 แบบได้:

Command.cs (เวอร์ชันแชร์)
public double ดึงความยาวคาน(Element beam)
{
#if REVIT2026
// โค้ดบรรทัดนี้จะถูกรวมเข้าไปเฉพาะตอนเราเลือกตลับ Build เป็นของปี 2026
return beam.GetElementUnit();
#elif REVIT2024
// ส่วนนี้จะทำงานก็ต่อเมื่อเราสลับไป Build ตัว 2024 ให้ลูกค้าที่ยังไม่อัปเกรดเครื่อง
return beam.GetUnit();
#endif
}

🔍 เจาะลึกความท้าทายระดับสูง: การย้ายเฟรมเวิร์กและระบบหน่วยวัด (Units Migration)

Section titled “🔍 เจาะลึกความท้าทายระดับสูง: การย้ายเฟรมเวิร์กและระบบหน่วยวัด (Units Migration)”

เมื่อนักพัฒนาเริ่มก้าวขึ้นสู่การทำปลั๊กอินรองรับหลายเวอร์ชันพร้อมกัน (Multi-targeting) มีมิติเทคนิคสำคัญ 2 ประการที่มักสร้างความปวดหัวให้กับจูเนียร์เดฟอย่างมาก ซึ่งเราจะมาชำแหละวิเคราะห์แนวทางแก้ไขดังนี้ครับ:

1. การปะทะกันของสถาปัตยกรรม .NET (.NET 4.8 vs .NET 8.0 vs .NET 10.0)

Section titled “1. การปะทะกันของสถาปัตยกรรม .NET (.NET 4.8 vs .NET 8.0 vs .NET 10.0)”

นี่คือความเปลี่ยนแปลงครั้งใหญ่ที่สุดในประวัติศาสตร์ของ Revit API:

  • Revit 2021 - 2024: ทำงานบนเทคโนโลยีแบบเก่าคือ .NET Framework 4.8
  • Revit 2025 - 2026: ก้าวข้ามผ่านสู่ยุคโมเดิร์นบน .NET 8.0
  • Revit 2027+: ล้ำหน้าไปอีกขั้นด้วยการย้ายโครงสร้างสู่ .NET 10.0
  • ปัญหาทางปฏิบัติ: .NET เวอร์ชันต่างกันมีชุดไลบรารีเบื้องหลังหลายชิ้นไม่เหมือนกัน เช่น ระบบหน้าต่าง WPF หรือการประมวลผล JSON
  • ทางออกที่ดีที่สุด: เมื่อเราประกาศใช้ <TargetFrameworks>net48;net8.0-windows;net10.0-windows</TargetFrameworks> ให้เราใช้สัญลักษณ์ #if REVIT2024 คอยดักจับการเรียกใช้คลาสพิเศษที่ยกเลิกไป หรือใช้ชุดเงื่อนไขการติดตั้ง Reference DLL ในไฟล์ .csproj แยกประเภทการอ้างอิงให้ชัดเจนตามบล็อกสวิตช์ Configuration ครับ

2. มหากาพย์การเปลี่ยนผ่านระบบหน่วยวัด (ForgeTypeId vs DisplayUnitType)

Section titled “2. มหากาพย์การเปลี่ยนผ่านระบบหน่วยวัด (ForgeTypeId vs DisplayUnitType)”

ใน Revit API ทุกเวอร์ชันหลังบ้านจะคำนวณและเก็บมิติวัตถุ (ระยะทาง, ความสูง, ปริมาตร) เป็นหน่วยวัดสากลคือ “ฟุต (Feet)” เสมอ หากปลั๊กอินต้องการแปลงข้อมูลหน่วยวัดให้กลายเป็นระบบเมตรหรือมิลลิเมตรที่เราใช้กันในแบบก่อสร้างทั่วไป:

  • แบบเก่า (Revit 2021 และเก่ากว่า): ใช้รหัสระบบตัวแปรแจกแจง DisplayUnitType เช่น:
    // วิธีแบบเก่า (Deprecated / ถูกยกเลิกไปแล้วในเวอร์ชันใหม่)
    double widthInMm = UnitUtils.ConvertFromInternalUnits(widthInFeet, DisplayUnitType.DUT_MILLIMETERS);
  • แบบใหม่ (Revit 2022 - 2026+): มีการยกเลิก DisplayUnitType ไปอย่างถาวร และเปลี่ยนไปใช้คลาสระบุตัวตนสากลของ Autodesk ที่เรียกว่า ForgeTypeId เพื่อรองรับการระบุหน่วยของระบบคลาวด์ร่วมด้วย:
    // วิธีสากลแบบใหม่ (แนะนำสำหรับยุคปัจจุบัน)
    double widthInMm = UnitUtils.ConvertFromInternalUnits(widthInFeet, UnitTypeId.Millimeters);

💡 โค้ดตัวอย่างการเขียนรองรับหน่วยวัดแบบผสมผสาน (Cross-version Unit Converter)

Section titled “💡 โค้ดตัวอย่างการเขียนรองรับหน่วยวัดแบบผสมผสาน (Cross-version Unit Converter)”

หากคุณจำเป็นต้องเขียนสคริปต์แปลงหน่วยฟุตให้เป็นมิลลิเมตร โดยให้รันผ่านได้ทั้งบน Revit 2021 (แบบเก่า) และ Revit 2026 (แบบใหม่) ในไฟล์ซอร์สโค้ดกลางชุดเดียวกัน ให้ใช้สคริปต์ผสมผสานนี้ครับ:

UnitConverter.cs
using Autodesk.Revit.DB;
namespace RevitToolkit;
public static class UnitConverter
{
public static double ConvertFeetToMm(double feetValue)
{
#if REVIT2027 || REVIT2026 || REVIT2025
// สำหรับ Revit เวอร์ชันใหม่ (2025-2027+) ที่เปลี่ยนไปใช้ ForgeTypeId
return UnitUtils.ConvertFromInternalUnits(feetValue, UnitTypeId.Millimeters);
#else
// สำหรับ Revit เวอร์ชันเก่าที่ยังคงค้างอยู่กับระบบเดิม
return UnitUtils.ConvertFromInternalUnits(feetValue, DisplayUnitType.DUT_MILLIMETERS);
#endif
}
}

การทำความเข้าใจกับจุดแตกต่างทางเทคนิคของเวอร์ชัน .NET และการเตรียมรับมือกับการตัดถอนคำสั่งเก่า (Deprecated APIs) เหล่านี้ จะช่วยแต่งตั้งให้คุณมีทักษะระดับสถาปนิกซอฟต์แวร์ที่สามารถสร้างปลั๊กอินแชร์งานดูแลรักษาในองค์กรได้อย่างยั่งยืนและแข็งแกร่งที่สุดครับ!