Laden...
F
Froschkoenig84 myCSharp.de - Member
Trollschläger Scheibenwelt Dabei seit 11.06.2015 63 Beiträge
Benutzerbeschreibung
Wenn einem eingefleischten Pessimisten ein Stein vom Herzen fällt, dann ganz bestimmt auf den Fuß!

Forenbeiträge von Froschkoenig84 Ingesamt 63 Beiträge

30.08.2021 - 12:15 Uhr

Obwohl ich ein großer Fan von MicroServices bin, habe ich inzwischen auch die Vorteile gesehen, die ein thematisch angepasst-übergreifendes Projekt bietet. Ich mag auch nicht diese Common-MicroServices, da nutze ich ausschließlich die im Hauptprojekt (und somit namespace-seitig "offen liegenden") Bibliotheken und Module.

Andererseits muss GraphQL nicht zwangsläufig die MicroServices brechen. Klas, nach EVA dürfte jedes Teilprojekt auch nur die Daten laden/speichern können, die für das Modul gedacht sind, aber wenn man bspw. Produktions-Simulatoren entwickelt, hat man mir riesigen Datenmengen zu tun, aber nur wenige Grunddaten, wenn auch aus völlig unterschiedlichen Modulen. Ich nutze GraphQL nur, um meine Analyse-Objekte wegzuspeichern, das mache ich aber auch sehr simpel, quasi via 1-Single-POCO-Object (Table). Aber natürlich, ich habe nach Außen meine REST-Schnittstellen (Anforderung) für die Nutzung des Moduls und nach "Innen" eben GraphQL, um mir Daten aus verschiedensten Tables zu holen, dann alles in zahlreichen Loops zu verarbeiten und schließlich nur noch das Ergebnis via Response zurückzuliefern. Allerdings speichere ich ein paar Ergebnislisten weg, damit beim nächsten Aufruf (der ohne Veränderungen stattfand) die Ergebnisse direkt aus der DB kommen und nicht alles neu berechnet werden muss. Aber ich finde GraphQL auch toll, wenn man tatsächlich dynamisch über alle Tabellen seine Daten zusammensuchen muss. Einzig die Integration von OAuth und den berechtigten Rollen, sowie die Einschränkung endlessloops oder zu großen Rückgabedaten fiel mir etwas schwer. Läuft aber inzwischen auch. - Ich mag GraphQL, aber hier muss noch einiges gemacht werden. Ähnlich wie in der JSON-API fehlen ein paar standardisierte Abfragen, die man eben immer benötigt. Ich hatte neulich mit einigen Jungs von ChilliCream in Slack gequatscht und die Bibliothek wird es dann mit modularen Erweiterungen geben. Das Prinzip finde ich aber toll. - Perfekt wäre ein Mix aus REST, JSON-API und GraphQL, aber ich werde einen Teufel tun, etwas eigenes zu entwickeln. 😉

Und zurück zu den Datenbanken-Typen. Japp, GraphDatabases sind langsamer, aber ebenso optimierbar. Man muss nur entsprechendes Verständnis mitbringen und wissen, an welchen Stellen man sie optimiert. Gleiches gilt ja auch für SQL, Ich habe mal ein interessantes Gespräch mit dem technischen Leiter der Kantar über die riesigen Datenmengen (die verdienen Ihr Geld in der Sammlung und Auswertung von "Statistiken") und da habe ich gemerkt, dass meine bisherigen Anpassungen in SQL ein Witz sind, im Vergleich was dort gemacht wird. Wobei BigData-Schemas natürlich auch einfacher zu optimieren sind, als hunderte separate Tables nach was-weiß-ich-Normalform.

Aber ich bin davon überzeugt, wenn man es korrekt und sauber macht, kann auch eine GraphDatabase sehr schnell laufen, trotz der vielen integrierten Abfragen basierend auf den Abhängigkeiten. - Wie gesagt, ich probiere mich mal dran. 😉

Aber ich vermute fast, ich werde am Ende alles dreis parallel nutzen (klassisch relationales SQL, KVP-NoSQL und für die Analysen GraphDatabases), abhängig vom Verwendungszweck der Daten. Anders wird sich die Geschwindigkeit kaum optimieren lassen.

27.08.2021 - 01:12 Uhr

Generell versteh ich Dich: Du musst Fehler machen um draus zu lernen 😉
Tob Dich aus, wenn das ein privates Projekt ist; geh verantwortungsbewusst vor, wenn es ein Kunden- oder Team-Projekt ist.

Jo, ich werde mich in den kommenden Tagen damit auseinandersetzen und mal wild verschiedene Systeme ausprobieren.
Was das OE betrifft, na ja, da hast du schon Recht. Aber vor 20 Jahren war ich noch ungemein sparsam mit DB-Daten und und heute ... 😉
Okay, ich orientiere mich nicht direkt an allen Problemen von Google, Facebook oder Amazon. Aber an den gröbsten Schwierigkeiten und auch deren Lösungsansätze. So bin ich bspw. über die JSON-API auf GraphQL gekommen. Und bei den DataStorage-Technologien/-Typen hat @JimStark natürlich auch nicht ganz Unrecht, wenn man die Cloudnutzung nicht ganz außer Sicht lässt.

Ich denke, ich werde mich erstmal auf GraphDatabases stürzen, da ich hier ein wenig den Mix von NoSQL und relational erkenne und mich hoffentlich schneller zurecht finden werde.
Mich reizt sehr, dass es nicht nur Abhängigkeiten wie Primär- und Fremdschlüssel, sondern auch die gesamte Abhängigkeitsstruktur und sogar die Verknüpfungen als eine Art integrierte Abfrage ermöglicht. Anstatt komplexe Abfragen, lassen sich zweckdienliche Sub-DataSets "erzeugen" und die Abfragekomplexität auf die DB selbst verlegen. Das macht es spannend, denn es beinhaltet sowohl die klassischen Records (bspw. Shop: Produkt, Infos, Kategorie, Preis, Verfügbarkeit, Lagerstandorte, Bestellung, Kunde, Rechnung, Versand, ...), aber eben auch die rasche Analyse und die Herauslösung jener gefilterten SubDataSets (also nur das, was gerade benötigt wird), wie ein riesiger Join, aber clevere Indizies und übergreifende Selects. Die meisten aufgeführten Beispiele beschreiben die Analysen von Kaufverhalten oder die Kundenanalyse. Aber auch Social-Communities (Beziehungsanalysen) und Risiko-/Fehleranalysen werden immer wieder mit den GraphDatabases in Verbindung gebracht.

