Page 10: Iterate over the Local Database |
It is assumed that you've followed the steps from Page 1: Prerequisites to Page 8: Handle Communication Errors, and thus have a runnable project open in Visual Studio 2015.
This topic contains the following sections:
First we need to declare our expectations about the provider database with the following nested types:
private sealed class SapphireRoot : Root<SapphireRoot> { internal Sapphire Sapphire { get; private set; } } private sealed class Sapphire : FieldNode<Sapphire> { internal Sources Sources { get; private set; } } private sealed class Sources : FieldNode<Sources> { [Element(Identifier = "FPGM 1")] internal Source Fpgm1 { get; private set; } [Element(Identifier = "FPGM 2")] internal Source Fpgm2 { get; private set; } } private sealed class Source : FieldNode<Source> { internal Fader Fader { get; private set; } [Element(Identifier = "DSP")] internal Dsp Dsp { get; private set; } } private sealed class Fader : FieldNode<Fader> { [Element(Identifier = "dB Value")] internal RealParameter DBValue { get; private set; } internal IntegerParameter Position { get; private set; } } private sealed class Dsp : FieldNode<Dsp> { internal Input Input { get; private set; } } private sealed class Input : FieldNode<Input> { internal BooleanParameter Phase { get; private set; } [Element(Identifier = "LR Mode")] internal EnumParameter<LRMode> LRMode { get; private set; } } private enum LRMode { Stereo, RightToBoth, Side, LeftToBoth, Mono, MidSideToXY }
A few things are of note here:
The elements FPGM 1, FPGM 2 and dB Value have identifiers that cannot be C# property names. The associated properties need to carry an ElementAttribute instance with the correct identifier.
The getters and setters of the various properties can have any accessibility. However, since client code should never set any of the properties, it is best to declare the setter private.
The constants in the enum and LRMode have names that differ from the ones presented by the provider. This is possible because the library only checks that the provider enumeration and the local enum have an equal number of constants and that they have the same integer values.
In rare cases, client code might only be interested in the integer value of an enumeration or does not want to statically confine the possible values an enumeration can have. In these cases, client code should use IntegerParameter rather than EnumParameterTEnum.
We can now iterate over the local database as follows ...
AsyncPump.Run( async () => { using (var client = await ConnectAsync("localhost", 9000)) using (var consumer = await Consumer<SapphireRoot>.CreateAsync(client)) { WriteChildren(consumer.Root, 0); } });
... and get the following output:
Node Sapphire Node Sources Node FPGM 1 Node Fader Parameter dB Value: -255.999999999998 Parameter Position: 0 Node DSP Node Input Parameter Phase: True Parameter LR Mode: Stereo Node FPGM 2 Node Fader Parameter dB Value: 9 Parameter Position: 255 Node DSP Node Input Parameter Phase: False Parameter LR Mode: Stereo
Notes:
Compared to the output we've seen on Page 5: Iterate over the Local Database, this time the database only contains the elements that we've explicitly declared interest in. So, for large provider databases, the static interface offers a way to reduce the memory footprint on the consumer side.
ConsumerTRootCreateAsync verifies that the expectations we've declared match with what the provider sends. Any mismatch is signaled with a ModelException. This verification includes the presence of required parameters and nodes, the match of types and many other checks.
Proceed to Page 11: React to Changes.