Neue Features für den EBC Designer

Vor ein paar Tagen hatte ich ja schon meine verbesserte Version des EBC Designers für Visual Studio vorgestellt. Hier jetzt noch mal ein Update, das folgendes beinhaltet:

  1. Die neue Version kann parallel zum alten EBC Designer verwendet werden (man braucht also die alten .ebc Boards nicht neu zu malen). Dadurch, dass ich die Extension auf .ebcx geändert hab gibts auch keine Probleme mit den Dateien
  2. Die Usings auf dem Board werden jetzt automatisch generiert, wenn man einen neuen Typ hinzufügt (als Connection-Typ oder Part-Typ). Damit wird die Darstellung übersichtlicher, weil nur noch die Typnamen (ohne Namespace) im Diagramm angezeigt werden. Das sieht dann ungefähr so für das Diagramm aus:

usings1

 

Und so für die generierten Usings:

usings2

Installation

Achtung: Unbedingt erst den Designer von meinem vorigen Post deinstallieren (weil der sich auch auf ebcx registriert).

Quellcode steht jetzt zum Download unter codeplex. Wer trotzdem die vsix Datei gleich haben möchte findet die wieder hier.

Gedanken über den EBC Designer

Unter codeplex sind einige hilfreiche Tools für die Event-Based-Components zu finden. Interessant ist auch der  grafische Designer dazu. Der hat den Vorteil, dass man den grafischen Verdrahtungsplan 1 zu 1 in Code übersetzen lassen kann, d.h. das Modell entspricht exakt der Realität. Leider ist der Designer aber ziemlich unkomfortabel, was vor allen Dingen daran liegt, dass man alle Daten mit der Hand eingeben muss (z.B. Typnamen) und keine Möglichkeit hat irgendetwas auszuwählen.

Man kann aber mit relativ wenig Aufwand den Designer maßgeblich verbessern, was ich einmal versucht habe. Hier meine ersten Ergebnisse:

1. Typauswahl bei EBC-Komponenten



 

 

Es gibt jetzt 2 Arten von Parts, den PartContract (heißt der Compiler soll ein Interface generieren) und einen Existing-Part der über meinen in Visual-Studio integrierten Type-Browser eine Typauswahl ermöglicht (Auswahl von Typen im aktuellen Projekt und allen Projekten auf die verwiesen wird). Die Typen werden zwar im Clr Format ausgewählt, dann aber zur Anzeige und zum Weiterverarbeiten von mir ins C# Format konvertiert (String wird zu string usw.) Hier noch ein paar Screenshots vom Type-Browser:

2. Die Connections

Auch der Typ, der an den Connections fließt kann jetzt über den Type Browser ausgesucht werden. Zusätzlich kann man über ein Drop-Down Menü Event-Ausgänge oder Methoden-Eingänge auswählen (falls es sich um ein Existing-Part an dem jeweiligen Endpunkt der Connection handelt), ansonsten kann man natürlich auch einfach einen Namen eingeben (Create New). Falls möglich wird der Typ der Connection dann automatisch auf den jeweiligen Parametertyp festgelegt.

Die Ein- und Ausgänge müssen natürlich ebenfalls immer per Hand mit Create New benannt werden.

Download

hier vsix Datei runterladen

Ein Framework für eventbasierte Programmierung

Es ist schon eine Weile her, als sich Ralf Westphal das letzte Mal hier Gedanken über eine EBC Infrastruktur gemacht hat. Allerdings hab ich einen gewissen Abscheu gegenüber LabView und derartigem grafischen Rumspielen. Man nehme doch einfach mal an man hätte 40 funktionale Abhängigkeiten als Methoden und Delegates und möchte die jetzt zusammenstecken. Da ist 40 noch gar nichts. Bei größeren Projekten hat man doch locker allein 40 Interfaces im UnityContainer ganz zu schweigen von den Methoden noch… Als XML Datei geh ich da schon mal noch mit(zum Beispiel so):

Ok, also das geht, ist aber natürlich nicht so schön wie nur die Klassen registrieren, weil man wesentlich mehr mit der Hand tippen muss, sieht aber noch ganz übersichtlich aus, weniger das hier:

