auch wenn statt der exe eine dll verwendet wird, brauchst du eine linux dll die zu deiner platform passt. Also so einfach wirds erstmal nicht klappen. Glaube da ist es einfacher ffmpeg passend im container zu installieren und dieses durch den wrapper aufrufen zu lassen.
Denn Pfad zu der Binary kann man ja übergeben.
Es wird aus dem proto Contract gleichzeitig auch eine REST API erzeugt die du per Put/Get/Post.... nutzen kannst. Es wird aus dem Proto Contract eine OpenAPI definition erstellt, und auch eine SwaggerUI erzeugt. Das dies nicht für alle API Befehle geht, zumindest nicht in den der Server dann proaktiv was sendet ist mir klar.
Mir ist schon klar das der Kunde der Rest bzw. die HTTP API und nicht den GRPC Teil nutzt keinen Rückkanal hat.
Aber das ist okay für mich.
Firewallprobleme sind für mich auch kein Thema, unsere ganze Software läuft sowieso nur intern beim Kunden.
Wenn Du eine Json Schnittstelle durch RPC generierst, dann ist es eine Json RPC API und keine REST API.
Warum? Wenn die API die Rest Anforderungen erfüllt. (Representational State Transfer#Prinzipien)
Denke das hab ich gesucht.
Ich kann mit gRPC eine RPC Kommunikation machen, gleichzeitig aber auch eine Rest API zur verfügung stellen. ("Create RESTful services with gRPC JSON transcoding")
WebHook sind sehr beliebt, einfach zu implementieren; aber sind nicht für sofortiges Real Time-Verhalten gedacht.
Womöglich passt das aber besser zu Deinem eher simple gewünschten Verhalten; WebSockets sind womöglich "zu oversized" für Deinen Fall.
Ja, die Performance der Antwort ist nicht wirklich relevant, nur das wir überhaupt benachrichtigen können.
Vlt. bieten wir dann ja auch beides an.
Was ist den der Unterschied zwischen Callbacks und Webhooks?
Mir geht es halt hauptsächlich darum das wir die API in C# Entwickeln können, und das ganze auch zur Doku verwenden können.
Ja normalerweise sollten wir die API schön planen und dann danach entwickeln, aber so läufts bei uns leider meist nicht.
Ja, da wollte Ich eben wissen, was ist state of the art.
Wir machen eben gerade die HTTP/REST Schnittstelle neu, mit Swagger UI, Automatischer Doku usw... Weil das vorher was wild zusammengestriktes war. Eine Schnittstelle die HTPP oder TCP oder Dateibasiert konnte, nichts gut, schlecht dokumentiert usw...
Mir geht es halt auch um die Dokumentation, mit Swagger bekomme ich eine schöne Doku und Testumgebung für die eine Richtung sofort.
Nochmals,
wir haben ein WMS, welches eine Standard HTTP (ob wir nun genau Rest haben oder nicht) Schnittstelle bietet. Dadurch kann das überlagerte System bei uns Aufträge einstellen, Materialien anlegen usw.
Das reicht auch für viele Kunden.
Viele brauchen aber auch einen Rückkanal wenn z.B. ein Auftrag fertig gestellt wurde.
Wie würdet Ihr diesen bauen?
Ein Kunde z.b. will jetzt das wir nun wiederum seine Rest API aufrufen. Dies passt aber nicht zu unserem Standard Interface da dies bei jedem Kunden anders wäre
Bei anderen haben wir es im Moment so, das sie ständig per Get Pollen ob es neue Antworten gibt. (Das haben wir in unserer Standard HTPP Schnittstelle so drin, gefällt uns aber eben nicht wirklich.
Daher war die Frage wie würde man den Rückkanal implementieren.
Wir brauchen kein LoadBalancing oder ähnlichen Schnickschnack, an der Schnittstelle hängt maximal 1 Client, also High Perfomance Szenarien brauchen wir nicht supporten.
Wichtig ist uns das wir es Standardisiert haben, das wir bei klein Projekten dem Kunden diese Schnittstelle bieten können, aber ohne Anpassungen.
Und wie gibst du in ObjectResult oder CollectionResult den Typ mit?
Also wenn es nicht als Antwort auf ein POST/GET kommt sondern über den Websocket Kanal?
Ich muss ja dann wissen was es für ein Typ ist.
Ich meine wenn ich per GET die Url für eine Antwort auslese, bleibt der Request so lange offen bis entweder eine Antwort kommt oder die Anfrage einen Timeout hat
Wir haben für unser WMS System als Schnittstelle eine Http/Rest Schnittstelle mit Swagger Interface.
Nun ist für mich die Frage, was ist die beste Möglichkeit für einen Rückkanal.
Im Moment wurde es so entwickelt, es gab einen GET Aufruf mit dem man die Antworten abgerufen hat, und diese hat im Header auch noch gesagt ob es weitere gibt.
Und Wenn keine Antwort vorlag, hat der Aufruf so lange blockiert bis eine weitere eingetragen wurde (Long Polling).
Was man auch noch beachten muss, DateTimeOffset braucht 2Byte Speicher mehr als DateTime, da es intern eine DateTime Struktur und ein Int16 für das Offset verwendet.
Vielleicht ist LINQ2DB ein wenig schneller, aber rechtfertigt dieser Vorteil, dass man auf EFCore mit allen seinen Vorteilen und Change Tracking (was man auch abschalten kann) verzichten muss?
Ganz ehrlich: Dieses Framework bildet den Kern der Anwendung, der sollte sitzen und da setze ich lieber auf EFCore, das weit weit umfangreicher getestet ist, weil es ungefähr überall genutzt wird.
Kommt halt darauf was für features man braucht. Und es gibt eben noch andere Frameworks als EF-Core, das wollte ich eben erwähnen.
Auch linq2db ebenso durch tausende Tests abgedeckt.
Ich hatte (es zumindest früher), häufig so das ich viele features in SQL nicht mit EF-Core als linq abbilden konnte. Da war zu der Zeit linq2db weiter (Merge, Window Functions). Weiß nicht wie da der aktuelle Stand mit EF-Core ist, wir haben uns EF-Core dann nicht mehr angeschaut.
Wenn es rein um die Perfomance geht, oder du einfach nur einen ORM Mapper für Typisierte SQL Abfragen brauchst, kann ich auch noch linq2db empfehlen (https://github.com/linq2db/linq2db). Das bietet Typsicheres SQL via Linq wie EFCore auch, aber häufig ein klein bisschen performanter, jedoch auch mit weniger features (wie z.b. kein Change Tracking)
Ein "Tag" braucht bei uns ca. 500 Bytes als Leeres Objekt (durch die Verschiedenen Properties die wir im Tag haben (Min/Max Value, Scripte, ...)). Das ist aber noch ohne Strings wie dem Tag Namen und der Adresse. D.h. wenn ich 1.5mio tags habe, welche im speicher sind, dann habe ich alleine dafür knapp 1GB speicher, da ich aber noch strings wie Name, Adresse und Beschreibung habe, komme ich auf ca 1.5GB.
Das ganze funktioniert ja so auch, ich überlege einfach nur was, und ob ich es reduzieren kann oder soll.
Ein Tag hat bei uns z.b. über 30 Properties wo man ihn konfigurieren kann, wie archivierung, meldung erzeugen, usw...
wir haben eine Applikation, ein Visualisierungssystem für PLCs.
Aus Performance Gründen halten wir immer alle aktuellen werte als Klasse im Speicher. Dadurch das wir Visualiserungen haben mit mehr als 1,5 Mio Variablen, haben wir einen ganz schönen Speicherverbrauch.
da ja jede Variable durch eine Klasseninstanz repräsentiert wird.
Nun bin ich am überlgen das ganze etwas zu reduzieren, dazu ein paar fragen:
- Was braucht ein leeres Feld vom Type "object" einer Klasse im Speicher? Ich würde im Moment mal auf 8 Byte tippen, oder täusche ich mich da?
- Was braucht ein Bool? Würdet Ihr es als sinnvoll erachten mehrere Bool's in eine Flags enum zu verstecken (nur zur speicheroptimierung?)
wir verwenden in unserer Applikation Roslyn um dynamisch Code zu kompilieren...
Nun bekomme ich bei folgender Klasse:
using MCC.Common.ServiceInterfaces.DTO.CacheableQuery;
using MCC.VISU.DL;
using MCC.VISU.Services.VisuServiceInterfaces.DTO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Globalization;
using LinqToDB;
using System.Linq.Dynamic.Core;
namespace CachableQuery
{
public class REPORT_SrmStatictisQuery : ICachableQueryScript
{
public object Run(object[] parameters)
{
DateTime @from = DateTime.Now; // (DateTime)parameters[0];
DateTime to = DateTime.Now; //(DateTime)parameters[1];
using (var db = new VisuDb())
{
var retVal = new List<Data>();
var tagsQry = db.Tags.Where(x => Sql.Like(x.Name, "SRM.%.ServiceData.%"));
var p = Expression.Parameter(typeof(TagDTO));
if (parameters[2] != null)
{
var srmNames = ((string)parameters[2]).Split(',');
Expression e = Expression.Call(Expression.Property(p, "Name"), typeof(string).GetMethods().First(x => x.Name == "StartsWith" && x.GetParameters().Length == 1), Expression.Constant("SRM." + srmNames[0] + ".ServiceData."));
foreach (var nm in srmNames.Skip(1))
{
e = Expression.Or(e, Expression.Call(Expression.Property(p, "Name"), typeof(string).GetMethods().First(x => x.Name == "StartsWith" && x.GetParameters().Length == 1), Expression.Constant("SRM." + nm + ".ServiceData.")));
}
tagsQry = tagsQry.Where(Expression.Lambda<Func<TagDTO, bool>>(e, p));
}
....
return retVal;
}
}
}
}
den Fehler:
Fehler
: "REPORT_SrmStatictisQuery.cs(66,45): error CS1503: Argument 2: cannot convert from 'System.Linq.Expressions.Expression<System.Func<MCC.VISU.Services.VisuServiceInterfaces.DTO.TagDTO, bool>>' to 'System.Func<MCC.VISU.Services.VisuServiceInterfaces.DTO.TagDTO, bool>'"
Problem ist, in Visual STudio kann er die Klasse ohne Probleme übersetzen. Aber irgendwie findet er die Implementierung in der Queryable Klasse nicht wenn ich Roslyn selber starte.
Als wir Roslyn noch aus dem Net-Framework 4.8 aufgerufen haben hat das so funktioniert. Seit wir nun auf netcore 6 sind geht es nicht mehr...
Mein Roslyn Aufruf sieht ungefähr so aus:
var st = SourceText.From(code.ToStream(), Encoding.UTF8, canBeEmbedded: true);
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(st,
new CSharpParseOptions(kind: SourceCodeKind.Regular, languageVersion: LanguageVersion.Latest), path: codefileName);
string assemblyName = Path.GetRandomFileName();
var emitOptions = new EmitOptions(debugInformationFormat: DebugInformationFormat.PortablePdb );
var embeddedTexts = new[] { EmbeddedText.FromSource(syntaxTree.FilePath, syntaxTree.GetText()) };
var references = new List<MetadataReference>()
{
MetadataReference.CreateFromFile(AssemblyLoader.AssemblyLoader.GetAssemblyPath(systemCollectionsAssembly)),
MetadataReference.CreateFromFile(AssemblyLoader.AssemblyLoader.GetAssemblyPath(systemRuntimeAssembly)),
MetadataReference.CreateFromFile(AssemblyLoader.AssemblyLoader.GetAssemblyPath(netstandardAssembly)),
MetadataReference.CreateFromFile(AssemblyLoader.AssemblyLoader.GetAssemblyPath(systemComponentModelAssembly)),
MetadataReference.CreateFromFile(AssemblyLoader.AssemblyLoader.GetAssemblyPath(typeof(object).Assembly)),
....
};
if (neededAssemblys != null)
{
foreach (var neededAssembly in neededAssemblys)
{
references.Add(MetadataReference.CreateFromFile(AssemblyLoader.AssemblyLoader.GetAssemblyPath(neededAssembly)));
}
}
var compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Debug));
using (var ms = new MemoryStream())
using (var msPDB = new MemoryStream())
{
EmitResult result = compilation.Emit(ms, pdbStream: msPDB, options: emitOptions, embeddedTexts: embeddedTexts);
Muss es denn ein Trigger sein oder kannst du dich auch an die Events hängen?
Dann kannst ja einfach sowas
public static IEnumerable<T> FindVisualChilds<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null) yield return (T)Enumerable.Empty<T>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject ithChild = VisualTreeHelper.GetChild(depObj, i);
if (ithChild == null) continue;
if (ithChild is T t) yield return t;
foreach (T childOfChild in FindVisualChilds<T>(ithChild)) yield return childOfChild;
}
}
public class ThreadControl : IDisposable
{
private readonly object _wakeUpLock = new object();
private readonly Action _workerThreadLoop;
private readonly Timer _workerThreadWakeupTimer;
private volatile bool _go;
private volatile bool _stopThread;
private Thread _workerThread;
public WorkerThreadControl(Action workerThreadLoop)
{
_workerThreadLoop = workerThreadLoop;
_workerThreadWakeupTimer = new Timer(state => WakeUpTimer(), null, -1, -1);
}
public void Dispose()
{
lock (_wakeUpLock)
{
_stopThread = true;
Monitor.Pulse(_wakeUpLock);
}
if (_workerThread != null && !_workerThread.Join(5000))
{
_logger.Warn(GetType().Name, "Worker Thread stop did not respond...");
_workerThread.Abort();
_workerThread.Join();
_workerThread = null;
}
}
public void StartInternal()
{
if (_workerThread == null)
{
_workerThread = new Thread(() => _workerThreadLoop());
_workerThread.Start();
}
}
public void Wait(int timeout, out bool stop)
{
lock (_wakeUpLock)
{
while (!(_stopThread || _go))
{
_workerThreadWakeupTimer.Change(timeout, -1); // start
Monitor.Wait(_wakeUpLock); // Lock is released while we’re waiting
_workerThreadWakeupTimer.Change(-1, -1); // stop
}
stop = _stopThread;
_stopThread = false;
_go = false;
}
}
public void WakeUp()
{
Task.Factory.StartNew(() =>
{
lock (_wakeUpLock)
{
_go = true;
Monitor.Pulse(_wakeUpLock);
}
});
}
private void WakeUpTimer()
{
lock (_wakeUpLock)
{
_go = true;
Monitor.Pulse(_wakeUpLock);
}
}
}
Ich denke das geht doch bestimmt einfacher.
Auch Thread.Abort sollte raus, geht in Netcore eh nicht mehr. Und denke wir brauchen auch keinen extra Thread
Nur über den ThreadingTimer, ich kann zwar die Zeit auf 0 setzen, aber was passiert wenn der Timer gerade läuft? Woher weiß ich wann ich die Zeit hochsetzen muss.
bin auf der Suche nach einer speziellen Klasse, bzw. wir haben schon eine wollte aber wissen, vlt. gibt es sowas ja direkt im Framework.
Wir haben eine Action welche in bestimmten Intervallen aufgerufen werden muss. Glkeichzeitig brauche Ich auch einen externen Call, (z.b. WakeUp) der die Action, wenn Sie gerade nicht läuft, sofort startet. Dieser WakeUp kann mehrmals während die Action läuft aufgerufen werden, soll die Action danach aber nur ein mal noch Triggern. Diese soll aber sofort getriggert werden, und nicht erst wenn der timer wieder abgelaufen ist.
Ein Kollege von mit hat das vor Jahren recht kompliziert implementiert, nun wollte Ich Fragen gibts da direkt was oder muss ich das selber bauen. Möchte es auf jeden fall vereinfachen.
wir haben bei uns eine sehr große Solution, mit einer Host exe welche assemblies dynamisch nachladen kann. Damit die dynamischen assemblies alle vor der Host exe gebaut werden, wurden diese in der Solution unter "Projektabhänigkeiten" als abhänigkeit für das Hauptprojekt definiert.
Die Dynamisch geladenen Assemblies werden aber in ein anderes Verzeichnis compiliert als die host exe.
Nun haben wir folgendes Problem, ein paar der subassemblies haben dateien welche im Projekt auf "copy always" stehen. Nun werden diese dateien auch in das Verzeichnis der "host" assembly kopiert. Jemand eine Ahnung wie man das verhindern kann?
Ganz so schlimm ist es nicht, aber das RegexLab-Tool hilft nicht immer weiter, manches muss ich da probieren. Tatsächlich funktioniert es jetzt wie gewünscht bis auf eine kleine Kleinigkeit:
Absatzübergreifend findet er den Text auch jetzt nicht. Das dürfte an dem '\s+' liegen, denn in den Absatzumbrüchen stehen Sachen wie '<div> </div>' oder '<br>', eben keine WhiteSpace-Zeichen. Die könnte ich natürlich auch noch 'rausnehmen, aber das wird aufwendiger als nötig.
Was willst du denn genau machen? Vlt. wäre ja ein HTML (wie AngleSharp) parser besser geeignet