ASMX Web Service 方法參數預設為具體型別。若需傳遞**繼承階層物件** (基底類別參數接收衍生類別實例),需讓 XML 序列化器識別衍生型別,否則反序列化時只會產生基底類別實例,衍生屬性遺失。
在 Web 方法或基底類別標註 `[XmlInclude(typeof(DerivedType))]`,告知 `XmlSerializer` 可能出現的衍生型別。
public abstract class Vehicle
{
public string LicenseNumber { get; set; }
public DateTime MakeTime { get; set; }
}
public class Car : Vehicle
{
public int DoorCount { get; set; }
public string EngineType { get; set; }
}
public class Truck : Vehicle
{
public double LoadCapacityTons { get; set; }
public int AxleCount { get; set; }
}
[WebService(Namespace = "https://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class VehicleService : System.Web.Services.WebService
{
[WebMethod]
[XmlInclude(typeof(Car))]
[XmlInclude(typeof(Truck))]
public string RegisterVehicle(Vehicle vehicle)
{
if (vehicle is Car car)
return $"Car registered: {car.LicenseNumber}, Doors: {car.DoorCount}";
if (vehicle is Truck truck)
return $"Truck registered: {truck.LicenseNumber}, Capacity: {truck.LoadCapacityTons}t";
return $"Vehicle registered: {vehicle.LicenseNumber}";
}
}
<xs:complexType name="Vehicle">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="LicenseNumber" type="xs:string" />
<xs:element minOccurs="1" maxOccurs="1" name="MakeTime" type="xs:dateTime" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="Car">
<xs:complexContent mixed="false">
<xs:extension base="tns:Vehicle">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="DoorCount" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" name="EngineType" type="xs:string" />
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
關鍵:`
[XmlInclude(typeof(Car))]
[XmlInclude(typeof(Truck))]
[XmlType(Namespace="https://tempuri.org/")]
public abstract partial class Vehicle { ... }
public partial class Car : Vehicle { ... }
public partial class Truck : Vehicle { ... }
var client = new VehicleServiceSoapClient();
var car = new Car
{
LicenseNumber = "ABC-1234",
MakeTime = DateTime.Now,
DoorCount = 4,
EngineType = "Hybrid"
};
string result = client.RegisterVehicle(car); // 正確序列化 Car 型別
var truck = new Truck
{
LicenseNumber = "TRK-999",
MakeTime = DateTime.Now,
LoadCapacityTons = 15.5,
AxleCount = 3
};
result = client.RegisterVehicle(truck); // 正確序列化 Truck 型別
[XmlInclude(typeof(Car))]
[XmlInclude(typeof(Truck))]
[XmlInclude(typeof(Motorcycle))]
public abstract class Vehicle { ... }
優點:所有使用 `Vehicle` 參數的 Web 方法自動支援多型,無需重複標註。缺點:基底類別耦合所有衍生類別,違反開放封閉原則。
| 限制 | 說明 | 替代方案 |
|---|---|---|
| 僅支援已知衍生型別 | 需預先在 XmlInclude 列出,動態載入外掛型別不支援 | 改用 WCF `KnownTypeAttribute` / `ServiceKnownType`,支援動態型別解析器 |
| 序列化為 XML,體積較大 | 不適合高頻、大量資料傳輸 | WCF `NetTcpBinding` / `binaryMessageEncoding`、gRPC Protobuf |
| ASMX 屬舊技術 | .NET Framework 4.8 為最後版本,不再新增功能 | 遷移至 ASP.NET Core Web API / gRPC,原生支援多型 (JSON `$type` / Protobuf) |
| 非 .NET 客戶端難以處理 | 需手動解析 XML 中的 xsi:type | RESTful API 使用 JSON + 自訂型別判別欄位 (如 "vehicleType": "Car") |
// 使用 JsonPolymorphism (System.Text.Json 7.0+)
[JsonDerivedType(typeof(Car), "car")]
[JsonDerivedType(typeof(Truck), "truck")]
public abstract class Vehicle { ... }
[HttpPost]
public ActionResult Register([FromBody] Vehicle vehicle)
{
return vehicle switch
{
Car c => Ok($"Car: {c.DoorCount} doors"),
Truck t => Ok($"Truck: {t.LoadCapacityTons}t"),
_ => BadRequest()
};
}
`XmlIncludeAttribute` 是 ASMX 時代實現 Web Service 多型的標準機制,透過在 WSDL 中明確描述型別繼承關係,使客戶端 Proxy 正確產生衍生類別並序列化。但受限於 XML 序列化效能、ASMX 技術淘汰、跨平台互通性差,**新專案請直接採用 ASP.NET Core Web API + System.Text.Json 多型序列化**,獲得更優雅、高效能、跨平台的解決方案。