Je mehr Abhängigkeiten hinzukommen, umso unübersichtlicher wird diese grafische Darstellung. Man stelle sich nur mal vor einen Fehler in einem Baum zu finden der 4 – oder noch mehr mals größer als der obige ist. Ehrlich, irgendwie ist das für mich nicht DIE Lösung. Aber: müssen wir denn alle Abhängigkeiten auf Methodenebene definieren? Womit ich sagen will: Müssen wir jede Zuordnung definieren? Ich glaube nein!

Wenn wir im UnityContainer Klassen registrieren und mit Injection Based Components arbeiten, dann registrieren wir doch auch nur die Service-Klassen, und nicht ihre Verbindungen zu den Clients. Sie werden dann von einem, oder mehreren Clients über eine Kontraktspezifikation das Interface gefunden.

Ok, aber das geht bei EBC´s doch auch,  nur nicht so offensichtlich. Der Kontrakt sind die Parameter auf der Methode oder dem Delegate. Wenn ein Client eine Action<string> verlangt passt dazu eben nur void(string parameter) und nicht zum Beispiel int(bool parameter, double parameter). Logisch.

Nur leider ist dieser Kontrakt noch nicht ganz eindeutig, es ist ja auch möglich mehrere Methoden mit gleichen Parametern und Rückgabetypen zu definieren. Also: Mein Vorschlag, und damit schlagen wir gleich zwei Fliegen mit einer Klappe: im Unity Container werden wieder ganz normal Klassen registriert(Service Klassen), diesmal eben als object, weil ja kein Interface bekannt ist, und die passenden Service-Methoden werden per Reflection ermittelt. Vorteile:

1. Die Methoden sind mit einer höheren Wahrscheinlichkeit eindeutig, weil sie jetzt auch noch durch den Klassen-schlüssel im Unity Container beschrieben werden, der auf dem Client optional mit angegeben werden kann

2. Der Konfigurationsaufwand verringert sich immens, weil Klassen und keine Methoden registriert werden

Super, das hört sich ja schon ganz gut an, aber: auch eine Klasse kann ja zwei Methoden mit gleichen Parametertypen definieren, nun nichts einfacher als das, wir geben einfach der Methode noch einen optionalen Schlüssel, der auch beim Client wieder mit angegeben wird. Die Implementierung, mach ich jetzt einfach mal ganz spontan über Attribute, weil die sich schön einfach handhaben lassen, also sieht ein Client jetzt zum Beispiel so aus:

[EBCImport]
public Func<DigitTable, NumberSystem, NumberSystem, DigitTable> ConvertDigitTable
{
    get;
    set;
}

[EBCImport]
public Func<int, DigitTable> IntToDigitTable
{
    get;
    set;
}

[EBCImport]
public Func<DigitTable, NumberSystem, int> DigitTableToInt
{
    get;
    set;
}

[EBCImport]
public Func<string, NumberSystem, DigitTable> StringToDigitTable
{
    get;
    set;
}

[EBCImport]
public Func<DigitTable, NumberSystem, string> DigitTableToString
{
    get;
    set;
}

Und ein Service könnte so aussehen:

[EBCExport]
public DigitTable BuildGivenFormattedNumber(string formattedNumber, NumberSystem format)
{
    ...
    return result;
}

[EBCExport]
public DigitTable BuildGivenInt(int value)
{
    ...
    return result;
}

Dann steht der Service normal im UnityContainer, einfach als object:

this.container.RegisterType<object, DigitTableBuilder>("DigitTableBuilder", new ContainerControlledLifetimeManager());

Was natürlich viel einfacher ist als Methode zu registrieren, es gäbe da ja allein an der Tatsache das Action<> und Func<> generisch sind Umständlichkeiten. So ist jetzt alles wie gewohnt, mit der Ausnahme, dass bei  einigen Fällen eben noch auf [EBCImport(string containerKey, string key)] und [EBCExport(string key)] zurückgegriffen werden muss, um Dopplungen zu vermeiden, ich denke aber das ist recht akzeptabel und auch überschaubar, weil normalerweise nicht viel Dopplungen vorkommen.
Um die Infrastruktur und Reflection braucht sich keiner (außer ich jedenfalls) mehr zu kümmern, weil ich die schon fertig hab und in Kürze zu Download zur Verfügung stellen werde(einfach mal hier vorbeischauen).
Zu beachten ist, dass eine Bootstrapper Klasse, die von EBCBootstrapper erbt, und einen Service Locator zurückgibt(der für den Unity Container liegt unter CDT.Infrastructure.EBC.Connections.Unity) zu erstellen ist, dessen Run Methode ganz am Anfang, bevor die EBC´s kommunizieren können aufgerufen werden muss. Diese Klasse muss in der überschriebenen Methode CreateContainer() alle Services und Clients im ServiceLocator registrieren, und natürlich:
Bitte die Attribute nicht vergessen sonst funktioniert das nicht(Achtung über die Klassen und nicht über die Interfaces setzen!). Natürlich ist das noch nicht die Endlösung weil sie im Moment nur Delegates und Methoden unterstützt(und keine eigenen Channels oder so), aber das lässt sich noch nachrüsten, und soweit erstmal viel Spaß mit den EBC´s!

