Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Table of Contents
stylenone

Einfache Relationen

Relationen können über den ClassBuilder hinzugefügt werden. Der RelationBuilder erstellt dabei eine Relation von x → y und entsprechend von y → x.

Eine einfache Relation kann wie folgt erstellt werden:

Code Block
Elements.Company.Add.Relation(AssociationType.OneToMany, Elements.Person) 
  .Description(English, "The company of the person")
  .Description(German, "Die Firma der Person");

Das führt zu folgendem Ergebnis auf der Datenbank:

...

In der folgenden Tabelle werden die unterschiedlichen AssociationType-Werte und die damit verbundene Platzierung des ForeignKeys und dessen Nullability beschrieben.

Quell-Tabelle

Quell-FK

Quell-Kardinalität

AssociationType

Ziel-Kardinalität

Ziel-Tabelle

Ziel-FK

A

 

1

OneToZeroOrOne

0-1

B

AId*

A

BId

0-1

ZeroOrOneToZeroOrOne

0-1

B

A

BId*

0-1

ZeroOrOneToOne

1

B

 

A

 

0-1

ZeroOrOneToMany

n

B

AId

A

 

1

OneToMany

n

B

AId*

A

BId

n

ManyToZeroOrOne

0-1

B

 

A

BId*

n

ManyToOne

1

B

 

Bei der ZeroOrOneToZeroOrOne Verbindung ist auch die Platzierung des FK auf der anderen Seite denkbar. Wir haben uns aber dazu entschieden, bei einer ZeroOrOneToZeroOrOne-Verbindung von A nach B den FK in A zu platzieren, damit auf A per SearchEdit ein B ausgewählt werden kann.

Eine Relation mit erweiterter Konfiguration kann wie folgt aussehen:

Code Block
breakoutModewide
Builder.Add.Relation(Elements.Company, AssociationType.ZeroOrOneToMany, Elements.Office, MetaPathRule.Cascade)
  .Caption(SwissGerman, "Büros")
  .InverseCaption(SwissGerman, "Firma")
  .Control(nameof(ControlType.GridEditor))
  .InverseControl(nameof(ControlType.SearchEdit))
  .InverseLayout(LayoutType.Detail, "CompactLayout")
  .Rename("Offices");

Einzelne Elemente einer Relation können auch über einen callback konfiguriert werden:

Code Block
breakoutModewide
Builder.Add.Relation(Elements.Company, AssociationType.ZeroOrOneToMany, Elements.Office, MetaPathRule.Cascade)
  .Configure(tuple =>
  {
    // tuple.relation;
    // tuple.inverseRelation;
    // tuple.foreignKey;
    // tuple.path;
    // tuple.index;
  });

Werden mehrere Relationen von einer Klasse zur nächsten gelegt so müssen diese mit einem Prefix ausgezeichnet werden.

Code Block
breakoutModewide
Elements.Company.Add.Relation(AssociationType.OneToMany, Elements.Person)
  .Description(English, "The company of the person")
  .Description(German, "Die Firma der Person");

Elements.Company.Add.Relation(AssociationType.ZeroOrOneToMany, Elements.Person, sourceNamePrefix: "CompanyPrefix", inverseNamePrefix: "PersonPrefix")
  .Description(English, "The company of the person")
  .Description(German, "Die Firma der Person");

Dies führt zu vier Relationen:

  • Eine Relation mit dem Namen People von Company => Person

  • Eine Relation mit dem Namen CompanyPrefixPeople von Company => Person

  • Eine Relation mit dem Namen Company von Person => Company

  • Eine Relation mit dem Namen PersonPrefixCompany von Person => Company

Eine Relation kann im DB-First Modus auch auf einen bestehenden ForeignKey gesetzt werden. Dazu steht ein zusätzlicher Parameter zur Verfügung. Der FK muss bereits als Property modelliert sein damit die Relation erstellt werden kann.

Code Block
breakoutModewide
Elements.Company.Add.Relation(AssociationType.ZeroOrOneToMany, Elements.Person, foreignKey: "ExistingFK")
        .Description(English, "The company of the person")
        .Description(German, "Die Firma der Person");

Auf dem Relation Builder stehen verschiedene Methoden zur Verfügung womit die Relation weiter verfeinert werden kann. Die Methoden stehen jeweils mit einem “Inverse” Prefix zur Verfügung.

  • Caption => setzen der Captions welche auch im UI angezeigt werden.

  • Filter => Einschränken der Relation/Daten die geladen werden.

  • Control => Setzen des Control welches benutzt werden soll um die Relation anzuzeigen im UI.

  • RenameForeignKey => Umbenennen des Fremdschlüssel.

Generische Relationen

