Open ewoutkramer opened 1 month ago
Kas and I decided to call it "IScopedNode" for now, since it is still basically ITypedElement + more stuff - Definition.
Final design:
// Now.
IDictionary<string,object>
object:
* "value" => int,string,boolean,....
* "id" / "url" => string
* List<>
* Base
// ITypedElement
* !"value" (NB Extension.value)
* "id" / "url" => FhirString, FhirUri (*) POCO: string, string
* ITypedElement****
* Value (CQL types)
Remarks:
* Het feit dat Element.ElementId een string is en Extension.Url een string geeft veel uitzonderingen in de code, want je moet het vaak als Base behandelen.
* Graag gebruiken wij de key "value" niet, want dit is toch een speciaal geval, heeft geen zin om hetzelfde te behandelen als de andere properties.
// Then
Base:
partial class Patient
{
// Slimme getters en setters.
public FhirBoolean Active { get; set; }
Base[] Children(string? name)
{
// Base[].Parent = this;
}
bool TryGetElement(string, Base[])
{
}
void SetElement(string, Base[])
string GetLocation();
Base Resolve()
}
public Base? Child(this Base? b, string name)
{
if(b is null) return null;
return b.TryGetValue(name, bla) ? bla.SingleOrDefault() : null;
}
public T? Child<T>(this Base?, string name) => ...
var p = new Patient();
p.Children("active").Resolve();
p.Child<FhirBoolean>("active").Value = true;
p.Active.Resolve();
// Tweede oplossing, zonder slimme setters/getters, uses Navigate() to step to other "world" of navigation.
// Onze oude PocoElementNode
record ScopedNode(Base[] Poco) : ITypedElement
{
private Name;
private Parent;
private Index
// Hieronder de navigatie
IEnumerable<ScopedNode> Children()
this[]
SetValue()
Resolve()
GetLocation()
IEnumerable<ITypedElement> ITypedElement.Children(string name) =>
this.Children(name);
bool Equals(object other) => other == this;
public implicit operator Base(ScopedNode sn) => sn.Poco.SingleOrDefault();
public explicit operator Base[](ScopedNode sn) => sn.Poco;
}
var x = p.Navigate()["active"] as FhirBoolean;
x.Value = false;
ITypedElement ToTypedELement(this Base b) => b.Navigate();
var p =;
// Add dynamic stuff
dynamic pn = p;
var d = pn.name.given;
record ScopedNode<T>(T[] Poco) : ITypedElement where T : Base
{
private Name;
private Parent;
private Index;
public T? Single => Poco.SingleOrDefault();
IEnumerable<ScopedNode<Base>> Children() => // some impl with GetElemPairs on the poco.
ScopedNode<Base>? Child(string name) => // some impl with TryGetValue on the poco.
this[]
SetValue()
Resolve()
GetLocation() // remark: the Location property is part of ITE, so we cant make this a method.
IEnumerable<ITypedElement> ITypedElement.Children(string name) =>
this.Children(name);
bool Equals(object other) => other == this;
public implicit operator Base(ScopedNode sn) => sn.Poco.SingleOrDefault();
public explicit operator Base[](ScopedNode sn) => sn.Poco;
}
static class ScopedNodeExtensions
{
public static IEnumerable<ScopedNode<T>> Children<T>(this ScopedNode<Base>? sn, string? name = null) where T : Base
=> name is null
? sn?.Children().OfType<T>() ?? []
: sn?.Children(name).OfType<T>() ?? [];
public static ScopedNode<T>? Child<T>(this ScopedNode? sn, string name) where T : Base
=> sn.Child() is {Single is T} node
? node
: null;
}
var pat = new Patient();
ScopedNode<HumanName> x = pat.Navigate().Child<HumanName>("name"); // null if multiple or none.
ScopedNode<HumanName> y = pat.Navigate().Children<HumanName>("name"); // empty if none.
HumanName z = pat.Navigate().Child<HumanName>("name").Single; // poco
string s = pat.Navigate().Child("name").Child<FhirString>("given").Value;
// or maybe even:
string s = pat.Navigate().Child<FhirString>("name.given").Value; // Children would have to allow period-delimited names.
``
We need a new interface with at least Parent (and maybe Resolve()). This is the "ITypedElement" for POCOs. See #2893 for more discussion on the design need and issues for this interface.