Von Event- und Network Based Components – Softwarearchitektur mit Flow

Diesen Monat hat Ralf Westphal in der dotnetpro einen Artikel über Event Based Components veröffentlicht. Dazu mal ein bisschen auf seinem Blog rumgestöbert und das hier gefunden. Es geht mir dabei nicht um irgendeine Geschäftsanwendung oder sowas in der Art, sondern vielmehr um die Logik für ein Computerspiel. Die Leute, die sich in der Spieleentwicklung auskennen werden wissen, dass es da immer einen Game-Loop gibt. Also eine Update Methode, die immer aufgerufen wird. Jetzt mal weitergedacht: Was wenn ich an dieses Update eine Sammlung oder eine Schicht von EBC´s ranhänge, bei denen direkt irgendwelche Updates immer ausgeführt werden. Also ganz kleine Einheiten, wie zum Beispiel prüfen ob eine Taste gedrückt ist, und dann ein Event feuern. Das sind meine Inputs, also meine Update Einheiten. Dann aktivieren die logische Einheiten, wie zum Beispiel dann irgendeine Funktion, dass sich Spielfiguren bewegen und so weiter. Das ganze kann sich ja auch auf sehr viele Schichten von EBC`s aufteilen, die wieder miteinander verknüpft sind. Irgendwie erinnert mich das an neuronale Netze. Oder zumindest einfach  an ein Netz. Es werden inputs definiert, und dann steuert sich der Ablauf nur noch über Events, und dann? Ja dann könnte es zum Beispiel Draw Einheiten im Netz geben, die bei der Draw Methode abgefragt werden, und deren Zustand dann eben gezeichnet wird. Es war mir schon ewig ein Problem wie man eigentlich diese zwei Sachen Update und Draw verbindet, und dabei noch eine anständige Architektur hat. Eine Möglichkeit sind Game Objects oder Entities, die einfach alles vom Baum bis zum Schwertkämpfer darstellen als universelle Basisklasse mit universeller Funktionalität. Das lässt sich aber mit so einem Netz deke ich besser hinbekommen, man kann solche Sachen ja als größere zusammengefasste Funktionseinheiten implementieren, kein Problem… Ich stelle mit das ungefähr so vor:

So schön wandert jetzt der Flow hier durch, und auf ein Event können sich beliebig viele, oder auch gar niemand registrieren, man muss dann eben vor dem Aufruf prüfen ob eine Registrierung vorliegt. Ich nenne meine „neuen“ Bausteine jetzt Network Based Components(NBC`s). Hier bietet sich natürlich(wie von Ralf Westphal auch erkannt) eine schöne Möglichkeit asynchron anzusetzen. Warum denn nicht die Events asynchron verschicken, was heißt verschicken und Events, dann nehmen wir doch gleich Ports von der CCR(Concurrency and Coordination Runtime) und schicken uns die Nachrichten so, oder noch besser: AppSpace(auch von Ralf Westphal). Spiele einfach übers ganze Intenet laufen zu lassen mit dem einen Teil der Logik auf dem einen dem anderen Teil auf dem anderen Rechner. Mal sehen was draus wird, auf jeden Fall sehr spannend das Thema! Wahrscheinlich nur bedingt einsetzbar wenn es um Geschäftsanwendungen geht, aber hier schaut das richtig gut aus.

EDIT: Bitte mal hier nachschauen, ich möchte nicht sagen es ist die optimale Lösung, aber es macht vieles einfacher(zum Beispiel bei Geschäftslogik)