Es gibt immer wieder Use-Cases wo von einer Klasse aus eine Relation auf eine ganze Reihe von möglichen Klassen benötigt werden, wobei jeweils zur gleichen Zeit maximal nur eine dieser Relationen gesetzt ist. Anstatt dies mit einer (grossen Anzahl) von einzelnen Relationen abzubilden kommen hier generische Relationen resp. Entity References zum Zug. Dies ist z..B. im QuinoComments Modul der Fall um Kommentare an beliebige andere (Parent-) Klassen anhängen zu können. Dieses Beispiel wird in den nachfolgenden Code-Samples auch wieder aufgegriffen.

Eine Entity Reference besteht nicht nur aus einem Foreign-Key der auf einen anderen Datensatz zeigt sondern auch dem Namen der Klasse auf welcher der Foreign-Key zeigt. Erst damit wird eine eindeutige Beziehung hergestellt.

Modell-Definition

Eine einfache generische (nullable) Relation wird mit

Code Block
classBuilder.Add.EntityReference();

angefügt. Das erzeugt intern die drei Properties TargetClassName, TargetObjectId und TargetObjectTitle wobei das letzte Property ein berechnetes Property ist und den Titel des referenzierten Objekts beinhaltet.

Mit

Code Block
commentBuilder.Add.EntityReference("Parent");

wird eine generische Relation eingefügt deren Property Namen den Prefix “Parent” haben. Das ermöglicht es auch mehr als eine generische Relation auf einer Klasse zu verwenden.

Wie andere Properties auch sind Entity References ohne weiteres Zutun nullable. Um eine Entity Reference nicht nullable zu machen braucht es also:

Code Block
commentBuilder.Add.EntityReference("Parent")
  .IsNotNullable();

Und für Database-First oder andere individuelle Szenarien gibt es die Möglichkeit mit einem Aspekt die Propertynamen zu kontrollieren:

Code Block
commentBuilder.Add.EntityReference(new EntityReferenceAspect(...))

Verwendung in Layouts

Um eine Entity Reference in einer Liste anzuzeigen wird am besten

Code Block
layout.Add.EntityReferenceColumn("Parent");

Und im Detail ist es dann

Code Block
group.Add.EntityReferenceLink("Parent");

Es gibt in der Klasse EntityReferenceExtensions noch weitere Methoden für die Verwendung in Layouts die in bestimmten Situationen hilfreich sein können.

Verwendung im Code

Um zum Beispiel ein Comment-Objekt mit seinem Parent zu verbinden:

Code Block
comment.ReferenceObject(parentObj, "Parent");

Und um das Parent Objekt vom Comment her wieder zu laden:

Code Block
var parentObj = obj.FindReferencedObject<IDataObject>("Parent");

Filtern von :1 Sourcen

Beziehungen welche auf ein einzelnes Objekt verweisen zeigen im UI normalerweise eine Liste von möglichen Einträgen zur Auswahl an. Diese Auswahl kann eingeschränkt werden - auch in Abhängigkeit zum aktuell geöffneten Objekt.

Um die der Relation zur Verfügung stehenden Einträge zu reduzieren kann folgende Methode verwendet werden:

Code Block
languagec#
Elements.Office.Add.Relation(AssociationType.ZeroOrOneToMany, Elements.Person)
    .FilterSource("SourceObject.CompanyId == CompanyId")

Der Aufruf von FilterSource erstellt im Hintergrund eine neue virtuelle Relation welche beliebig gefiltert werden kann. In der Expression steht das aktuelle Objekt (wenn vorhanden) als SourceObject zur Verfügung.

Diese Art von Filter führt dazu das die Auswahlliste neu geladen wird wenn eine abhängige Eigenschaft editiert wird. In diesem Beispiel wenn eine andere Company ausgewählt wird und sich der Fremdschlüssel CompanyId ändert.

Filtern von ValueList Sourcen

Die zur Auswahl stehenden Werte einer ValueList können ebenfalls eingeschränkt werden. Dabei wird jedoch lediglich der “enabled” state des entsprechenden Eintrag geändert.

Code Block
languagec#
Elements.Person.Add.ValueListRelation(Elements.PersonMaritalStatus)
  .IsValueListEntryEnabled(MaritalStatus.Married, "Active == true")
  .IsValueListEntryEnabled(MaritalStatus.Divorced, "Active == true")
  .IsValueListEntryEnabled(MaritalStatus.Single, "Active == true")

Im obigen Beispiel stehen die Werte Married, Divorced und Single nur zur Verfügung wenn eine Person als aktiv markiert ist. Diese Einschränkung kann sowohl auf Level der Relationen als auch auf Level von PropertyLinks im Layout gemacht werden.

Die Expressions werden im Client evaluiert - asynchrone Expressions sind nicht unterstützt.