Bei einer SocialCommuniy für Musikliebhaber mit besonderem Augenmerk auf Konzerte, Veranstaltungen (Finanzierung: Ticketverkäufe oder Affiliate-Marketing) oder Merchandise, sowie CDs und Musik-Downloads (Affiliate-Marketing oder CPC/CPL/CPV) wären bspw. solche Analysen sehr wichtig. Außerdem sind die meisten dieser GraphDatabases aktuell für das schnelle Webspeichern (oft noch schneller als BulkCopy) und das enorm rasche Auslesen gefilterter Datensätzen für spezielle PartialObjects (hier stelle ich mir C#-seitig regulary-depended LightObjects vor, die entweder reflected POCOs sind oder mit Func/Action, sowie Properties arbeiten). Na ja, das schnelle Auslesen und die simple Verwendung der Datenbank-API klingt spannend. Einige APIs lassen sich vollständig über den Client (in meinem Fall JavaScript) verwalten, ich bin aber ein Fan, einer eigenen API um OAuth, Rollen und Berechtigungen integrieren zu können. Leider gibt es noch keine standardisierte Abfragesprache, also werde ich mir erstmal nur die wichtigsten, wie GraphDB, Neo4j, AWS-Neptun und OrientDB zu Auge führen können.

Wie gesagt, erstmal nur ein kleines privates Projekt, habe eigentlich gar nicht die Zeit eine marktreife Idee zu erzeugen und auch noch nutzbar umzusetzen. Es ist also nur ein Tryout-Project, aber vielleicht läuft es ja aus irgendeinem Grund und kann tatsächlich eine Wirtschaftlichkeit damit erreichen.

Sofern ich es nicht vergesse, werde ich hier immer mal wieder neue Erkenntnisse zu meinen Versuchen mit den NoSQL-Datenbanken darunter posten. 🙂
Danke an alle für die vielen Infos.

25.08.2021 - 22:21 Uhr

Weiterhin könnte man sich zum Thema "Repository Pattern" informieren, da langfristig vermutlich mehrere Datenbanken inkl. Abfragen getestet oder eingesetzt werden.

Ja, meine GraphQL-API nutzt quasi ein Repository, das über mehrere Datenquellen (externe APIs, interne DBs, externe DBs) bedient wird.
Wobei ich inzwischen auch sämtliche Clients (in meinem aktuellen Job handelt es sich beim Client um eine Anwendungssoftware, UI = WinForms) auf async umgestellt habe, um dort je nach Anforderung auf die Payloads und unnötige Datentransfers zu verzichten. Aber das Repository Pattern nutze ich bereits sowohl clientseitig, als auch serverseitig.

Es hilft mir nur leider bei der Frage der primären Daten-Speicherung relativ wenig weiter.
Ich muss erstmal recherchieren, welche DataStorage-Typen ich wie und für was sinnvoll und schnell nutzen kann.

25.08.2021 - 22:13 Uhr

Die Technologien, die Du vor der Datenbank hast, sind prinzipiell irrelevant.
Man wählt einen Datenbank-Typ (nicht das Produkt) primär anhand seiner Daten aus. Eine Hobby-Film-Datenbank ist halt alles andere als komplex oder hat immense technische Anforderungen.
Ist halt ein Unterschied zu komplexe Strukturen mit tausender Entitätstypen, 10GB Daten pro Minute bzw 1 Mio Requests pro Minute verarbeiten muss.

Wobei ich hier wie gesagt ein Test-Projekt fahre. Also mit mindestens 10 Mio Datensätzen bei den Filmen und vermutlich 50 Mio Darsteller.
Ich könnte auch meine Konzertbesuche im großen Stil aufbauschen und eine Community-Plattform für Musik- und Konzert-Fans, inkl. Ticketsystem usw... hochziehen.

Tatsächlich skaliere ich aktuell für Bestellungen in einem Hardware-/Game-PC-Shop überraschend altmodisch (vertikal) da ich häufiger auf Collections zurückgreife, als auf komplexe Objektverzweigungen. Dennoch tauchen diese ab und zu auf und ich benötige eine dann vor allem schnelle Abfragen. Derzeit bin ich mit MSSQL-EF-GraphQL zufrieden, auch wenn ich vermutlich mit den Bibliotheken von Dapper und BulkCopy schneller wäre, aber EF war leider vorgeschrieben. Aber wie gesagt, vorerst muss ich mich um ein DataStorage-Typ (also relational, NoSQL, Graphs, etc...) entscheiden, bevor ich mich um die Zugriffs-Bibliotheken oder Server-Client-Schnittstellen/APIs kümmere.

Ich werde sicherlich weder ein zweites Facebook, noch ein zweites Amazon oder Eventim bauen. Aber ich orientiere mich genau an diesen Riesen, da ich vermute, dass Daten bald schon sehr billig werden. - Nicht missverstehen, ich meinte damit die Payloads (Transferraten) und die Speicherung (Festplatten). Nicht, dass man mir hier gleich wieder Datenschutzmissachtung oder leichtsinnige Datensicherheit vorwirft.

  • Strukturelle Daten: relationale DB (SQL)
  • Unstrukturelle Daten: nicht-relationale DB (NoSQL Documents)
  • Beziehungen mit Eigenschaften: Graph Database (NoSQL Graphs)
  • Unstrukturierte Daten basierend auf Keys: (NoSQL KVP)
  • ...

Da sich Daten oft mit mehreren Datenbanken prinzipiell darstellen lassen, spielt der Use Case mittlerweile auch eine sehr große Rolle:

Genau das macht es kompliziert. Ich hab mehrere Projekte im Kopf, die ich bislang ausschließlich relational weggespeichert habe. Aber viele diese Projekte stoßen an ihre Grenzen. Beispielsweise eine Tool, das mein Team damals für einen großen Reiseanbieter entwickelt hatte, bei dem Meeting-Räume vom Kunden aus nach Wunsch bestückt, ausgestattet und gebucht werden konnten, nur eben, dass hier ca. 11K Hotels weltweit in der Liste standen und es sowohl eine B2B-Vewaltungsplattform für die Hotels, als auch eine B2C-Plattform für Endkunden gab. Hier hatten wir mit MSSQL-DBs und EF gestartet und sind dann auch MongoDB und filtering/pagination REST-APIs gewechselt. Damit haben wir zwar zu Beginn die Datenzugriffszeiten halbiert, aber leider bei jeder Anpassung/Erweiterung der Objekte eben auch einen Grad der Entartung gebrochen. Nach der vierten Version, hatte ich wirklich lust auf klar strukturierte relationale SQL-DBs. 😉

Das ist der Grund, wieso ich mich seither nicht mehr heran gewagt habe. Also weder an KVP, noch an Documents. GraphDBs kenne ich noch gar nicht, aber mir gefallen die unstrukturierten (dynamischen) Schemas, aber die Entitäten und die Beziehungen, um zumindest halbwegs klare Strukturen getrennter (unabhängiger) Datenmodelle (bspw. in 3. Normalform) zu realisieren. Das macht es etwas aufgeräumter. - Übrigens auch der Grund, wieso ich immer schon mal PostGreSQL (also relational, aber mit JSONB-Datenfeldern) ausprobieren wollte. Auch, weil man schnell Daten weg schreiben kann.

Grobe Regeln:

  • Generell ist NoSQL schneller als SQL
  • Generell skaliert SQL meist vertikal besser, NoSQL meist horizontal besser
  • Generell garantiert SQL eine höhere Datensicherheit (ACID)
  • Graph- und KVP Datenbanken sind schnell im Lesen, nicht wirklich toll für viele Änderungen

Aber ja: einzelne Produkte kann sich im ein oder anderen Punkt unterscheiden und jedes Produkt hat gewisse eigene Features.

Mein Tipp: schau Dich mal im Netz um, wer so mit welcher Datenbank unterwegs ist und wieso.
Gibt da eigentlich viele richtungsweisende Tutorials / Videos zu, an denen Du Dir was abschauen kannst.

Bei großen Projekten arbeitet man zusätzlich oft mit gewollten Redundanzen über verschiedene Datenbank-Typen hinweg, um die jeweils optimale Datenbank zu nutzen.
Damit kann man sehr viel Performance raus holen und immense Kosten sparen.

Meine Idee, war es die DataStorage-Technologie via dynamischer Abfrage-API als serverseitige Schnittstelle zu betreiben und dann clientseitig komplexe Requests/Responses abzufragen.
Ah, die klassische Einhorn-Anforderung 😉 : wünscht sich jeder, existiert aber nicht.

Ja vermutlich, die eierlegende Wollmilchsau gibt es eben nicht. Aber genau was du bereits sagtest, ich müsste mir die Beispiele anschauen. Allerdings ist das leichter gesagt, als getan, wenn man quasi nur die klassischen relationalen Datensysteme gewohnt ist. Ich weiß gar nicht genau, wonach ich suchen soll, ganz zu schweigen, dass viele größere Projekte oft gerne ein wenig ein Geheimnis um ihre Technologien machen.

Mein zweites Problem ist der Zeitmangel. Ich werde vermutlich weder die Zeit finden mich in alle Schemata und Datentypen perfekt einzulesen und das Wissen darüber hinaus noch zu erweitern. Aber vielleicht finde ich interessante Anregungen für eventuell spätere Anforderungen.

Aber deine Infos sind schonmal wegweisend und hilfreich für meine Recherche. 🙂
Danke schön.

25.08.2021 - 06:11 Uhr

Hallo Leute.

Kurz zur mir. Ich bin C#.NET-5 Entwickler und arbeite normalerweise mit MSSQL, welches ich serverseitig via GraphQL-API anspreche.
Privat habe ich nun ein paar Tage Zeit, mich etwas mit Alternativen zu relationalen SQL-DBs zu beschäftigen.

Ich habe in der Vergangenheit auch schon in Projekten gearbeitet, in denen MongoDB in Kombination mit Redis genutzt wurde.
Das waren aber bereits existierende Projekte, so dass ich dort nur zugearbeitet hatte und kaum am Data-Design mitwirken konnte.

Ich bin also bislang noch relativ altmodisch veranlagt.

Gehen wir mal von einem einfachen privaten Test-Projekt aus:

  • JS-Client (simples web, kein CMS)

  • C#.NET-5.0 (serverseitig)

  • Film-Datenbank (DVDs und OnDemand, etc...)

  • Objekte:

  • Filme & Beschreibungen

  • Schauspieler & Biografien

  • Genres & Altersbeschränkungen

  • Regisseure

  • Produzenten & Labels

  • Veröffentlichungsdatum & verfügbare Sprachen

  • Romane auf denen die Filme basieren

  • Freunde & deren Vorlieben (welcher Freund passt zu welchem Filmabend)

  • keine Lust zahlreiche Endpunkte in der REST-API - für unterschiedlichste Filter und Abfrage-Kombinationen (Zusatzinfos) - zu erzeugen

  • komplexe Abfragen (clientseitig erzeugt) via serverseitiger API (um nicht zahlreiche Endpunkte, sondern gezielt kombinierte Objekte mit einer Abfrage zu erhalten)

  • CRUD-Optionen (also auch Mutationen)

Ich dachte entweder an GraphQL oder an eine JSON-API, gerne aber auch etwas ganz anderes. 🙂
Wichtiger ist sowieso eher die Datenspeicher-Technologie. Also GraphDB oder klassisches relationales SQL oder doch Key-Value-Pairs?
Bei den KVPs habe ich die Befürchtung, dass die Struktur schnell entarten kann. Und ist eine GraphDB tatsächlich eine Kombination aus KVPs und wie strukturiert man dann Fremdschlüssel?

Ich habe hier absolut noch keine Idee und würde mich freuen, ein paar Vorschläge für DataStorage-Technologien und Anregungen zur Nutzung von euch zu lesen. 🙂
Ich wollte immer auch schon mal PostGreSQL mit JSONB probieren. Aber auch GraphDBs (ich kenne ehrlich gesagt keine einzige) klingen interessant.

Meine Idee, war es die DataStorage-Technologie via dynamischer Abfrage-API als serverseitige Schnittstelle zu betreiben und dann clientseitig komplexe Requests/Responses abzufragen.

30.08.2019 - 14:12 Uhr

Na ja, Swagger mach ich ja nur, ums zu Testen. Postman wird aus irgendeinem Grund geblockt, da ich das SSL-Zertifikat vom IIS-Express nutze. - Mal sehen, wie das am Ende laufen wird.

Aber Danke für den Hinweis, werde ich am Wochenende mal reinschauen. - Wie gesagt, das ist nur ein Dumm, damit ich weiß, wie ich das zukünftig machen müsste. Feinschliff erfolgt später, ich kann eben nicht gleich perfekt anfangen, außerdem lerne ich mit der Erfahrung.

Am Wochenende muss ich mir mal die ASP.NET Core Identity Auth(s) anschauen. Da ich Dapper nutze, ließ sich das Identity Framework (EF) nicht nutzen. - Es gibt zwar ein paar Git-Projekte, die es mit Dapper realisieren, aber unabhängig davon benötige ich eine einfache Lösung für JWT-Tokens, die ich später mit OAuth-2 (sobald der Kunde etwas mehr Budget freigibt) verknüpfen kann. Ist nur ein internes Dashboard (quasi eine Verwaltungsplattform), daher nicht so kritisch, aber man möchte ja auch für spätere Projekte dazulernen. Wer weiß, vielleicht wird das Ding irgendwann mal riesig und sobald Rechnungen über die Plattform generiert werden, muss es auch wirklich ernsthaft sicher sein.

Aber dennoch vielen Dank für eure Unterstützung, ich nehme das wirklich alles sehr ernst.

30.08.2019 - 10:23 Uhr

Sicherlich wäre es nützlich später noch eine Validierungsklasse zu erzeugen. Wie gesagt, ich probiere noch aus. Aber händisch erzeugt wäre es dieser Code... (Sorry wegen des Quick'n'Dirty-JSON-Objekts)

// PUT api/users/5
[HttpPut("{id}")]
public object Put(int id, [FromBody] Dictionary<string,object> modFields)
{
    var userProbs = typeof(User).GetProperties().Select(p => p.Name);
    var unknownFields = modFields.Keys.Where(k => !userProbs.Contains(k, StringComparer.OrdinalIgnoreCase));
    if (unknownFields.Count() == 0)
    {
        //return _repo.ModUser(id, modUser);
        return 1; //just a test-obj
    } 
    else
    {
        var httpStatusCode = 400;
        var fieldsErrJson = unknownFields.Select(f => "\"" + f + "\": [\"[User." + f + "] field is unknown.\"]").ToList();
        var statusJson = "{ \"errors\": { "+String.Join(", ",fieldsErrJson)+" }, \"title\": \"One or more validation errors occurred.\", \"status\": "+httpStatusCode+", \"traceId\": \"0:00000000\" }";
        
        HttpContext.Response.StatusCode = httpStatusCode;
        return JsonConvert.DeserializeObject<object>(statusJson);
    }
}

Und erzeugt...

Falls das irgendwie einfacher geht, immer her damit. 😃

30.08.2019 - 10:21 Uhr

Wie ihr seht, hatte ich ja die Rückgabe nur testweise auf VOID zrückgesetzt. Daher ist die reguläre/erfolgreiche Rückgabe ja auch auskommentiert. Wobei ich gerade sehe, das sich - wieso auch immer das return weggenommen hab. - Aber egal...

Ich suche nur nach einer Möglichkeit, neben dem StatusCode noch eine zusätzliche ErrorMessage zurückzugeben. Aber ich denke, verstanden zu haben, dass ich die Fehler quasi als klassische Rückgabe definieren müsste, ...

...bei einem invaliden POST wurde diese Ausgabe automatisch generiert, auch wenn mir die Trace-ID noch nicht ganz klar ist. **:::

Notfalls baue ich die Ausgabe händisch nach.
Ich vermute mal
[400 Bad Request] und als ErrorMessage eben die obige Ausgabe mit den aufgelisteten unbekannten Feldern. Ggf. würde sicherlich auch ein 5XX gehen, aber ich finde keinen passenden.
Daher den 400er oder doch den 404er. Wobei die angefragte Ressource ja gefunden wurde, nur dass die übermittelten Felder nicht existieren.

29.08.2019 - 17:52 Uhr

Na ja, die Reflection sei mal dahingestellt, aber gibt es in .NET Core keine individuelle ErrorMessages mehr?

29.08.2019 - 17:34 Uhr

Infos:
:::

#:::

Hallo,

ich habe heute meine aller erste CRUD-API unter .NET Core gebaut und möchte gerne einen Fehler zurückgeben...

// PUT api/users/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] Dictionary<string,object> modFields)
{
    var userProbs = typeof(User).GetProperties().Select(p => p.Name);
    var unknownFields = modFields.Keys.Where(k => !userProbs.Contains(k, StringComparer.OrdinalIgnoreCase));
    if (unknownFields.Count() == 0)
    {
        // _repo.ModUser(id, modUser);
    } 
    else
    {
        HttpContext.Response.StatusCode = 502;
    }
}

Okay, so kann ich also einen StatusCode zurückgeben, aber gibt es hierzu auch einen Status oder eine ErrorMessage? - Will dem Client sagen, dass sein Put-Data nicht unterstützte Felder besitzt und daher nicht verarbeitet wird.

Das muss ja möglich sein, hier auch einen Fehler-Text ausgeben zu können oder?

EDIT: Swagger-PUT-Screenshot:

10.12.2018 - 14:44 Uhr

@emuuu

Joa, aber ich denke, das ist ähnlich wie AWS aufgebaut. Die bieten ja inzwischen auch ziemlich coole Cluster, bzw. Virtualisierungen an. Also anstatt eines Serververbundes hat man dann 100 Docker-Einheiten für die Datenbanken, 100 für die statischen Files (Buckets) und 100 für die API... oder so. Der Vorteil bei AWS ist, dass die Server rund um die ganze Welt bereitgestellt werden, was für ein Projekt, das eventuell einen internationalen Charakter hält interessant werden könnte.

Mal sehen. Ich hab das Projekt und auch die Idee jetzt quasi allein übernommen, da mein Compagnon aktuell mit einem großen EK-VK-Projekt beschäftigt ist, das für mich wirtschaftlich sowieso nicht mehr nachzuvoellziehen ist. (Sorry, der kauf irgendwelche Massenware im Ausland billig auf, "veredelt" sie und verkauft sie dann in der westlichen Welt wieder mit 400% oder so ähnlich.)

Na ja, wenn das Produkt nun mein privates ist, muss ich schauen, wie ich es finanziere und ob ich neben meinem normalen Job auch noch genug Zeit finde ein Projekt dieser Größe - ganz alleine - nebenbei aufzuziehen.

Kollegen haben mir geraten erstmal einen "keen prototype" zu basteln und den dann zu publizieren und gleich vom ersten Tag an Werbekunden, Partner usw. zu finden, um 100% der Anteile zu halten, ohne sich zu verkaufen.

Toll ist aber auch, dass nun kein Druck mehr auf mir lastet. Also kann ich in aller Ruhe mal die ganzen .NET-Core-Tutorials durchspielen und dann Stück für Stück den deutlich vereinfachten Prototypen basteln. 😃 Ich meine so schwer kann es nicht sein, eine einfache RESTful/CRUD-API in Core zu bauen. Etwas schwieriger ist die Entscheidung des Frontends... Angular, VueJS, React... oder doch was ganz anderes? Vielleicht auch nur ES6 und ein JS-dyn. CSS. Muss ich mir alles demnächst mal überlegen. Aber ist ja auch nur der Prototyp.

Sobald sich Interesse, Erfolg und Wirtschaftlichkeit signalisieren, müsste ich sowieso ein paar Entwickler organisieren, die das Ding von Grund auf neu aufsetzen. Denn Code-Optimierungen und Refactoring wird dann in 1-2 Jahren nicht mehr funktionieren. 😉

Jetzt wird aus einem programmatischen Projekt ein Business-Projekt und da habe ich wirklich viel hinzuzulernen. Wirtschaftlichkeit, Keen-Business und auch

27.11.2018 - 11:30 Uhr

Zunächst mal an alle... Vielen Dank für eure Infos.

Ich weiß, dass es für @Abt nicht vorstellbar scheint, dass jemand in eine Programmiersprache wechselt und bereits mit dem ersten Tag, Aufträge oder Stories abarbeiten muss. - Wenn es nach mir ginge, würde ich auch gerne erstmal 1-2 Jahre mit den Fremd-Modulen, Erweiterungen oder zusätzlichen Libs spielen. - Aber ich bin eben genau wegen dieser Projekte von PHP (was zwar in den vergangen Jahren stärker geworden ist, aber noch lang nicht mit Python oder eben .NET mithalten kann) zu C#.NET gewechselt und muss irgendwie mein Brot verdienen.

Ich habe mir jetzt noch mal die exakte Beschreibung des JWTs durchgelesen und dort im Forum einige Beiträge entdeckt, die das ähnlich sehen wie ich. Aber inzwischen ist mir klar, dass die Checksum einfach nicht via JS generiert werden kann, da sie sonst offen läge und somit auch "manipulierbar" wäre. Insofern die Checksum (inkl. Verschlüsselung) auf beiden Seiten im Backend liegt, ist das problemlos nutzbar. - Mir gefällt dieser Standard sogar, da er simpel und durchdacht ist.

Wegen der URLs, ... na ja, wenn der Zugriff auf mein System ausschließlich übers Frontend (bspw. JS) stattfindet, dann liegt der Token offen und kann auch kopiert genutzt werden. Genau dieser Kopien dienen Token ja auch. - Allerdings, sollte die Authentifizierung entweder backendseitig (bzw. nicht öffentlich) stattfinden oder ich muss beim Zugriff eins Client eben die URL prüfen, um sichergehen zu können, dass hier niemand einfach nur einen Token kopiert. Kurz gesagt, müsste der Token beidseitig mit der URL verknüpft sein. 😃

Aber aktuell versuche ich meinem Kunden diesen ganzen Blödsinn von wegen MiddleWare-AccessLayer auszureden. Ich denke, wichtiger ist es, dass er eine möglichst starke REST-API (CRUD, GPPAD) bekommt und ein vernünftiges Verifizierung-Werkzeug. Hier dachte ich an oAuth oder im größeren Stil dann eben den IdentityServer4. - Leider mach ich das derzeit FullStack und finde kaum Zeit für DockerContainer, obwohl ich die Technologie zur Pseudo-Virtualisierung sehr interessant finde. - Ich muss das Produkt erstmal auf den Markt bringen und zwar so sauber und sicher wie es nur geht. Mein Kunde träumt von einem Tool, dessen Frontendausmaße gigantisch werden und aktuell geht mehr Zeit darauf, meinem Kunden Traumschlösser auszureden, als tatsächlich daran arbeiten zu können.

Dazu kommt, dass ich (selbst für einen PHP-Entwickler) als FullStack trotz C#.NET-Core + VueJS + Bootstrap + WebPack ziemlich schwach bezahlt werde - und eben alleine bin. Aber wenn das Ding erstmal läuft, gehört mir ein Teil davon und ich hoffe, dass es sich dann auch wirtschaftlich lohnt. 😃 Wobei ich das Projekt in erster Linie angenommen habe, weil mir der Aspekt und die Idee, sowie die programmatischer Herausforderung gefällt.

**Nochmals vielen Dank an alle. 😃 **
Besonders an @emuuu, denn ich erkenne in dir ein wenig den Mutmacher. 😃 Ich mag solche Menschen, die verschwinden nämlich in der Szene mehr und mehr. 😕

15.11.2018 - 16:17 Uhr

Ein einfaches Rich-Client über eine clientseitige REST-API wäre mir und vielen Kunden lieber. 😕

15.11.2018 - 14:21 Uhr

@Abt:

CORE
Klar, Core steht ganz oben auf meiner Liste, sobald ich das Ding unter Linux zum Laufen gebracht habe. 😉 Aktuell kämpfe ich genau damit. 😕 Aber Scott Hanselmann ist ja ein guter Vorreiter. Ich bin nur nicht so fit auf der Linux-Shell.

URL-CHECK
Na ja, eigentlich ist der ClientToken unsinnig. Wichtig ist allein die Identifizierung der URL, von der aus der Request aufgerufen wird. Der Token sollte eigentlich für einen Kunden stehen, somit allerdings auch diversen Daten und Einstellungen, so beispielsweise der eingesetzten URL für die Aufrufe meiner REST-API. - Mir geht es primär darum, verhindern zu können, dass unauthentifizierte URLs (Kunden) auf meine REST-API zugreifen können. Tokens lassen sich kopieren, aber ich vermute, dass es Wege gibt, die Remote-URL abzufragen.

USER-AUTH
API-User-Authentifizierung erfolgt durch einen UserToken. Daher absolut Standard-Prozedere. Wenn ich das mit der Kunden-URL hinbekommen würde, wäre ich auch mit oAuth zufrieden. - Mal sehen. Aber die User-Authentifizierung ist aktuell nicht das Problem. Sondern eben jener URL-Check.

REST-VALIDATE
Auch die Validierung sollte kein Problem darstellen. Da die REST-API nah an RESTfull orientiert sein soll und um keine Special-Methods oder logische Operatoren erweitert wird. 😃 Klar müssen alle Request-Attribute geprüft werden, aber das sollte einfach sein.

@emuuu:

Ich bin immer noch der Meinung, dass sich JWT (wird von ++IdentityServer4 ++verwendet) einfach zu klonen oder zu manipulieren ist, insofern eine der beiden Seiten clientseitig (bspw. JavaScript) verarbeitet wird. Na ja, ... ich bin grundsätzlich ein großer Fan von oAuth, hatte ich auch schon in PHP eingesetzt. Muss nur schauen, wie ich ein Script (bzw. alle Request aus einer bestimmten Remote-URL) identifiziere, ohne dass jemand einfach den Token kopieren kann. - Die User-Auth ist mir dann klar.

Prinzipiell finde ich, ist dieser ++IdentityServer4 ++genau das, was ich suche. Auch wegen dem Unterschied zwischen Kunde und Nutzer. - Komplizierter ist allerdings meine Business-Logik, denn Nutzer sind global und können auch auf verschiedenen Kundenseiten die Requests abfeuern, also egal über welche Kunden-URL. Kunden sind die, die den Service auf ihrer Website anbieten wollen. User sind unabhängig von den Kunden, daher global. Sie können sich also auf allen Kunden-URLs anmelden und den Service nutzen.

Rein fiktives Beispiel:

Stellt euch vor, es gibt eine Datenbank voll mit Kneipen, Restaurants usw... Der User hat einen Account bei eat-n-drink.com und kann dort auch seine Daten verwalten oder schauen, welche Kneipen ihn ansprechen (Ort, Getränke, Essen, Preise, Öffnungszeiten, Fotos, Bewertungen, etc...).

Dann gibt es ja noch die Vergleichsseiten (die sprießen ja derzeit aus jeder Richtung hervor), bspw. wohin-in-berlin.de oder dublin-afterwork.ie. Der "Kunde" (also bspw. wohin-in-berlin.de) kann auf alle für ihn relevanten Daten (Kneipen, Restaurants, Clubs usw...) zugreifen, sein Design/Frontend aber selber auswählen. - Erstmal egal ob nun ein registrierter Nutzer darauf zugreift oder nicht.

Wenn nun aber ein bereits registrierter User über wohin-in-berlin.de darauf zugreift (via Cookie oder Web Storage) oder - falls der Kunde es auf seiner Seite zulässt - sich direkt einloggt, kann der User nun auch sehen, welcher seiner Freunde bereits welche Kneipen besucht hat oder deren Bewertungen lesen, ggf. auch eigene Bewertungen verfassen. Quasi über wohin-in-berlin.de auf die REST-API von eat-n-drink.com darauf zuzugreifen.

Okay, das Beispiel hinkt ein wenig, da es hier sicherlich zu Datenschutz-Konflikten kommen würde (aber ich bin kein Jurist und es ist ein rein fiktives Beispiel, hätten auch Reisebüros und die API eines Fluglinien-Portals sein können). Trotzdem soll aber nun verhindert werden, dass was-geht-ab-in-berlin.x.gp jene REST-API benutzen kann, ohne bei mir einen Kunden-Account anzulegen, diverse Settings zu setzen und natürlich den AGBs zuzustimmen.

14.11.2018 - 19:06 Uhr

Hallo. 😃

Zunächst einmal, ich bin noch kein C#/ASP.NET-Profi.

Ich plane eine relativ einfache REST-API die als Controller zwischen den JS-Clients (JS-Requests) meiner Kunden (bestimmte Kunden-URLs) und meiner serverseitigen Datenbank (BSON/JSONB).

Zunächst sollen folgende Dinge geprüft werden:1. Passen [URL] und [ClientToken] zusammen? - Also ob der Request überhaupt valid/accepted ist. [] Hat der angemeldete Benutzer (via [UserToken]) die nötige Berechtigung, um auf den Controller (Database), Action (Collection), Index (SingleElement) zuzugreifen? [] Sind alle notwendigen Übergabe-Parameter gesetzt, bzw. sind diese gültig/anwendbar?[/list]

Jeder Request soll folgende Token mitsenden: [list][] ClientToken [i](identifiziert den Kunden, der es bei sich auf der Website einbaut und überprüft den Referer/RemoteAddr/RemoteHostIp mit der im Kundenbereich hinterlegten URL)[/i] [] UserToken [i](jeder in meinem System registrierte Benutzer erhält nach dem Login einen lebenslänglichen UserToken, ggf. zusätzlich noch eine SessionId)[/i] [*] RequestToken [i](bei jedem Request wird ein Token mitgesendet, der den Request identifiziert und somit auch serverseitig dokumentiert)[/i][/list]

Der ClientToken repräsentiert quasi den Referer, bzw. den Kunden, der Zugriff auf meine API hat. Der RequestToken ist erstmal rein optional und mehr eine ID, als ein echter Token. Ob es überhaupt eine Session-ID benötigt weiß ich auch noch nicht.

Zu meiner Frage... Ist es möglich in der Global.asax einen der folgenden Werte sicher abzufragen... [list][]Referer [] RemoteAddr [*] RemoteHostIp[/list]...um die URL des abrufenden REQUESTs zu prüfen?

Denn irgendwie muss ja ermittelt werden können, von welcher URL (oder zumindest IP-Adresse) aus, der Request abgesendet, bzw. der Response angefordert wurde. Ich möchte eben verhindern, dass irgendein [URL]http://www.gemeiner-mitbewerber.com[/URL] auf meine API zugreifen kann, indem er sich einfach das Script inkl. ClientToken von einer Kundenseite auf seine eigene Website herüber kopiert.

Falls da jemand eine Lösung hat, immer her damit.
Ich werde außerdem mal prüfen ob Apache, nginx, IIS oder Kestrel bereits einen HostnameLookup überprüft und ggf. Regeln befolgt, wie Zugriff nur für [LISTE VON URLS].
Aber irgendwie würde ich es dann trotzdem gerne nochmal in meiner API prüfen.

Besten Dank im Voraus. 😃

22.08.2018 - 14:56 Uhr

@Abt Cool Danke. 😃 Ich les' mir das mal durch.

20.08.2018 - 17:50 Uhr

UserAgent == Browser
Nicht die Tabs. Cookies sind für gewöhnlich auch nicht tab- sondern browserbasierend.

Aber ich gebe dir Recht. Mein Problem ist, ich habe das Projekt übernommen und arbeite quasi "ohne Bezahlung" daran. Okay, wenn alles läuft, gehört mir ein Stück von dem Ding. 😃

Mein Plan war es zunächst eine "LEAN & KISS" (schlank und einfach) Umsetzung zu bewerkstelligen. Sollten da wirklich einmal mehr als 50K Nutzer mitspielen (was tatsächlich möglich wäre), würde ich tatsächlich über oAuth oder OpenID nachdenken. Jedoch - wie du bereits bemerkt hast - bin nicht mehr auf dem neusten Stand und auch sicherheitstechnisch nicht optimal geschult. - In diesem Fall würde ich also mit einer einfachen Session-Cookie-Lösung (gerne auch via SHA-256) starten und später einen Experten zu Rate ziehen, der sich um die oAuth oder OpenID kümmert.

Ich denke, dass ich alleine kaum viel Zeit finde, mich aktuell tief in etwas einzuarbeiten. Ich will zwar auch nicht nur aus Fertigmodulen irgendwas zusammenstecken, aber je weniger ich daran arbeiten muss, desto mehr Zeit finde ich, noch kleinere Korrekturen anzupassen.

Das Problem ist, uns fährt ein rinnt ein wenig die Zeit davon. Und wir müssen erstmal Online kommen, um erste Kunden an Land zu ziehen. Sobald es sich selbst finanziert, ist es etwas einfacher, sobald es Gewinn abwirft, werden wir ein größeres Team benötigen, somit auch Leute, die mehr drauf haben als ich. 😉

Ich musste leider auch etwas Abstand von der geplanten RESTful-Anbindung nehmen. Wobei ich generell schon versuche so wenig wie möglich Specials einzubauen. Aber sie ist nicht mehr 100% regelkonform. Sobald ich mal mehr Zeit hab und das Ding läuft, werde ich da mehr Energie und Zeit investieren und auch hoffentlich mit weiteren, guten Entwicklern sauberere Lösungen finden.

Das gemeine an dem Projekt ist, es ließe sich in 3 Sätzen erklären, aber die vielen Module, Seitenbereiche, Einzelfälle, Objekt-Strukturen und Methoden blähen das Ding unglaublich auf. Andererseits, wenn es so einfach wäre, hätte es ja schon irgendwer so umgesetzt. 😉

Daher erstmal die Schmalspurbahn, später den ICE und schließlich die Magnetschwebebahn. 😛

20.08.2018 - 12:12 Uhr

Zunächst, ich habe bereits früher (schon ein paar Jahre her) bereits unter PHP ganz klassische Logins mit einem Nutzerkennwort und einem MD5-Abgleich aus der DB eine Session mit entsprechender ID generiert. Das ist ja auch nicht sonderlich schwer, kann jedoch ein paar Schwächen und Nachteile aufweisen.

Jetzt unter C#.NET würde ich das sehr ähnlich machen, jedoch habe ich zwei entscheidende Änderungen zu früher...
*Single-Page-Application (via jQuery/JSON-API) *Multi-Page-Session (Nutzer kann mehrere URLs parallel im selben UserAgent öffnen) *Multi-User-Login (wobei ich hier noch nicht sicher bin)

Meine Idee war, erstmal nur mit einem einzigen Cookie zu arbeiten, für Infos wie UserID, SessionID oder Token.

Die Nutzdaten der einzelnen SPA-"Seiten" sollen via localStorage/sessionStorage verwaltet werden. So kann ich das Cookie ausschließlich zur Identifizierung nutzen.

Was benötige ich überhaupt?1. Benutzername + Passwort (clientseitig in MD5(+Salt+Pepper) umgewandelt) werden an den Server gesendet

  1. MD5-Abgleich mit der Datenbank und Rückmeldung falls nicht gefunden
  2. Session-Erzeugung bei erfolgreichem Login und Generierung einer öffentlichen SessionID
  3. falls Multi-User-Login, Erzeugung eines nutzerseitig austauschbaren Token (bin aber kein Freund von, versuche ich meinem Kunden auszureden)
  4. Speicherung der SessionID/Token im Cookie (ggf. mit Laufzeit)

Eigentlich ganz klassisch. Was eventuell schwieriger wird ist die Idee (wie es ja auch FB macht) eines Multi-Session-Logins. Also ein Nutzer kann mehrere Sessions (auf unterschiedlichen UserAgents/IPs) erzeugen, nutzen und auflisten lassen.

Ich muss noch mit meinem Kunden reden. Ich sehe persönlich erstmal keinen Grund, für clientseitige Tokens. Bestenfalls als "Sicherheits-Fallback". Auch bin ich dagegen, dass vom selben UserAgent aus, mehrere Benutzerkonten parallel verwendet werden dürfen. Kann aber sein, dass das tatsächlich möglich sein muss. 😦

Wichtig wäre bei einer Plattform (quasi ein Community-Projekt), dass ein Nutzerkonto sich an mehreren Standorten parallel anmelden können. Bei FB ist das ja anschaulich mit der Sitzungsliste gelöst. Jeder UserAgent besitzt seine eigenes SessionCookie und kann somit gelistet, beobachtet, verwaltet oder auch über eine andere Sitzung beendet werden.

Auch wichtig (und schon wieder bei FB abgeschaut) ist, dass bestimmte Objekte mehreren Nutzern zugewiesen werden können müssen. Beispielweise legt jemand eine Veranstaltung an. Bspw. ein Rock-Festival... normalerweise ist diese Veranstaltung dann administratorisch nur dem Ersteller zugewiesen. Wenn der nun aber bemerkt, dass die Veranstaltung zu groß für ihn allein ist, könnte er bestimmte Bereiche auch anderen Nutzern freigeben. Beispielsweise Bearbeitungsberechtigung für Unterseiten/Artikel für die jeweils auftretende Band oder auch seinen eigenen Mitarbeitern. Wichtig ist, dass in der History (zumindest intern) später noch nachweisbar ist, wer was geändert hat.

Aber jene Zuweisung beschreibt ja im Grunde nur die Objekt-Berechtigung und hat erstmal nix mit dem Login zu tun.

Meine Frage an euch ist, hat jemand bereits Erfahrungen mit Logins? Gibt es unter C#.NET irgendwas, auf das ich besonders achten muss? Tipps, Tricks und Vorschläge werden gerne angenommen. 😃

31.05.2018 - 20:32 Uhr

Okay, hab's erstmal anders gelöst, aber wäre trotzdem interessant, wie man das löst. 😃

31.05.2018 - 15:47 Uhr

Also, das aktuelle Projekt läuft bereits, aber bisher nur auf der Kommandozeile, daher habe ich eine WebApp erzeugt, die mir schnell und einfach via CsQuery ein kleine Template läd und Ein- und Ausgabeformate liefert.

Mein Problem, ich erhalte immer nur HtmlControl - Elemente, benötige aber HtmlGenericControl-Elemente, um damit arbeiten zu können.

Die Ausgabe-Datei habe ich wie gewohnt mit id="html" runat="server" versehen:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="default.aspx.cs" Inherits="PageDefault" ValidateRequest="false" %>
<!DOCTYPE html>
<html id="html" runat="server">
	<head>
		<title></title>
        <meta charset="utf-8"/>
	</head>
	<body></body>
</html>

Ich kann im Page_Load() auch auf das html-Objekt zugreifen, aber es handelt sich eben um ein HtmlControl. Für meine Designklassen benötige ich es jedoch als HtmlGenericControl. 😕

Ich versteh es gerade nicht. 😕 Das habe ich schon hunderte Male gemacht, aber jetzt will es nicht mehr. 😕

Einiger Unterschied...
.Net-Framework: 4.6.1 (früher habe ich 4.5.1 verwendet)

20.05.2018 - 16:25 Uhr

@Abt:

Ist kein öffentlicher Blog 😃 Nur einzelne Subdomains werden öffentlich erreichbar sein. Suchmaschinen sind daher tatsächlich Nebensache. - Ich habe gezielt eine ganze bestimmte Nische im Auge. Es geht mir auch mehr um die Community hinter dem Blog. Wird auch mehr eine Pinnwand, als ein Forum/Blog, jedoch auch keine Kopie von Facebook. Wie gesagt, es geht um eine Nische. 😃

Übrigens sind/waren die meisten Social-Communities für Suchmaschinen unerreichbar. Das gilt für Facebook, aber auch für Lokalisten, StudiVZ, MySpace, etc...

Ich habe natürlich auch vor ein paar einzelne Pages und Bereiche öffentlich zugänglich zu machen, damit wir nicht vollständig bei den Suchmaschinen rausfallen, aber der Großteil ist nur für Mitglieder einsehbar.

Ich verwende für die SPA natürlich die URL-Manipulation, um separate, valide URLs erzeugen zu können. Bspw.: sub.domain.tld/AREA/SUBAREA/PAGE/SUBPAGE/ auch wenn immer nur die eine physische Datei aufgerufen wird, werde ich via JavaScript history.pushState() (etwas vergleichbares unterstützen seit 3-4 Jahren eigentlich alle bekannten Browser) den URL so manipulieren, dass ich quasi immer neue URLs erhalte und via JS und RESTful-API im Hintergrund eben nur noch der Content ausgetauscht, bzw. regeneriert wird.

* https://developer.mozilla.org/de/docs/Web/Guide/DOM/Manipulating_the_browser_history

19.05.2018 - 17:09 Uhr

Jo, das ist der Plan.

Leider habe ich mich erst vor 3 Monaten zu eine RESTful-API (JSON) entschieden. Da ich vorher auch keine SPA geplant hatte.

Bisher existiert ja noch nicht viel von meinem Blog. Daher kann ich noch schieben und schummeln. Aber bis Ende des Jahres sollte das Ding zumindest laufen. Veröffentlichen würde ich ihn gerne im Februar 2019. - Viel Zeit, weiß ich. Aber es ist auch etwas größer als ein normaler Blog. 😃

Vielen Dank für eure Hilfe. - Wenn ich live gehe, dürft ihr gerne mal ganz streng drüber gucken. 😛

19.05.2018 - 14:25 Uhr

@Abt:

Na ja, aber erstens ist es nicht untypisiert, da ich einfach ein DictObj mit entsprechenden Value-Typen dahinterlege (für PrimaryKey [= !TimeStamp/DateTime)] und Values [= !UserId, Message, ?Comments].

Zweitens bin ich mir noch nicht sicher, ob ich gleich BSON (MongoDB) schreibe, aktuell habe ich aber noch keinen eigenen Server und werde vermutlich aus Kostengründen zunächst auf PostGreSQL zurückgreifen, bevor ich mich entscheide, ob ich Trans-/MSSQL oder MongoDB nutze. Der Vorteil von PostGre ist, dass es sowohl JSON-Queries, als auch SQL-Queries unterstützt, auch wenn es vorwiegend relational aufgebaut ist.

Ich war früher nie ein Fan von NonSQL-DBs, aber ich nutze im Job inzwischen sehr gerne MongoDB und die Geschwindigkeit ist der Hammer, außerdem bekommt man mit vernünftigen Abfragen bereits das exakte Ziel-JSON für die API-Ausgabe, auch wenn ich es immer noch mal überprüfe. 😉

Ich habe mit OracleSQL angefangen, als ich Ende der 90er meine Ausbildung als Banksoftware-Entwickler begonnen hatte. Bin aber irgendwann zur Webentwicklung gekommen und habe auf PHP und MySQL umgeswiched, was auch lange sehr gut funktioniert hat. Dann habe ich irgendwann wieder mit C#.NET zu tun gehabt und natürlich auch mit MSSQL und war gerade im Bezug auf BigData sehr begeistert davon. Inzwischen bin ich an der Entwicklung mächtiger SPAs beteiligt und bin derzeit begeistert von MongoDB.

Aber privat hat mir jemand PostGre empfohlen, als Mix zwischen SQL und JSON. Anfangs hielt ich das für lächerlich, ... aber inzwischen bin ich überrascht, wie schnell eine gut konfigurierte und saubere PgDb läuft. Gerade wenn es um die Fütterung einer JSON-API geht. Du hast die Standardfilter für SQL-Queries um Relationen vernünftig über SQL-JOINs zu bedienen, dann aber wieder JSONs dahinter, die du mit JSON-Filtern noch detailliert steuern kannst. Die Ausgabe ist dann quasi schon das finale JSON, quasi ohne eine Zeile C#-Code. 😉 Wobei ich via LINQ natürlich die RESTful-Controller (DataCtrl, UserCtrl) und somit auch die DB-Abfragen via C#.NET steuere. Trotzdem, PostGre ist ein toller Mix aus relationaler und non-relationaler DB. 😃

Zurück zum Thema, hab auch über eine weitere Tabelle nachgedacht. Ist ja auch nicht kompliziert, daraus ein JSON für die Ausgabe zu konvertieren, aber irgendwie ein JSON und aufgrund weniger Tabellen auch der SQL-Query kürzer, handlicher und einfacher. - Im Grund gebe ich dir aber Recht, denn nach 3rd-NF sollte man die Daten in deren Einzelheit kleinstmöglich unterteilen und dann vom Typen her separat archivieren. - Ich versuche im SQL meine 3NF-Rgel nie zu brechen, aber in diesem Fall bot es sich einfach an.

18.05.2018 - 18:06 Uhr

@Abt: Damit hatte ich fast gerechnet. Also dann dachte ich daran, es wirklich extrem einfach zu gestalten und einfach zwei DB-Felder (aktuelleVersion, jsonAlleAltenVersionen). Im ersten Feld steht wie weiterhin der aktuelle Text, in einer weiteren Spalte dann ein JSON der vergangenen Versionen. (*Kurze Info, es handelt sich um eine SPA, daher werden alle DB-Abfragen, auf Anforderung via JSON/AJAX-Response gesendet.

Danke an euch alle. - Dann spare ich mir da jegliche Diff-Tool-Entwicklung/-Einbindung.

15.05.2018 - 12:23 Uhr

Hallo.

Ich möchte gerne eine Art Blog bauen, bei dem es möglich ist, Nachrichten zu schreiben und auch zu kommentieren. Ähnlich wie es in den meisten Blogs oder auch bei Facebook in der Pinwall möglich ist, soll man seine Texte im Nachhinein noch ändern können.

Prinzipiell hätte ich einfach gesagt, dass man für jede Version einen eigenen Datensatz in die DB schreibt. Aber bei extrem langen Texten, nimmt das mit der Zeit auch einen relativ großen Umfang ein. Vor allem dann, wenn mehrmals nur einzelne Buchstaben/Rechtschreibfehler verändert worden oder ich irgendwann wirklich sehr viele, schreibwütige Nutzer zähle.

Jedesmal einen LongText in die DB zu hämmern, nur weil sich einzelne Zeichen verändert haben, erscheint mir als sparsamer DB-Entwickler etwas verschwenderisch. - Okay Speicherplatz ist natürlich nicht so wichtig/teuer, wie die Leistung des Servers, aber sooft wird die History einzelner Beiträge ja gar nicht angeschaut, es dient daher ehr einem Fallback-Zweck, bzw. der optionalen Möglichkeit, bei Interesse.

Ich bin ein GIT-Fan und würde gerne "nur" die Veränderungen zur aktuellen Version (also genau umgekehrt) wegspeichern. Es soll deswegen die "aktuelle" Version als Volltext weggespeichert werden, da diese Version am häufigsten aufgerufen wird und daher nicht erst dynamisch zusammegschustert werden sollte.

NEU...

Lorem ipsum dolor sit amet, [B][COLOR]consetetur[/COLOR][/B] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.

Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. [B][COLOR]At vero eos et accusam et justo duo dolores et ea rebum.[/COLOR][/B]

Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.

Nam liber tempor cum soluta nobis eleifend option congue [B][COLOR]nihil[/COLOR][/B] imperdiet doming id quod mazim placerat facer possim assum.

ALT...

Lorem ipsum dolor sit amet, [B][COLOR]consteteur[/COLOR][/B] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.

Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. [B][COLOR]Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.[/COLOR][/B] 

Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.

Nam liber tempor cum soluta nobis eleifend option congue [B][COLOR]nhiil[/COLOR][/B] imperdiet doming id quod mazim placerat facer possim assum.

Zunächst dachte ich an eine IndexOf()-Substring-Liste für die Änderungen. Aber ich habe auch nicht die Zeit, sämtliche Logik zu prüfen. Vermutlich gibt es zu diesem Thema bereits jede Menge fertige Skripte, Module oder Apps.

Ich kenne sie nur nicht.

Vielleicht hat ja jemand von euch bereits so etwas verwendet.
Wichtig ist noch, dass ich kein fertiges Blog-System verwende, sondern etwas eigenes entwickeln möchte.

08.05.2018 - 11:39 Uhr

Exakt. Mich hatte es auch irritiert. Anscheinend ist es auch bislang noch nie aufgetreten, aber als ich vor ein paar Wochen begann die Localizations zu übernehmen, wollte ich eben auch mal ein wenig "Ausprobieren".

Fakt ist, ich habe in den vergangen Wochen so viel gelernt, dass ich mein privates Projekt ganz, ganz-ganz anders aufziehe. Auch die Übersetzungen/Lokalisierungen werde ich einfacher, aber sicherer bauen. Außerdem mag ich diese Mixen an verschiedenen Technologien nicht. Ich werde es vermutlich relational aufziehen, somit auch zweidimensional, auch wenn ich Sprachen und Gruppen voneinander getrennt nutzen möchte, soll das nur der Übersicht dienen. Vermutlich wird das dann eine Art "#tagging"-System. (...außerdem werde ich mir klevere Übersetzer zulegen 😉 )

Ich denke euch trotzdem riesig für eure Tipps. Denn ich will diesen Projektteil nur noch hinter mich bringen, um mich dann im Juni wieder sinnvolleren Entwicklungen zu widmen. 😃

Beste Grüße.

07.05.2018 - 13:42 Uhr

Bitte nicht erschlagen, ... aber das ist sogar erwünscht, da i18n es ebenfalls so löst und somit quasi meine App exakt das wiedergibt, was auch das i18n-Skript darstellt. - In Sachen Logik ist das sogar korrekt, besser wäre es, wenn der Syntax-, bzw. Schema-Validator von i18n das rechtzeitig als Fehler erkennen würde. Das tut er aber nicht immer...

Wenn ich den Key zweimal in verschachtelter Form setze, erkennt i18n den Fehler.
Wenn ich den Key einmal verschachtelt und einmal als FullPath-String setze, erkennt i18n es als valid an und überschreibt den ersten Value durch den letzten.

Wird aber hoffentlich nicht so oft vorkommen. Und falls doch, bekommen die Übersetzer eben eins auf die Finger. 😉

07.05.2018 - 12:05 Uhr

Werde ich so machen, also sowohl via for(), als auch die zuvorige Prüfung der Tiefe, bzw. der Anzahl der Children. Bei Iterativ muss ich leider passen, da ich auch nicht 100% verstehe, wie das funktionieren soll. Kenne den Begriff nur aus der Mathematik 10. Klasse, damals habe ich Nährungswerte berechnen müssen. - Sorry. 😕 - Wobei ich bei einem StackOverlow sowieso erstmal ein Problem hätte, da dann allein schon die Anzahl der Verschachtelungen und auch der grundsätzlichen Key-Value-Einträge so hoch wären, dass es mal irgendwann echt eine Nummer zu heftig wird. 😉 Dann würde ich tatsächlich mit meinem Kunden telefonieren müssen, wenn das Übersetzungs-JSON pro Sprache jenen Rahmen bricht.

Außerdem wurde mir noch von meinem Kollegen empfohlen, anstatt .Add() besser dict[path] = value zu wählen, so können doppelte Pfade überschrieben werden. Eigentlich sollten gar keine doppelten Pfade vorkommen, aber es ist mir mit einem kleinen Trick gelungen ein valides i18n-File zu erzeugen, dass tatsächlich zwei gleiche Pfade besitzt. 😉 Es wird beim Aufruf aber immer nur das letzte genutzt. Dennoch ist die alte-C-Schreibweise tatsächlich sicherer, damit es nicht zu einem Duplicate-Key-Error kommt.

04.05.2018 - 15:04 Uhr

Jetzt klappt es! 😃

METHOD

public Dictionary<string, object> GetJsonLevels2(JToken JsonObj, Dictionary<string, object> JsonLevels)
{
    if (!JsonObj.HasValues)
        JsonLevels.Add(JsonObj.Path, JsonObj);
    else
        foreach (JToken JsonValue in JsonObj)
            GetJsonLevels2(JsonValue, JsonLevels);
    return JsonLevels;
}

RUN

Dictionary<string,object> JsonLevels = JsonHelper.GetJsonLevels2(JToken.Parse(I18n03), new Dictionary<string, object>());

RESULT (JSON of Dictionary<string,object>)

{
  "testObj.keyA": "valueA",
  "testObj.keyB": "valueB",
  "testObj.keyC": "valueC",
  "testArray[0]": "arrayA",
  "testArray[1]": "arrayB",
  "testArray[2]": "arrayC"
}

Und das beste, ich habe jetzt gefühlt 7'322 Zeilen JSON reingehämmert, mit bis zu 11 Verschachtelungen und es läuft einwandfrei.

Prinzipiell danke ich euch allen, aber ganz besonders @Taipi88, da seine Lösung mich erst einmal dazu gezwungen hat, mir die JTokens genauer anzuschauen. Dank dieses Vorschlags, bin ich nun endlich auch bei meiner Lösung.

Ich weiß, es ist nicht wirklich sauber und auch nur ansatzweise schön, aber ich sag's mal so, die Auftraggeber haben mir auch keine sauberen und schönen Daten geliefert, ... ich finde dafür ist es programmatisch doch recht annehmbar. 😉

Privat würde ich das ganz anders machen. 😛

04.05.2018 - 12:14 Uhr

@T-Virus

Nein, ich habe bereits eine bestehende i18n-"JSON" und dann will ich dort hinein die ebenfalls aus dem alten Projekt bestehenden RESX hinzufügen.

Die bestehende i18n-Struktur soll um die RESX erweitert werden. Dazu muss ich aber erstmal schauen, welche Daten in der JSON liegen, um sie zu erweitern, bzw. ggf. zu ersetzen.

Und ich darf weder die i18n noch die RESX anfassen. 😕 Nur ggf. Duplikate dürfen aus der i18n-JSON herausgelöscht/ersetzt werden.

Hey, ich habe das Projekt nicht so geplant. Als Entwickler hätte ich das sowieso viel einfacher gehalten, bspw. mit einer CSV, thematisch durch "Kommentarzeilen" sortiert und pro Spalte eine Sprache. 😉 So habe ich es in meinem privaten Projekt vor, notfalls lässt sich diese CSV in SQL oder auch in Mongo-BSON unterbringen, solange sie zweidimensional bleibt.

Aber wann hat ein Entwickler schon mitzureden? Das Projekt ist schon 3-4 Jahre alt und soll jetzt in eine SPA überarbeitet werden. Aber irgendwie haben die POs und PMs niemals mit den PLs oder DEVs kommuniziert. 😦

Ich versuche mich jetzt hier dran und dann bin ich glücklich einen komplett neuen Teil zu bauen.

04.05.2018 - 10:48 Uhr

@Abt

Na ja, die kommen von den Übersetzern. - Ginge es nach mir, wären die nur 2-Dimensional und würden (ist schließlich ein Übersetzungs-JSON) nur Texte (Strings) enthalten.

Aber ich kann die Übersetzer leider nicht kontrollieren. Ausgegeben werden alle Inhalte als Strings, somit könnte ich notfalls damit leben, diese ggf. einfach zu "<strings>" zu verwandeln.

Aber die unzähligen Dimensionen/Verschachtelungen machen es mir schwer. 😕

04.05.2018 - 10:43 Uhr

@Taipi88

Ich verstehe nicht ganz, womit ich die Methode aufrufe. Ich hab's testweise mal public gesetzt und übergebe mein JObject. - Aber ich bekomme wieder einen Fehler.

Fehlermeldung:
Member 'DLH.ParseJsonToDictionary(Newtonsoft.Json.Linq.JToken)' cannot be accessed with an instance reference; qualify it with a type name instead

Muss ich hier einen JToken erzeugen und übergeben?

PS: Das war ja auch nur ein Beispiel-JSON, in den Anwendungsfällen sind die JSONs teilweise bis zu 10 Dimensionen tief.

04.05.2018 - 10:28 Uhr

@MrSparkle
Mal ist es ein Str, mal ein Int. Unterschiedlich, je nach JSON. 😕 Sonst könnte ich ja direkt das Dictionary durch-schematisieren. 😉

03.05.2018 - 19:07 Uhr

Okay, ich versuch es mal...

JSON


{
  "testObj": {
    "keyA": "valueA",
    "keyB": "valueB",
    "keyC": "valueC"
  },
  "testArray": [
    "array1",
    "array2",
    "array3"
  ]
}

Ein ganz simples JSON. Ich denke, es spricht für sich selbst.

Methode


    public Dictionary<string,object> GetJsonLevels(dynamic JsonLevelObj, string JsonLevelKey, Dictionary<string, object> JsonLevels)
    {
        int i = 0;
        if (JsonLevelObj.GetType().Name == "JObject")
            foreach (var JsonLayer in new Dictionary<string, object>(JsonLevelObj.ToObject<Dictionary<string, object>>()))
                foreach (KeyValuePair<string,object> X in GetJsonLevels(JsonLevelObj[JsonLayer.Key], JsonLevelKey + "." + JsonLayer.Key, JsonLevels))
                    JsonLevels[X.Key] = X.Value;

        if (JsonLevelObj.GetType().Name == "JArray")
            foreach (var JsonLayer in JsonLevelObj.ToObject<string[]>())
                foreach (KeyValuePair<string, object> X in GetJsonLevels(JsonLevelObj[JsonLayer.Key], JsonLevelKey + "[" + (i++) + "]", JsonLevels))
                    JsonLevels[X.Key] = X.Value;

        else if (JsonLevelObj.GetType().Name == "JValue")
        {
            JsonLevels.Add(JsonLevelKey, JsonLevelObj);
        }
        return JsonLevels;
    }

Ich trenne zwischen "JObject", "JArray" und "JValue". Die Methode soll sich für die ersten beiden Typen erneut aufrufen, dabei aber sowohl das die nächste Ebene des ursprünglichen Objektes öffnen, als auch den JSON-Key um eine Ebene erweitern. Bis irgendwann das letzte "JValue" erreicht und ausgegeben wurde.

Aufruf


dynamic myObject = JsonConvert.DeserializeObject<dynamic>(Json);
Dictionary<string, object> FinalDict = new Dictionary<string, object>();
FinalDict  = GetJsonLevels(myObject, "", new Dictionary<string, object>());

Als Ergebnis hätte ich mir eigentlich ein Dictionary<string,object> gewünscht, das in etwas so aussieht:

Dictionary<string,object>

{
  "testObj.keyA": "valueA",
  "testObj.keyB": "valueB",
  "testObj.keyC": "valueC",
  "testArray[0]": "arrayA",
  "testArray[1]": "arrayB",
  "testArray[2]": "arrayC"
}

Ich hab's mal als JSON dargestellt, aber es soll eben ein Dictionary<string,object> sein.

Error> Fehlermeldung:

Server Error in '/' Application.
Collection was modified; enumeration operation may not execute.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

Source Error:

Line 83: {
Line 84: dynamic JsonNextLevelObj = JsonLevelObj[JsonLayer.Key];
Line 85: foreach (KeyValuePair<string, object> X in GetJsonLevels(JsonLevelObj[JsonLayer.Key], JsonLevelKey + "." + JsonLayer.Key, JsonLevels))
Line 86: {
Line 87: JsonLevels[X.Key] = X.Value;

Source File: [METHODE] Line: 85

Stack Trace:

[InvalidOperationException: Collection was modified; enumeration operation may not execute.]
System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) +56
System.Collections.Generic.Enumerator.MoveNext() +14790106
DLH.GetJsonLevels(Object JsonLevelObj, String JsonLevelKey, Dictionary2 JsonLevels) in [METHODE]:85 System.Dynamic.UpdateDelegates.UpdateAndExecute4(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3) +275 DLH.GetJsonLevels(Object JsonLevelObj, String JsonLevelKey, Dictionary2 JsonLevels) in [METHODE]:85
System.Dynamic.UpdateDelegates.UpdateAndExecute4(CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3) +772
_default.Page_Load(Object sender, EventArgs e) in [AUFRUF]:259
System.Web.UI.Control.OnLoad(EventArgs e) +103
System.Web.UI.Control.LoadRecursive() +68
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1381

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.7.2633.0

Angeblich habe ich eine Collection manipuliert, dabei versuche ich nur die nächste Ebene aufzurufen. 😕

03.05.2018 - 15:55 Uhr

Erstmal Danke für die Hinweise.

Um kurz zu erklären, was ich vor habe...

Ich bekomme RESX-Files und versuche diese in meine Vue-i18n-Files einzubinden. Falls dort doppelte Values auftauchen, soll dem Bearbeiter in einer kleinen WebApp angezeigt werden, welche Stellen eventuell bereits redundante Values enthalten.

Allerdings sollen jetzt nicht etwa die redundanten Values/Keys aus dem RESX gelöscht werden, sondern die bereits vorhandenen aus der Vue-i18n (habe ich in ein JSON konvertiert). Bzw. für jedes Matching soll der Editor entscheiden, ob jener ursprüngliche JSON-Eintrag gelöscht (und durch RESX ersetzt) werden soll oder eben nicht und somit eine Redundanz erhalten bleiben soll.

Falls gelöscht wird, soll ein internes Skript sämtliche Content-Files (HTML, JS, ...) im Verzeichnis durchsuchen und nach den ursprünglichen ObjKey-Strings suchen und diese dann durch die neuen RESX-KeyStrings ersetzen.

03.05.2018 - 15:37 Uhr

Aber genau darum geht es ja. JSONs sind ja auch nicht typisiert. Wenn ich im Voraus nicht weiß, nach welchem Schema das JSON aufgebaut ist, bleibt mir ja nur <dynamic>. 😕

03.05.2018 - 15:02 Uhr

Ist es grundsätzlich möglich, ArrayLists() zu verschachteln?


        ArrayList Array = new ArrayList();
        ArrayList L1 = new ArrayList { "L1a", "L1b", "L1c" };
        ArrayList L2 = new ArrayList { "L2a", "L2b", "L2c" };
        ArrayList L3 = new ArrayList { "L3a", "L3b", "L3c" };

        Array.Add(L1);
        Array.Add(L2);
        Array.Add(L3);

        foreach (ArrayList Ls in Array)
        {
            foreach (ArrayList L in Ls)
            {
                Response.Write(L);
            }
        }

Hier erhalte ich eben ein Cast-Problem für den Typen ArrayList, ab der zweiten Dimension. 😕

Grundsätzlich möchte ich ein "unbekanntes" JSON (also unbekannt vom Aufbau her) so integrieren, dass ich die Inhalte schnell und einfach mit einem "fixem" nested Dictionary (fixe Struktur) vergleichen. Das Problem ist, dass die JSONs auch viel-dimensional sein können und das JObject ziemlich nervig ist.

es ergibt sich eine nahezu endlose Abfrage wie if(jobjjarray|jobject) foreach() { if(jobjjarray|jobject) foreach() { if(jobjjarray|jobject) foreach() { if(jobjjarray|jobject) foreach() { ... }}}}

JSON-Objekte scheinen irgendwie perfekt geeignet, wenn man mit den PHP MixedVars arbeiten kann, aber bei strengen Schematas wird es schon komplizierter. Ich sehe mich schon Zeile für Zeile durch das JSON durchparsen und dann die ZeilenNummern als Index zu verwenden (string[]). 😕

Ich vermute meine Anfrage wird euch merkwürdig vorkommen, aber ich bin auch totaler Anfänger in C#.NET. Ich bitte euch daher, mich nicht pauschal als schlechten Programmierer abzustempeln.

THX.

28.02.2018 - 16:03 Uhr

Hmmm... sollte ja nur zeigen, wie ich die Dateien angelegt hatte.

Also die Solution und auch meine App heißt bei mir Peppermint (das ist einfach der Name, auf meinem Server laufen mehr als 600 C#.NET-Projekte). - Ansonsten ist alles gleich, nur dass ich mal gelernt hatte, das man keine Partial Classes nutzen soll. Aber ich schau mal, ob ich das so hin bekomme.

Danke erstmal. 😃

28.02.2018 - 14:24 Uhr

So, hab die drei Klassen jetzt mal in einzelne Dateien gesetzt. Sollte zwar eigentlich keinen Unterschied machen, aber die liegen dann alle im entsprechenden Namespace Peppermint.Crypt. Demnach dann also Peppermint.Crypt.Aes und dann die Methode .Dec("test","password"). Aber gleiche Fehlermeldung.

Ich werde mich jetzt mal durch ein paar US-Foren kämpfen, um das Problem zu fixen.

28.02.2018 - 14:15 Uhr

Das mit der Instanz habe ich schon längst versucht, aber die gleiche Fehlermeldung. (so wie oben im Screenshot) Eigentlich logisch, dass ich die Instanz von der Klasse nicht erzeugen kann, wenn ich keine Referenz zum Namespace erzeugt bekomme.

28.02.2018 - 11:55 Uhr

Nutze eigentlich PascalCase. Musste mich sehr daran gewöhnen, da ich aus der alten C-Syntax nur Kleinbuchstaben kannte. - Ich hab neugestartet, gleicher Fehler. 😕

28.02.2018 - 11:07 Uhr

Wollte keinen zusätzlichen Beitrag erzeugen. Und meine PNGs wurden zwar angehängt, aber nicht korrekt positioniert.

28.02.2018 - 10:50 Uhr

Hallo.

Ich habe bisher nur ein wenig mit Web-Site-Projekten gebastelt aber noch kein Web-App-Projekt entworfen. - Bzw. ist das hier mein erstes...

ProjectName: Peppermint

Mir wurde gesagt, dass ich das Verzeichnis App_Code nicht mehr so verwenden kann, wie bisher und daher am Besten ganz weg lasse. Jetzt habe ich ein Verzeichnis mit Namespace und Classes darin hinzugefügt...

 [B]Peppermint.Crypt.Crypt.cs[/B]: [csharp]using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Peppermint.Crypt
{
    public class Md5
    {
        /// <summary>Convert Text to MD5 hash.</summary>
        /// <param name="PlainText">Text (String), which should be hashed.</param>
        /// <returns>MD5 hash (String).</returns>
        public static string ToHash(string PlainText)
        {
            if (!string.IsNullOrEmpty(PlainText))
            {
                return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(Encoding.Default.GetBytes(PlainText)));
            }
            return string.Empty;
        }
    }

    public class Aes
    {
        /// <summary>Encrypt plain text to an AES String.</summary>
        /// <param name="PlainText">Plain text (String), which should be encryped.</param>
        /// <param name="Key">Key, which should be used for encrypting (like a password).</param>
        /// <returns>Plain text (String).</returns>
        public static string Enc(string PlainText, string Key)
        {
            string CipherText = string.Empty;
            byte[] BytesBuff = Encoding.Unicode.GetBytes(PlainText);
            using (System.Security.Cryptography.Aes Aes = System.Security.Cryptography.Aes.Create())
            {
                Rfc2898DeriveBytes Crypto = new Rfc2898DeriveBytes(CryptMd5.ToHash(Key), new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
                Aes.Key = Crypto.GetBytes(32);
                Aes.IV = Crypto.GetBytes(16);
                using (MemoryStream MStream = new MemoryStream())
                {
                    using (CryptoStream CStream = new CryptoStream(MStream, Aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        CStream.Write(BytesBuff, 0, BytesBuff.Length);
                        CStream.Close();
                    }
                    CipherText = Convert.ToBase64String(MStream.ToArray());
                }
            }
            return CipherText;
        }

        /// <summary>Decrypt an AES String to plain text.</summary>
        /// <param name="CipherText">Encrypted text (String) to decrypt.</param>
        /// <param name="Key">Key, which should be used for decrypting (like a password).</param>
        /// <returns>Plain text (String).</returns>
        public static string Dec(string CipherText, string Key)
        {
            string PlainText = string.Empty;
            CipherText = CipherText.Replace(" ", "+");
            byte[] BytesBuff = Convert.FromBase64String(CipherText);
            using (System.Security.Cryptography Aes = System.Security.Cryptography.Create())
            {
                Rfc2898DeriveBytes Crypto = new Rfc2898DeriveBytes(CryptMd5.ToHash(Key), new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
                Aes.Key = Crypto.GetBytes(32);
                Aes.IV = Crypto.GetBytes(16);
                using (MemoryStream MStream = new MemoryStream())
                {
                    using (CryptoStream CStream = new CryptoStream(MStream, Aes.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        CStream.Write(BytesBuff, 0, BytesBuff.Length);
                        CStream.Close();
                    }
                    PlainText = Encoding.Unicode.GetString(MStream.ToArray());
                }
            }
            return PlainText;
        }
    }

    public class Des
    {



    }
}[/csharp] [IMG]https://img1.picload.org/image/daaoidla/crypt.png[/IMG]
 
 
 
[B]Peppermint.Default.aspx.cs[/B]: [csharp]using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Net;
using System.IO;
using Peppermint.Crypt;
using CsQuery; //docu: https://github.com/jamietre/CsQuery

namespace Peppermint
{
    public partial class PageDefault : System.Web.UI.Page
    {
        //string test = "test";
        string Test = Peppermint.Crypt.Aes.Dec("value", "key");

        protected void Page_Load(object sender, EventArgs e)
        {
            HtmlGenericControl HtmlContainer = new HtmlGenericControl("body");

            Response.Write("TEST: "+ HtmlContainer.InnerHtml.ToString());
            HtmlContainer.ID = "NewControl";

            CQ Dom = HtmlContainer.InnerHtml.ToString();
            Dom["body"].Text("yeaaahhh");
            Dom.Render();
            HtmlContainer.InnerHtml = Dom["html"].Html();
        }
    }
}[/csharp] [IMG]https://img1.picload.org/image/daaoidll/default.png[/IMG] 

Warum kann ich nicht auf den Namespace zugreifen? Auch wenn ich es direkt in Peppermint setze, klappt es nicht. 😕 - Ich will doch nur aus der Default die Methoden in meiner Crypt aufrufen. 😕

Was mach ich falsch?

03.10.2017 - 00:25 Uhr

Hat das irgendwelche Nachteile (Sicherheit, Geschwindigkeit, Stabilität), ich habe die sogar in unterschiedliche Dateien ausgelagert. 😕

Letztendlich ist es doch nur eine Form der Strukturierung. Ich sehe viele Hilfs- und Standardklassen, die genauso unterteilt sind.

02.10.2017 - 13:30 Uhr

@LaTino
PS: Der Mapper gefällt mir aber. 😃 Ich denke darüber nach, ihn etwas verändert im Admin-Bereich zu verwenden. 😃

Vielen Dank für die Idee.

02.10.2017 - 13:27 Uhr

😉 Nein, aber ich verstehe worauf du hinaus willst. Ich habe kein RESTful, da mein NGINX - sobald es alle Ports durchlässt, leider 20% langsamer läuft. Ich fang nur :80 und :443 fürs später geplante SSL. 😃

Ich habe aber vor, später ein RESTful zu integrieren, vermutlich muss ich es in 1-2 Jahren sowieso neu bauen, denn noch sind meine Erfahrungen nicht so gut wie eure. 😃 Sobald ich besser werde, will ich dann auch umfassendere Systeme ausbauen. Bis dahin ist meine Infrastruktur deutlich größer. Geplant sind mehrere Cluster über die wichtigsten Markets verteilt. Aktuell wird mein Vorhaben (zumindest Idee) ganz gut bewertet und ich hab leider nur 1'000 EUR im Monat, die ich in meine Infrastruktur investieren kann. 😕 Also heißt es schnell online gehen, schnell Kunden und Geld verdienen und in 2 Jahren dann international ausbauen. 😃

Naiv - sagen die einen, aber ich hab ja nix zu verlieren. 😃 Ist ja nicht so, dass ich hunderttausende von EUR investiere. 😉 - Wenn alles klappt, suche ich dann ab 2020 ca. 4-6 C#.NET - Webentwickler. 😛

Und was meine Controller betrifft, die sind ziemlich sauber gebaut. - Und die Reflections unterlasse ich, hab ja schon gesagt, dass ihr mich davon überzeugt habt. Ich habe eine andere Möglichkeit gefunden, und die Controller prüfe ich via Switch-Case und spreche sie entsprechend so an. - Hab's getestet, läuft superschnell (100 Mio => 1'200ms). Und meine lokale Maschine ist nur ein I7-Laptop mit 256GB RAM, mein Server ist etwa 4x besser ausgestattet und es laufen wneiger Hintergrunddienste. 😉

Gerade trenne ich noch meinen MSSQL-Server ab, damit dieser ebenfalls auf einer separaten Maschine läuft. - Jetzt muss ich mir nur noch eine Lösung für MongoDB ausdenken. Ich habe keinen Anbieter gefunden der WinServer+MongoDb anbietet. Also ein weiterer vServer und dort dann irgendein LinuxServer den ich dann via Intranet anspreche. Hab intern 255 IPs. Wird schon klappen.

Mein Problem ist, dass ich aktuell 10 Baustellen gleichzeitig bearbeitet, anstatt mich auf meine Programmierung zu konzentrieren. Neben Backend, DBs, Frontend (Templates und JSON-HTTPResponses), Infrastruktur und der Hybrid-Mobile-App (Cordova), ... ist mein größtes Manko die Tatsache, dass ich noch nie Buchhaltung oder Wirtschaftsrecht hatte. 😕 Abr ich habe 2 befreundete Banker, die mir ein wenig unter die Arme greifen wollen. 😃

01.10.2017 - 21:39 Uhr

Ich habe ein alternatives Routing gebaut mit C++ auf NGINX. Dahinter läuft der IIS für die klassische Website und ein Apache für den Controller, der meine AWS-S3-buckets verwalteten soll, sobald ich genug Gewinn mit dem Produkt einnehme und mir einen großen AWS-S3-Pool leisten kann. Dabei handelt es sich um einen vServer mit NGINX (für das Routing), ein weiterer vServer mit einem LAMPP (um die Amazon-S3-Buckets zu verwalten) und ein dedicated Root Server mit IIS für meine C#.NET-Anwendungen.

Ich bin ein Freund von stabilen und sicheren Anwendungen, ich komme nämlich ursprünglich aus der Banksoftware-Entwicklung, habe anschließend aber 12 Jahre nur mit PHP in einer kleinen Webagentur programmiert, bis ich vor etwa 2.5 Jahren vollständig mit allem abgebrochen habe und mir ein paar Bücher zu Ruby, Python, JSF/JW und ein paar andere gesucht hatte... unter anderem bin ich irgendwann bei C# hängengeblieben, da ich bereits ein wenig Ahnung von C++-Syntax hatte und ich die strengen Richtlinien nicht verurteile. (auch wenn mir DynVarNames und MixedVars schon hin und wieder fehlen)

Also habe ich meinen wirklich gut bezahlten Job gekündigt, um in einer kleinen Webschmiede zu ein wenig C#.NET zu lernen. Leider war ich bislang nur in kleinen Projekten mit winizigen Projekten über 3-4 Templates mit maximal 4-6 dynamischen Landingpages vertaut worden. Also quasi nur eine .NET-WebPage, keine wirklich großes WebProject. Dafür kann ich aber nix. Also begonn ich eine Idee auszuspinnen, mit der man mit wenig Investment viel Geld verdienen kann.

Also habe ich dieses Projekt so vorbereitet, dass es relativ günstig umzusetzen ist. Da ich im Büro aufgrund meiner kleineren, minderwertigen Projekte kaum Weiterbildung erfahre, muss ich mich eben selber weiterbilden, was wäre also naheliegender, als mein eigenes Projekt umzusetzen.

Ich dachte zunächst an ein klassisches MVC mit den Standard-DesignPattern. Dann hab ich mir die Vorlagen in VisualStudio angeschaut und die waren leider alle viel zu umständlich. PHP oder nicht, wenn ich eines gelernt habe, dann ist es das KISS. Ich bin kein Fan von Fertig-Prozeduren, die ich - wenn ich sie manuell nachbaue fast 5mal schneller bekomme. Außerdem sind immer mehr Entwickler dabei von MVC auf DCI umsteigen. Ich habe eine Mischform aus beiden DVCR, wobei das Routing vollständig aus dem Modell herausgetrennt wird. Da dieses eigentlich extrem schnell laufen muss.

Mein Hauptrouting ist natürlich auf C++ über NGINX gelöst. Aber ich benötige in C#.NET trotzdem ein verdammt schnelles Rückfall-Routing

CONTROLLER -> INDEX -> VALUE|VALUE|VALUE
...oder...
CONTROLLER -> INDEX -> VAR:VALUE,VALUE,VALUE | VAR:VALUE,VALUE, VALUE

Ich habe mir diverse Lösungen angeschaut. Aber ich finde nix, das so schnell läuft, wie das, was ich gerade aufbaue.

Sobald ich meine Testversion beta geht, bin ich gerne an eurer Kritik interessiert. Vielleicht kann ich bis dahin ein paar Programmierer gewinnen, die jung und frisch denken und von den klassischen Mustern abweichen. Außerdem benötige ich Programmierer, die ein wenig Interesse am Hacken haben und die Sicherheit meines Projektes prüfen. 😛

Bitte verzeiht mir, dass ich euch hier nix erzählen kann, zumindest keine Details über das Projekt. Ich taste mich gerne voran, aber manchmal falle ich auf die Nase... aber ich weiß mich wieder hochzurappeln. So schlecht bin ich nicht, aktuell nur etwas unerfahren, aber sehr unkonventionell. 😛

01.10.2017 - 18:24 Uhr

Warum seid ihr eigentlich so aggressiv? Ich habe höflich gefragt und habe auch nichts gegen Vorschläge oder Tipps. Ich sagte bereits, dass ich erst seit kurzem in C# programmiere. Würde ich bereits seit 10 Jahren damit programmieren, hätte ich vermutlich bereits ganz andere Möglichkeiten gefunden. - Stattdessen werft ihr mir alle vor, was für ein Idiot ich bin und wie dämlich meine Konstruktionen sind. Es grenzt teilweise an Beleidigung, was ihr da von euch gebt.

Ich bin eigentlich ein sehr dankbarer Forennutzer. Aber ich verzichte hiermit auf weitere Hilfe von euch.

Ich habe mich bei meinem Controller-Action-Routing an der klassischen Vorlage beim ASP.NET MVC (+Routing) orientiert, das im VisualStudio bereits als Vorlage existiert. Aber meine Test haben ergeben, dass meine Lösung fast 19x schneller läuft, als die Vorgabe. Meine Controller können via Git sehr schnell separat angepasst werden, ohne dabei Merge-Crashs zu erzeugen.

Ich bin euch dankbar für den Hinweis und die Tipps, dass Reflections möglichst unterlassen werden sollten. Ich habe das inzwischen geprüft und festgestellt, dass diese unglaublich prozessortlastig in der Nutzung sind und habe außerdem in anderen Foren nachgelesen, in welchen Fällen sie zu Problemen und sehr wackligem Code führen. - Wie gesagt, dass habe ich in anderen Foren recherchiert. Auch wenn ich nur dank euch hier, überhaupt auf meine Keywords gekommen war, um danach zu suchen.

Es tut mir leid, dass ihr keinen C#-Experten vor euch hattet, der auch noch so unverschämt war, euch Super-C#-Entwickler (die ja alle ihr Geld damit verdienen) naive und simple Fragen zu stellen oder eventuell Vorstellungen zu haben, die eben jemand hat, der 20 Jahre lang PHP entwickelt hat und erst vor wenigen Monaten auf C# umgestiegen ist.

Ich entschuldige mich sehr demütigst.
Ich ziehe mich von euch Profis zurück.

01.10.2017 - 17:46 Uhr

Nein, die Idee ist, im Falle der Nichtexistenz soll der Controller einfach einen 404-Content basteln, bzw. den entsprechenden 404 hinterlegen, alternativ kann es auch eine Weiterleitung zur Starteseite sein.

Bei den Controllern seh ich da auch wenig Probleme, da die Leute selten per Hand ausprobieren, ob es eventuelle noch andere gibt. Aber bei den Actions kommt das überraschend oft vor, dass die User versuchen aus events/view/1/ mal einfach events/edit/1/ zu machen. Was ich selbstverständlich verhindere, da ich bei den Actions immer zunächst die Nutzerberechtigungen prüfe.

Aber ich habe eben mehr als 12 Controller und jedesmal viele unterschiedliche Actions (teilweise 24 Actions) darunter. Demnach müsste ich immer statisch prüfen...

... ob

C.ListOfController.Contains(InputController)

... und dann ob

C.EVENTS.ListOfActions.Contains(InputAction)

und diese Listen hinterliegen dann wie gesagt als (Controler) List<string> in der Klasse C und weitere Listen als (Action) List<string> in den einzelenen Controller-Klassen.

Ich müsste diese Listen dann aber bei jeder Veränderung im Code, auch wieder anpassen, da es sich ja um statische Listen handelt.

01.10.2017 - 17:16 Uhr

Ganz simpel...

ich habe ein Controller-Action-Routing. Ich habe also meine Controller-Klassen und darin liegen die Action-Methoden, die ein weiteres Vorgehen abhandeln...

bspw.

*/events/list/
*/events/view/1/

Zunächst prüfe ich, ob ein Controller "events" existiert. Da ich Klassen immer groß schreibe, eben ToUpper()...
Dann prüfe ich, ob die Action "view" existiert und übergebe möglichst eine ID via Parameter.

Insofern beides existiert rufe ich also C.EVENTS.View(1) auf, um Models und Views entsprechend der Interaktion zu verhochzeiten.

Der Aufruf ist nicht schwierig, aber problematischer ist die Tatsache, dass ich in der Klasse C eben alle existierenden Controller auf den vom User angeforderten auf dessen Existenz prüfen möchte. Klar, könnte ich in der Klasse C auf einfach ein Objekt bzw. Attribut - bspw. eine List<string> - anlegen, in der ich dann alle möglichen Attribute statisch hinterlege und abfragen kann. Das gleiche dann entsprechend in den einzelnen Klassen/Objekten meiner Controller, wo ich dann ein statisches Listenelement hinterlege, in dem ich wiederum alle Action-Methoden der entsprechenden Controllerklasse statisch hinterlege.

Dann lassen die sich abfragen, aber... ich bin faul. Dah muss ich immer diese Liste erweitern, sobald ich neue Controller oder Actions erstelle oder ggf. umbenenne oder gar lösche.