F #, serialization of discriminated unions with no data values

First of all, I have to say that I know that it is generally not recommended to use F #-specific things when integrating with other .NET languages.

My problem is that I do not understand how to create a link to a service for a service that contains methods that reveal discriminatory unions.

I get basics that are slightly different from this:

type TelephonyProductActivationData = | MobileUseNextIcc | Mobile of decimal | MobileBroadbandUseNextIcc | MobileBroadband of decimal | Fixed | Voip of int16 * int16 static member KnownTypes() = typeof<TelephonyProductActivationData>.GetNestedTypes(BindingFlags.Public ||| BindingFlags.NonPublic) |> Array.filter FSharpType.IsUnion 

If you are using F # interactive, first create a type:

 type TelephonyProductActivationData = | MobileUseNextIcc of unit | Mobile of decimal<Icc> | MobileBroadbandUseNextIcc of unit | MobileBroadband of decimal<Icc> | Fixed of unit | Voip of BoxNr * int16<BoxPort>;; 

And you execute part of the code of known types (slightly modified):

 (typeof<TelephonyProductActivationData>.GetNestedTypes(System.Reflection.BindingFlags.Public ||| System.Reflection.BindingFlags.NonPublic) |> Array.filter Microsoft.FSharp.Reflection.FSharpType.IsUnion) |> Array.map (fun x -> x.FullName);; 

you will see the following output:

 val it : string [] = [|"FSI_0047+TelephonyProductActivationData+Mobile"; "FSI_0047+TelephonyProductActivationData+MobileBroadband"; "FSI_0047+TelephonyProductActivationData+Voip"|] 

Note that values ​​that do not have data associated with them have disappeared. This means that when compiling this discriminatory union, no types will be created. By executing this statement in F # interactive:

 typeof<TelephonyProductActivationData>.GetProperties() |> Array.map (fun x -> (x.Name));; 

we will see what they have become:

 val it : string [] = [|"Tag"; "IsVoip"; "Fixed"; "IsFixed"; "IsMobileBroadband"; "MobileBroadbandUseNextIcc"; "IsMobileBroadbandUseNextIcc"; "IsMobile"; "MobileUseNextIcc"; "IsMobileUseNextIcc"|] 

As you can see, non-data values ​​have become properties. Now I can show you the real problem. When creating a service link to a service using this method, all I get is:

 [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")] [System.Runtime.Serialization.DataContractAttribute(Name="ActivationModel.TelephonyProductActivationData", Namespace="http://schemas.datacontract.org/2004/07/Svea.Inri.Data")] [System.SerializableAttribute()] [System.Runtime.Serialization.KnownTypeAttribute(typeof(ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData.Mobile))] [System.Runtime.Serialization.KnownTypeAttribute(typeof(ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData.MobileBroadband))] [System.Runtime.Serialization.KnownTypeAttribute(typeof(ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData.Voip))] public partial class ActivationModelTelephonyProductActivationData : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged { [System.NonSerializedAttribute()] private System.Runtime.Serialization.ExtensionDataObject extensionDataField; private int _tagField; [global::System.ComponentModel.BrowsableAttribute(false)] public System.Runtime.Serialization.ExtensionDataObject ExtensionData { get { return this.extensionDataField; } set { this.extensionDataField = value; } } [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)] public int _tag { get { return this._tagField; } set { if ((this._tagField.Equals(value) != true)) { this._tagField = value; this.RaisePropertyChanged("_tag"); } } } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged; if ((propertyChanged != null)) { propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")] [System.Runtime.Serialization.DataContractAttribute(Name="ActivationModel.TelephonyProductActivationData.Mobile", Namespace="http://schemas.datacontract.org/2004/07/Svea.Inri.Data")] [System.SerializableAttribute()] public partial class Mobile : ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData { private decimal itemField; [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)] public decimal item { get { return this.itemField; } set { if ((this.itemField.Equals(value) != true)) { this.itemField = value; this.RaisePropertyChanged("item"); } } } } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")] [System.Runtime.Serialization.DataContractAttribute(Name="ActivationModel.TelephonyProductActivationData.MobileBroadband", Namespace="http://schemas.datacontract.org/2004/07/Svea.Inri.Data")] [System.SerializableAttribute()] public partial class MobileBroadband : ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData { private decimal itemField; [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)] public decimal item { get { return this.itemField; } set { if ((this.itemField.Equals(value) != true)) { this.itemField = value; this.RaisePropertyChanged("item"); } } } } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")] [System.Runtime.Serialization.DataContractAttribute(Name="ActivationModel.TelephonyProductActivationData.Voip", Namespace="http://schemas.datacontract.org/2004/07/Svea.Inri.Data")] [System.SerializableAttribute()] public partial class Voip : ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData { private string item1Field; private short item2Field; [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)] public string item1 { get { return this.item1Field; } set { if ((object.ReferenceEquals(this.item1Field, value) != true)) { this.item1Field = value; this.RaisePropertyChanged("item1"); } } } [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)] public short item2 { get { return this.item2Field; } set { if ((this.item2Field.Equals(value) != true)) { this.item2Field = value; this.RaisePropertyChanged("item2"); } } } } } 

There are no subclasses for ActivationModelTelephonyProductActivationData (the ActivationModel part is a namespace) that represents values ​​that do not contain any data, and there are no properties in the base class where you can set values ​​that do not contain any data.

My question is finally how to do it. Should I add β€œunits” to all of my discriminatory union values ​​that have no data.

+6
source share
2 answers

If you define the type of DU as shown below, it will work.

 [<KnownType("KnownTypes")>] //[<DataContract>] // note: keep KnownTypes, but avoid DataContract // so that DataContractSerializer uses .NET 'Serializable' instead type TelephonyProductActivationData = | MobileUseNextIcc | Mobile of decimal | MobileBroadbandUseNextIcc | MobileBroadband of decimal | Fixed | Voip of int16 * int16 static member KnownTypes() = typeof<TelephonyProductActivationData>.GetNestedTypes(BindingFlags.Public ||| BindingFlags.NonPublic) |> Array.filter FSharpType.IsUnion 
+3
source

Basically, you depend on the implementation detail (compiled form of DU) for this to work. Even changing every case to be a non-tumor smell cracked me. I think the ideal solution is to use classes. DU roughly corresponds to an abstract base class for type DU and a subclass for each case. You can create a hierarchy of types yourself, achieve a similar effect and get better results.

EDIT . The compiled form of the DU, while the implementation detail, is defined in the specification and therefore unlikely to change. However, the type layout makes it explicit and prevents you from dealing with null cases.

+1
source

Source: https://habr.com/ru/post/904090/


All Articles