Ok, ihr ratet mir also eher von Microservices ab, und empfehlt eher den Schritt zu gehen, das aktuelle Projekt zu optimieren, so dass weitere Features und Services ohne weiteres implementiert werden können?
Klar, es gibt nicht DIE Architektur, die auf alles passt, hätte nur gedacht, dass Microservices "alles etwas einfacher machen", aber es scheint eher das Gegenteil zu sein.
Dann auf jeden Fall vielen Dank für das Feedback!
Servus zusammen,
ich arbeite bei einem mittelstätigen Unternehmen, welches im Tankstellenbereicht tätig ist.
Eins unserer Projekte hat die Aufgabe bei verschiedenen Tankautomaten-Herstellern Konfigurationen der Tankautomaten abzurufen, oder diese zu setzen. Jeder Hersteller nutzt da logischweise einen anderen Kommunikationsansatz. Die einen eine Rest API, die anderen eine TCP Verbindung.
Da sich dieeses Projekt langsam zu einem alles könnenden Monolithen entwickelt und in nächster Zeit nun weitere Funktionen hinzu kommen werden, würde ich alles etwas umstrukturieren wollen und den Ansatz der Microservice-Architektur umsetzen. Beispielsweise ein Service kümmert sich um die ankommenden Konfigurationen und ein weiterer Service sendet neue Konfigurationen. Nennen wir sie mal "Bekomme-Konfiguration-Service" und "Sende-Konfiguration-Service". Beide Services würden aber weiterhin auf dem gleichen Server laufen.
Mein Stolperstein ist hier allerdings die TCP Geschichte. Unser aktueller Service dient als TCP Server, die Hersteller bauen also eine Verbindung zu uns auf und nur durch diese Verbindung kann ich neue Konfigurationen senden.
Wenn ich nun aber den TCP Server abkoppel und in den "Bekomme-Konfiguration-Service" stecke, wie komme ich dann im "Sende-Konfiguration-Service" an die Verbindung? Oder macht es da eher Sinn den TCP Server als dritten Service anzulegen. Die ankommenden Konfigurationen würden dann an den "Bekomme-Konfigurationen-Service" weiter geleitet werden und neue Konfigurationen vom "Sende-Konfigurationen_Service" werden dann zum TCP Service geleitet und dort gesendet. Aber, wie schon gefragt, wie komme ich von aussen an die richtige Verbindung?
Oder macht hier ein ganz anderer Ansatz mehr Sinn?
Servus zusammen,
wie ich ja mittlerweile schon gelernt habe, werden Credentials aus Sicherheitsgründen in den Umgebungsvariablen hinterlegt und im Code ausgelesen.
Wie verhält es sich aber mit Zertifikaten, wie ist da der "way to go"?
Im Moment installiere ich die Zertifikate händisch im Certificate Store, da ich keine CI/CD Pipeline nutze. Das Auslesen der Zertifikate mache ich dann mittels
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
X509Certificate2 Certi = store.Certificates.First(x => x.FriendlyName == "meinZertifikat");
Danke schonmal!
Kriz
Zitat von Abt
Erster Grundregel: Deine ASP.NET Controller / Actions sind Deine Schnittstelle nach Außen.
Du solltest also eigene Modelle nur für diese Außenwelt bereitstellen: API Modelle. Deine Entitäten oder "Business Models" haben in dieser Welt nichts zu suchen.
Mit API Modelle meinst Du Models, die nur als Rückgabe für die Actions dienen? Also beispielsweise:
public class UpdateResult { public bool Success { get; set; } public DateTime ExecutionTime { get; set; } }
auf die Delete-Action? Klar, ein entsprechender ResponseCode tuts auch, aber nur verständnisshalber...
Code wie
IActionResult result = personService.UpdatePerson(person);
zeigt, dass die Logik falsch aufgebaut ist.
Der obige Code ist "zusammen gezimmerter Beispielcode", und kommt so in meinem Projekt nicht vor. Ja, da häte ich etwas genauer sein können. Methoden wie **personService.UpdatePerson(person)**
geben dann eher einen bool, oder integer zurück. Das würde dann doch eher wieder ins Schema passen, oder?
Zweiter wichtiger Punkt: jeder Request ist isoliert. Du solltest nur die Instanzen zwischen Requests (=Threads) teilen, die dafür konzipiert sind.
Deine Repositories sind das nicht, und daher Deine Logikbausteine ebenfalls nicht. Das muss also alles als Transient registriert werden - statischen Instance Sharing wiePersonRepository.Instance
ist ein No-Go.
In einem anderen Thread(finde ihn gerade nicht) wurde mir nahe gelegt Datenbanken als Singleton anzulegen. Was ist hier denn in dieser Situation anders, dass man es nicht als Singleton machen sollte?
Lass sowas wie "PersonService". Das ist das, was innerhalb von kürzester Zeit in riesen, unflexiblen Code-Klassen endet.
Arbeite mit Use Cases wie eben den Handlern, die Du unten angedeutet hast. Du kannst dafür fertige Frameworks (Wie MediatR) nehmen, die Dir paar Sachen zusätzlich geben, oder alles selbst machen und simpel halten.
Mein Gedankengang war, dass ich so wenig Logik-Code wie möglich in meinen Controllern habe. Also erstelle ich einen PersonService, an den werden die Requests weiter gegeben und dieser ruft dann die entsprechenden Handler auf. Order macht man da (je nach Komplexität) pro Action einen eigenen Handler der dann die Request übergeben bekommt?
Doch, das funktioniert genau so. Nur reicht man es nicht durch, sondern das macht alles das DI Framework automatisch. Du erstellst es halt von Hand, was nicht Sinn der Sache ist. Aber der Flow ist:
- Action nutzt Handler
- Handler nutzt Repository
Mit durchreichen meine ich: Wenn ich im Handler das Repository nutzen möchte, dann injiziere ich es dort im Konstruktor. Wenn ich nun den Handler im Controller nutzen möchte, würde ich im Controller das Repository injizieren und es an den Handler weiter geben. Oder würde der Handler bereits im Controller injiziert werden?
Was in Deinem Code nicht passt ist, dass Deine
UpdatePerson
Action Logik beinhaltet.
Sie prüft welcher Handler ausgeführt wird: das ist ebenfalls Logik.
Ne, das passiert in diesem Beispiel im PersonService, nicht im Controller.
Servus,
ich habe vor einigen Jahren als Quereinsteiger in der Softwareentwicklung angefangen. Dem entsprechend sehe viele meiner älteren Projekte aus, viel gewuchert und kaum sortiert. Nun bin ich beim Refactoring meines ersten Projekts (WebAPI) und mir haben sich (bis jetzt) zwei Fragen ergeben.
Meine Controller sehen beispielsweise so aus:
public class PersonController : Controller
{
private readonly PersonService personService;
public PersonController(IPersonService _personService)
{
personService = _personService;
}
public IActionResult UpdatePerson(Person person)
{
IActionResult result = personService.UpdatePerson(person);
return DeletedResult(result);
}
public IActionResult DeletePerson(int id)
{
bool result = personService.DeletePerson(id);
return result;
}
}
Jeder Controller bekommt per DI einen entsprechenden Service in den Konstruktor. Die Services bekommen ein Repository und de Repository bekommen Credentials, aller per DI. So weit, so klar.
Meine ServiceImplementierung sieht beispielsweise so aus:
public class PersonService : IPersonService
{
private readonly IPersonRepository personRepository;
public PersonService(IPersonRepository _personRepository)
{
personRepository = _personRepository;
}
public bool DeletePerson(int id)
{
int result = personRepository.DeletePerson(id);
return (result == 1);
}
}
Nun möchte ich aber beispielsweise in meiner PersonService.Update(Person)
unterschiedliche Handler/Klassen nutzen, um die Person weiter zu bearbeiten:
public IActionResult UpdatePerson(Person person)
{
IActionResult result;
MaleHandler maleHandler = new MaleHandler();
FemaleHandler femaleHandler = new FemaleHandler();
if (person.Gender == Gender.Male)
{
result = maleHandler.Update(person);
}
else
{
result = femaleHandler.Update(person);
}
return result;
}
Wenn ich nun in meinen Handlern (FemaleHandler
bzw MaleHandler
) auf die Datenbankschicht zugreifen möchte, müsste ich ja hier im Kontruktor das bereits erstellte personRepository übergeben:
MaleHandler maleHandler = new MaleHandler(personRepository);
Das wirkt mir aber irgendwie "nicht richtig", das Repository weiter durch zu reichen, "bis man es braucht".
Dieses funktioniert leider nicht:
public class MaleHandler
{
private readonly IPersonRepository personRepository;
public MaleHandler()
{
personRepository = PersonRepository.Instance;
}
public void Update(Person person)
{
...
}
}
Da ich im PersonRepository-Konstruktor die Credentials injiziere.
Gibt es da einen anderen Ansatz?
Ich sehe immer wieder, dass die verschiedenen Schichten in verschiedenen Projekten innerhalb einer Solution abgebildet sind. Was genau bringt dieses Vorgehen? Aktuell bilde ich die verschiedenen Schichten per Ordner ab und sehe in dem Mehraufwand der einzelnen Projekte keinen Mehrwert.
Danke schonmal!
Kriz
Wenn ich versuche meinen Avatar zu ändern bekomme ich nach Bildauswahl und Click auf 'Änderung speichern' die Fehlermeldung
Es wurde kein Bild angegeben.
The value '' is invalid.
Das benutzte Bild ist anbei.
Kriz
Klasse, vielen Dank! Das hat mir alles schonmal gut weiter geholfen!
Wir sind eine 2-Mann Firma und ich bin der einzige Entwickler, da war es bist jetzt noch nicht so nötig CI/CD und unterschiedliche Umgebungen "professionell" aufzuziehen, aber am Ende des Tages ist es einfach nur sinnvoll es von Anfang an auch schon richtig anzugehen.
Ich werde mal versuchen mit GitHub Actions einen CI/CD Workflow zu erstellen, das scheint einer der wenigen Anbieter zu sein, die nicht sooo teuer sind.
Vielen Dank!
Kriz
Zitat von Abt
Das Problem aber an für sich existiert gar nicht, weil Du auf einem Server individuelle Einstellungen über die Umgebungsvariablen umsetzen solltest (zB Connection Strings) und nicht über die config.
ConnectionsString in den Umgebungsvariablen hinterlegen? Hätte gedacht die sind eher für systemrelevante Sachen relevant... und nicht für ConnectionStrings einer Anwendung.
Die Lösung war übrigens der Publishdatei das Enviroment noch mit zu übergeben, das fehlte. Nun arbeitet es wie gewünscht. Danke nochmal an Th69 und T-Virus, manchmal sind es die offensichtlichen Sachen.
@Abt: Wenn nun aber die appconfig nicht für ConnectionStrings und Api-Pfade genutzt werden "soll", muss ich nun jedes mal beim Veröffentlichen einer Anwendung ein Batch laufen lassen, damit die nötigen Strings und Pfade in den Umgebungsvariablen eingetragen werden? Das wirkt mir etwas umständlich?!
Servus zusammen,
ich versuche ein ASP.Net Core Projekt so umzustellen, dass DB-Connectionstrings und API-Pfade aus der appconfig.json gelesen . Abhähig von der gewählten Konfiguration soll die appconfig.Development.json oder appconfig.Testserver.json (und noch ein paar weitere Konfiguratonen) gewählt werden.
Ich habe in der launchsettings.json mehrere Profile angelegt, die verschiedene Konfiguratonen "bedienen", beispielsweise:
"Development": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": "true",
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
Wenn ich nun lokal das Projekt mit "Development" starte, wird die korrekte appconfig gewählt. Soweit alles schön.
Nun veröffentliche ich mein Projekt per WebDeploy auf meinen IIS-Server, in den Einstellungen habe ich unter Configuration "Development" angegeben. Es wird allerdings immer die appsettings.json genutzt.
Mein HostBuilder:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddEnvironmentVariables();
var env = hostingContext.HostingEnvironment;
config.AddJsonFile($"appsettings.json", optional: false, reloadOnChange: true);
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
}
);
Wenn ich env.EnviromentName
auslese, ist es immer Production, klar dass dann die "falsche" appsettings.json genutzt wird.
Ich habe auf dem Server die Umgebungsvariablen ASPNETCORE_ENVIROMENT
und DOTNET_ENVIROMENT
(Hinweis auf GitHub) auf "Development" gesetzt, das hat allerdings nichts geändert.
Wo ist mein Fehler?
Danke schonmal!
Kriz
Dies ist in diesem Fall auch so, alle Kommunikation geht über einen API Server und dann erst weiter zur DB.
Mir ging es darum, dass ich verschiedene APIs für verschiedene UseCases habe (Releease, Debug, Debug mit Live Daten, usw). Anhand der ausgewählen Build Konfiguration nutze ich dann die entsprechende API. Meine Frage war nun ob es in diesem Fall einen einfachreren/besseren Weg gibt.
Erstmal danke für die Antwort!
Ich hätte dazu schreiben sollen, dass es um eine Xamarin App geht, dort gibt es den Luxus von appsettings.json in Verbindung mit Enviroments leider nicht. Zwar gibt es ein NuGet (Mobile.BuildTools), aber das wirft mehr Fehler, als dass es hilft und wird auch nicht mehr aktualisiert.
Im Darkmode bleibt der Texteditor weiß, der zu schreibende Text wird allerdings auch weiß, so dass man sein geschriebenes nicht mehr sieht.
Brwoser: Chrome Version 114.0.5735.135
Ich wollte gerade meinen Avatar ändern, nach dem Auswählen wird mir die Vorschau auch angezeigt. Aber ein Klick auf "Änderung speichern" resultiert in einem Fehler "Es wurde kein Bild angegeben. The value '' is invalid."
Der Titel klingt etwas wirr, hier die Erklärung:
Im Laufe der Entwicklung, greift Eure App ja auf verschiedene APIs oder SQL Connections zu, oder nutzt verschiedene Credentials, abhängig von Eurem aktuellen Development-Status. Also Release, oder Debug, oder Debug mit Live Daten, oder Release mit Debug Daten, usw...
Wie handhabt Ihr diese Situation?
In meinen Projekten nutze ich dafür die BuildConfiguration in VS. Ich habe x Build Konfigurationen und entsprechend der gerade ausgewählten, werden dann im DatabaseController, oder im APIController, oder wo auch immer die entsprechend benötigten Credentials/Strings geladen:
#if DEBUG_LIVE
private static string APIString = Data.Const.Release_API;
#endif
#if DEBUG_TEST
private static string APIString = Data.Const.Debug_API;
#endif
Gibt es da einen besseren/schnelleren/schöneren Weg?
Danke im Vorraus!
Kriz
Also erstmal danke für die ganzen Hinweise, ich habe meinen TCP Server nun refaktorisiert und Eure Ratschläge umgesetzt.
Zwar hat es sich herausgestellt, dass das Problem an der Gegenseite lag, aber hab ja trotzdem was gelernt.
Servus,
mir ist gerade eine Frage gekommen:
Handling A:
private void Test(object x)
{
if (x != null)
{
//do something
}
}
Handling B:
private void Test(object x)
{
if (x == null)
{
return;
}
//Do something
}
Gibt es hier ein BestPractice? Hat eine der Versionen Performance-Vorteile?
Danke schonmal!
Zitat von T-Virus
Ich sehe in deinem Code unmengen an Problemen.
Die Klasse solltest du als Singleton umsetzen.
Warum?
buffer ist bei dir ein Klassen Feld, entsprechend rufen alle Clients gleichzeitig den Buffer ab oder verwenden diesen.
Was dann drin steht ist purer Zufall.
Hier solltest du dringend den Code überarbeiten.
Danke für den Hinweis, habe den Code entsprechend geändert.
Dein Code ist auch mit try/catch vollgestopft, die viel mehr machen als sie sollten.
Ebenfalls ist das try/catch in GetShellForStationID unnötig, wenn dein Code sauber ist.
Da hast Du recht, Da wollte ich auf Nummer sicher gehen für die Fälle an die ich nicht gedacht habe. Was spricht denn generell gegen "zu viel" try/catch?
Die Begin/End Methoden des TCPListener solltest du durch die Async Methoden ersetzen.
Das ist noch ein uraltes Konzept für asynchrone Programmierung aus .NET 2.0 Zeiten, ist also sehr sehr altes Zeug.
Welche Methoden meinst Du genau? Die listenener.BeginAcceptTcpClient()? Dann werd ich mich da mal rein lesen, danke für den Hinweis!
Zitat von Abt
Dann zeig mal Deinen vollständigen Original-Code. Ist Käse mit Try-and-Error-Snippets zu hantieren.
Das ist meine TCPServer Klasse:
public class TCPServer
{
////Logger Logger = new Logger();
static X509Certificate serverCertificate = null;
static TcpListener listener;
private StringBuilder sb = new StringBuilder();
private byte[] buffer = new byte[2048];
public List<SslShell> endpoints = new List<SslShell>();
public static Action<TaskSiteInformation> onStationInformationRecieved;
public static Action<TaskSiteInformation> onNoShellFound;
public TCPServer()
{
RatioTransactionHandler.onTCPMessageSend += Send;
DataController.onPricingSend += Send;
TaskServiceHandler.onStationInformationRequested += Send;
}
public async Task Listen()
{
await Logger.LogTcpConnections("LISTENER STARTED", " LOCAL", null);
serverCertificate = GetServerCert();
listener = new TcpListener(***, ***);
listener.Start(2000);
listener.BeginAcceptTcpClient(Listen_Callback, listener);
}
private async void Listen_Callback(IAsyncResult ar)
{
TcpClient client = new TcpClient();
string endpoint = "unknown";
try
{
listener = (TcpListener)ar.AsyncState;
client = listener.EndAcceptTcpClient(ar);
endpoint = client.Client.RemoteEndPoint.ToString();
SslStream sslStream = new SslStream(client.GetStream(), false);
SslServerAuthenticationOptions options = new SslServerAuthenticationOptions()
{
ClientCertificateRequired = false,
RemoteCertificateValidationCallback = CertificateValidationCallback,
ServerCertificate = serverCertificate
};
try
{
sslStream.AuthenticateAsServer(options);
}
catch (AuthenticationException e)
{
await Logger.LogTCP(" LOCAL", "Exception: " + e.Message);
await Logger.LogTCP(" LOCAL", "Authentication failed - closing the connection.");
client.Close();
client.Dispose();
listener.BeginAcceptTcpClient(Listen_Callback, listener);
return;
}
await Logger.LogTcpConnections("CONNECTION ESTABLISHED", endpoint, null);
SslShell shell = new SslShell()
{
Stream = sslStream,
Endpoint = endpoint
};
shell.Stream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(SSLReadCallback), shell);
}
catch (Exception ex)
{
await Logger.LogTCP(endpoint, ex.Message);
try
{
await Logger.LogTCP(endpoint, ex.StackTrace);
}
catch (Exception)
{
}
client.Close();
await Logger.LogTCP(endpoint, "CONNECTION CLOSED");
listener.BeginAcceptTcpClient(Listen_Callback, listener);
}
listener.BeginAcceptTcpClient(Listen_Callback, listener);
}
private async void SSLReadCallback(IAsyncResult ar)
{
**Interna**
}
public async void Send(UnsolicitedRequest request)
{
try
{
Database db = new Database();
SslShell shell = GetShellForStationID(request.CardAcceptorID).Result;
if (shell == null)
{
onStationInformationRecieved?.Invoke(new TaskSiteInformation() { ID = request.CardAcceptorID});
await Logger.LogTCP("?", "-> " + "ERROR: No Shell found for: " + request.CardAcceptorID);
return;
}
byte[] sendBytes = request.ToByteArray();
await shell.Stream.WriteAsync(sendBytes);
await Logger.LogTCP(shell.Endpoint, "-> " + request.ToXAML());
shell.Stream.Flush();
string target = "";
if (request.RequestType == RatioRequestType.ReservePump)
{
target = request.RequestID;
}
else
{
target = request.ReferenceNumber;
}
await db.LogCommunication("Transactions", "TND", target, request.ToXAML());
}
catch (Exception ex)
{
await Logger.LogTCP("ERROR SENDING", ex.Message + ex.StackTrace);
SslShell shell = await GetShellForStationID(request.CardAcceptorID);
endpoints.Remove(endpoints.First(x => x.Endpoint == shell.Endpoint));
}
}
private async Task<SslShell> GetShellForStationID(string ID)
{
try
{
return endpoints.First(x => x.StationID.Trim() == ID.Trim());
}
catch (Exception)
{
return null;
}
}
}
}
Zitat von Abt
await shell.Stream.WriteAsync(sendBytes).ConfigureAwait(false); shell.Stream.Flush();
Das ist nur ein weiterer Versuch das Problem zu lösen, der ursprüngliche Code war
await shell.Stream.WriteAsync();
Es sind auch nicht immer 30 Sekunden Delay. Mal sind es 12, mal über eine Minute.
Zitat von Th69
Dann solltest du mal mit Wireshark o.ä. schauen, wann die Nachrichten verschickt werden (ob es also an deinem Programm oder am Netzwerk liegt).
Da bin ich schon was länger dran, hier hapert es daran, dass da wahnsinnig viel Traffic von der IP kommt und es schwierig ist den richtigen rauszufiltern. Da alles verschlüsselt ist und ich noch keine funktionierende Anleitung zum Decrypten gefunden habe, finde ich die entsprechenden Messages leider nicht.
Servus zusammen,
folgendes Problem: Ich sende per SSL-Stream Daten an einen Endpunkt. Soweit auch alles ok, wenn nun meine App (ASP Core Web-API)aber eine Zeit lang läuft, dann dauert die Übertragung der Daten teils über 30 Sekunden, zumindest macht es den Eindruck. Nach dem Neustart der API ist die Übertragung wieder wie erwartet. Ich weiß leider nicht wo genau ich bei der Fehlersuche am besten ansetze.
Hiermit sende ich, das Loggen findet also erst statt, wenn der Sendevorgang erfolgreich war.
if (shell.Stream.WriteAsync(sendBytes).IsCompletedSuccessfully)
{
await Logger.LogTCP(shell.Endpoint, "-> " + request.ToXAML());
shell.Stream.Flush();
}
shell.Stream ist vom Typ System.Net.Security.SslStream
In meinen Logs steht als Sendezeit beispielsweise 12:00:00, beim Empfänger kommt die Nachricht allerdings erst 12:00:30 an.
An unterschiedlich eingestellter Zeit kann es nicht liegen, da ja wie gesagt nach einem Neustart meiner API die Übertragung innerhalb weniger Millisekunden ankommt.
Irgendwo bei mir scheint also ein Puffer oder was auch immer voll zu laufen, aber wo suche ich da am besten?
Zitat von Th69
Warum überhaupt 3 Eigenschaften, wenn du doch schon den
OverlayType
hast?
Ich habe in einem MAUI Projekt drei verschiedene Controls von denen nur eins angezeigt werden darf. Das steuer ich über die IsVisible-Property. So brauche ich dann für jedes Conntrol eine eine IsVisible-Property zum Binden.
Servus,
Ich habe drei Properties:
private bool _ShowEllipse;
public bool ShowEllipse
{
get { return _ShowEllipse; }
set { SetProperty(ref _ShowEllipse, value); }
}
private bool _ShowRectangle;
public bool ShowRectangle
{
get { return _ShowRectangle; }
set { SetProperty(ref _ShowRectangle, value); }
}
private bool _ShowPath;
public bool ShowPath
{
get { return _ShowPath; }
set { SetProperty(ref _ShowPath ,value); }
}
Es darf immer nur eine dieser Properties true sein. Nun habe ich zwei Möglichkeiten:
switch (overlay.Type)
{
case OverlayType.Ellipse:
ShowEllipse = true;
ShowRectangle = false;
ShowPath = false;
break;
case OverlayType.Rectangle:
ShowEllipse = false;
ShowRectangle = true;
ShowPath = false;
break;
case OverlayType.Path:
ShowEllipse = false;
ShowRectangle = false;
ShowPath = true;
break;
default:
break;
}
-> Würde das hier gern schöner formatieren, aber es wird leider nicht richtig übernommen...
Beide Ansätze wirken nicht wirklich elegant, gibt es noch andere Möglichkeiten das Problem zu lösen?
Danke schonmal!
Kriz
Erstmmal danke für die Antwort!
Generell finde ich den Ansatz von MVVM schon sehr praktisch, egal ob Desktop, Mobile, oder Web. Es ist einfach strukturierter als beispielsweise MVU.
Mir ging es hier speziell um das Übermitteln von Parametern von einem ViewModel zum Nächsten, ohne die View nutzen zu müssen, da das ja irgendwie dem Prinzip von MVVM widerspricht.
Klar gibt es x Nuget-Packages dafür, oder das interne MessageCenter bei Xamarin, aber das "fühlt sich nicht richtig an".
Cheers,
mein aktueller Workflow in meinen Projekten (Xamarin/Maui/WPF):
Viewmodel:
public DetailViewModel(int itemID)
{
//ViewModel Zeug
}
View CodeBehind:
public partial class DetailPage : ContentPage
{
public DetailPage(int itemID)
{
BindingContext = new DetailViewModel(itemID);
InitializeComponent();
}
}
Aufrufen der View:
App.Current.MainPage = new DetailPage(123);
So hab ich es "damals" gelernt und so funktioniert es auch, aber mittlerweile frage ich mich, ob es der richtige Weg ist bzw ob es nicht einen besseren Weg gibt?!
Ich störe mich daran, dass ich den Parameter 123 erst an die View geben muss, damit diese ihn an das ViewModel weiter gibt.
Gibt es da bessere Ansätze?
Danke schonmal!
Moin,
ich entwerfe gerade eine Lebenssimulation als mobile App/Spiel (ja, davon gibts schon ohne Ende, aber darum geht es nicht).
Als Datenspeicher werd ich eine ganz einfache SQL Datenbank nutzen um die verschiedensten Dinge abspeichern zu können.
Ab und an sollen Zufallsereignisse ausgelöst werden. Diese können mal nur eine Antwort-Option enthalten, oder mal (maximal) drei. Soweit kein Problem. Nur stocke ich beim folgenden Gedankengang und weiß nicht recht wie ich das am geschicktesten umsetze:
Die Antwortoptionen können verschiedene "Situationen" auslösen. Also beispielsweise die Veränderung der Beziehung zu einer Person, eine Gehaltserhöhung, das geliebte Haustier muss eingeschläfert werden, oder es passiert einfach nichts.
Beispiel: Du gehst mit Deinem geliebten Haustier "Waldi" spazieren, durch einen dummen Zufall wird Waldi von einem SUV angefahren. Der Fahrer steigt aus seinem Auto und siehe da, es ist Dein Chef.
Reaktionen:
Eskalieren - Das Verhältnis zu Deinem Chef verschlechtert sich um x%, Waldi überlebt mit einer Schramme
Ruhig bleiben - Du bekommst eine Gehaltserhöhung weil Dein Chef ein schlechtes Gewissen hat, Waldi stirbt aber
Ja blödes Beispiel, aber es zweigt was ich meine.
Wie kann ich das am besten in der Datenbank umsetzen?
Danke schonmal!
Hallo gfoidl,
erstmal danke für die Antwort!
Das heisst das vorhandene Zertifikat (mit CERTIFICATE und PRIVATE KEY Abschnitt) kann ich als Client-Zertifikat nehmen, aber ist als Server-Zertifikat ungeeignet?!
Servus,
ich habe eine *.crt Datei mit Certificate und Private Key Abschnitt.
Dieses lese ich ein mit
X509Certificate2 serverCertificate = X509Certificate2.CreateFromCertFile("Data\\certificate.crt");
Nun möchte ich einen verschlüsselten TCPListener starten, bekomme aber bei
SslStream sslStream = new SslStream(client.GetStream(), false);
sslStream.AuthenticateAsServer(serverCertificate, clientCertificateRequired: true, checkCertificateRevocation: true);
den Fehler > Fehlermeldung:
The server mode SSL must use a certificate with the associated private key
Woran haperts, was hab ich vergessen?
Danke im Vorraus!
Kriz
Moin,
nehmen wir mal an, Ihr habt eine DesktopApp entwickelt, testet Ihr sie auf verschiedenen Systemen (Win 10, WinServer, mit/ohne SP, usw) und wenn ja, wie bewerkstelligt ihr das? Für jede mögliche Konfiguration eine eigene VM erstellen wird ja recht aufwendig werden. Oder gibt es da third party tools für?
Schonmal Danke für den Input!
Kriz
Th69, danke Dir, aber den Beitrag hatte ich schon mehrfach durchforstet.
Jein, mir fehlt die Möglichkeit dynamisch an die Propertys zu binden.
Ich kann bei einer DataGridTextColumn dynamisch an jeden Index meiner ObservableCollection binden, bei einer DataGridTemplateColumn leider nicht. Da kann ich nur die gesamte ObservableCollection dran binden, das bringt mir aber nichts.
for (int i = 0; i < MaxColumn; i++)
{
DataGridTemplateColumn column = new DataGridTemplateColumn() { Header = vm.StartDate.AddDays(i).ToShortDateString(), CellTemplate = template };
grdRota.Columns.Add(column);
}
Mit diesem Code ist der DataContext jeder Spalte die gesamte ObservableCollection, ich möchte aber pro Spalte den jeweiligen Index haben.
Ich habe mittlerweile auch DataTables ausprobiert, das fühlt sich aber sehr wie Flickschusterei an.
Folgendes Problem:
Ich möchte in meinem DataGrid ein DataTemplate für meine Cells benutzen. Nur kann man daran (anscheinend) nicht dynamisch binden. Welche Möglichkeiten gibt es?
ItemSource des DataGrid ist eine ObservableCollection<EmployeeRota>:
class EmployeeRota : BaseViewModel
{
private Employee _employee;
public Employee employee
{
get { return _employee; }
set { _employee = value;
OnPropertyChanged(nameof(employee));
}
}
private ObservableCollection<Shift> _ListOfShifts;
public ObservableCollection<Shift> ListOfShifts
{
get { return _ListOfShifts; }
set { _ListOfShifts = value;
OnPropertyChanged(nameof(ListOfShifts));
}
}
}
Das erstellen der Spalten erfolgt dynamisch, da ListOfShifts 1 - x Elemente enthalten kann:
//Spalte für den Namen erstellen
DataGridTextColumn NameColumn = new DataGridTextColumn() { Header = "Name" };
Binding NamebindingExpression = new Binding($"employee.Name") { Mode = BindingMode.OneWay };
NameColumn.Binding = NamebindingExpression;
grdRota.Columns.Add(NameColumn);
//Spalten für die Schichten abhängig der Anzahl der Einträge erstellen
DataTemplate template = (DataTemplate)grdRota.FindResource("template");
int MaxColumn = vm.ListOfEmployeeRotas.Max(x => x.ListOfShifts.Count);
for (int i = 0; i < MaxColumn; i++)
{
DataGridTemplateColumn column = new DataGridTemplateColumn() { Header = vm.StartDate.AddDays(i).ToShortDateString(), CellTemplate = template };
grdRota.Columns.Add(column);
}
Das Template ist recht einfach:
<DataTemplate x:Key="template">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" Margin="10"/>
</StackPanel>
</DataTemplate>
Würde ich nun eine DataGridTextColumn statt der DataGridTemplateColumn nutzen, könnte ich mit
Binding bindingExpression = new Binding($"ListOfShifts[{i}]") { Mode = BindingMode.OneWay };
column.Binding = bindingExpression;
das gewünschte Ergebnis erhalten, aber DataGridTemplateColumn enthält keine Eigenschaft Binding. Und ich würde gern DataGridTemplateColumn nutzen.
Also zusammen gefasst:
Jede Zelle/Spalte muss dynamisch an den entsprechenden Index einer ObservableCollection gebunden werden.
Mit DataGridTextColumn kein Problem, es muss aber DataGridTemplateColumn benutzt werden.
Ein Bonus wäre auch, wenn ich die Spalten im XAML erstellen könnte und dafür nicht extra im Code-Behind rumwurschteln müsste.
Danke schonmal im Vorraus!
Kriz
Hallo zusammen,
folgendes Problem:
Ich muss an eine SOAP Schnittstelle Daten senden, bekomme aber als Antwort immer nur
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<S:Fault xmlns:ns4="http://www.w3.org/2003/05/soap-envelope">
<faultcode>S:Server</faultcode>
<faultstring>The configured protocol is not SOAP, which has been requested.</faultstring>
</S:Fault>
</S:Body>
</S:Envelope>
Leider kenne ich mich nicht mit SOAP aus und wenn ich die Fehlermeldung google, bekomme ich gar keine passende Antwort. Ich weiß noch nicht einmal an welcher Stelle ich den Fehler suchen muss, liegt es an meinem Code, oder an der übermittelten Datei?
Mein Code:
XmlDocument Doc = new XmlDocument();
Doc.InnerXml = result.ToString();
HttpWebRequest Req = (HttpWebRequest)WebRequest.Create(PriceURL);
Req.Headers.Add("SOAPAction", "");
Req.ContentType = "text/xml;charset=\"utf-8\"";
Req.Accept = "text/xml";
Req.Method = "POST";
Req.Timeout = 10000;
Req.ClientCertificates.Add(Certi);
using (var streamWriter = new StreamWriter(Req.GetRequestStream()))
{
streamWriter.Write(Doc);
}
//Überprüfen ob API Daten korrekt erhalten hat
using (var response = (HttpWebResponse)Req.GetResponse())
{
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
var json = streamReader.ReadToEnd();
}
}
das Feld "result" vom Typ StringBuilder
Bevor es jetzt wieder böse Kommentare kommen:
Ich hab das Projekt aufs Auge gedrückt bekommen, das wurde vor 13 Jahren mal geschrieben und seit dem war da keiner mehr dran, nun soll ich als Service und WPF portieren. Bis jetzt hatte ich auch noch nie irgendwelche Berührungspunkte mit SOAP und werde es danach auch nicht mehr haben.
Ich kann "The configured protocol is not SOAP, which has been requested." übersetzen, weiß aber weder wo dieses Protokoll konfiguriert wird/wurde, noch von welcher Seite es verlangt wird/wurde.
Schonmal vielen Dank im Vorraus!
Kriz
@Th69, dank Dir! Ich glaub das ist genau das, was ich gesucht habe!
Das wird eine rein lokale Kommunikation. Die WPF Oberfläche wird einfach nur die GUI für den WindowsService sein und auf dem gleichen Rechner laufen.
Der Service ließt DB-Daten aus und senden sie an eine API. Die GUI soll einfach nur den Status des Services zeigen und kleine Einstellen setzen, die in einer INI gespeichert werden. Diese ließt dann der Service aus.
WCF muss nicht zwingend sein, hab den Begriff nur beim Googlen aufgeschnappt und mal reingelesen.
Ok, vielen Dank für die Tips, werde mich da mal einlesen.
Moin,
da ein WindowsService keine eigene UI haben sollte, habe ich mich für ein WPF Projekt als Oberfläche entschieden.
Welche Möglichkeiten habe ich nun mit dem Service zu kommunizieren? Es geht um ganz rudimentäre Kommunikation (bin da, starte neu, Aufgabe läuft).
Eine Möglichkeit ist anscheinend WCF, das wirkt mir aber etwas umständlich, da es anscheinend über http kommuniziert, oder hab ich mich da verlesen?
Welche Möglichkeiten habe ich denn noch?
Danke schonmal!
Kriz
Moin,
folgende Situation:
Ich habe eine ASP Core Webanwendung (MVC mit Razor), dort können Nutzer sich registrieren. Dafür nutze ich das vorgegebene Template für Autorisierung und Authentifizierung in VS2019.
Zusätzlich befindet sich auf dem gleichen Server auch die API für meine App und die Webanwendung.
Nun möchte ich, dass sich die User auch in meiner App anmelden können.
Ein Ansatz wäre nun in der Api einen Endpunkt einzurichten, der die Credentials des User entgegennimmt, und ihn verifiziert, das kommt mir aber irgendwie unsicher und nicht gewollt vor.
Gibt es für sowas bereits eine BuildIn Lösung in ASP Core, oder welchen Ansatz verfolge ich da am besten?
Kriz
Verwendetes Datenbanksystem: SQL
Moin zusammen,
ich hab eine API, die auf eine SQL Datenbank zugreift. Insgesamt sind es grob 300 verschiedenste Abfragen.
Lange Abfragen, die im Code schwer zu lesen sind, habe ich als StoredProcedur angelegt, kürzere Abfragen direkt im Code.
Beim Refactoring ist mir dieses Durcheinander aufgefallen un dich überlege, ob es nicht bessere Lösungen gibt?!
Beispielsweise könnte man ja die Strings auch in eine separate TXT ablegen und abrufen, oder eine statische Klasse nutzen...
Wie handhabt ihr Eure SQL Queries?
Servus, mal wieder nicht ganz C# related:
Ich würde gern in unregelmässigen Abstände Newsletter an die Nutzer meiner mobilen App senden. Bei meinem Website Anbieter (IONOS) ist das Limit bei 200 Mails am Tag. Ich hab eine Zeit lang Mailchip genutzt, dort ist das Limit im kostenlosen Paket aber 1000 Empfänger. Aktuell bin ich bei Newstroll, dort zahle ich 10€ im Monat für 50.000 Mail pM.
Ich sende aber aktuell vllt alle drei-vier Monate mal einen Newsletter, das lohnt sich also nicht wirklich.
Kurzum, wie kann ich einen eigenen Newsletter Service aufsetzen, also wie gehe ich das an und wie sind die Kosten?
Ok, dann werde ich mich von FTP verabschieden und es per HTTP Upload machen.
Danke für die Hinweise!
Falls noch jemand auf die gleiche Idee kommt mit FTP arbeiten zu wollen,hier ein gutes Video, warum man es nicht mehr machen sollte:
Hallo zusammen,
ich entwickle eine Weboberfläche mit der Bilder auf einen FTP Server geladen werden können, diese werden dann in einer App, sowie auf weiteren Websites verwendet.
Technisch habe ich keine Frage, mir geht es um die verschiedenen Sicherheitsaspekte, an die ich denken müsste. Also wie würdet ihr Dateiupload sichern? Mir fällt da spontan erstmal nur ein, anhand der Dateiendung zu checken, ob die Datei überhaupt eine Grafikdatei ist. Das lässt sich natürlich sehr leicht umgehen, indem man eine Exe beispielsweise einfach nur umbenennt.
Also wo sollte ich da ansetzen mit dem Sicherheitskonzept?
Danke schonmal!
Moin,
in einigen Asp.NET Projekten (Sowohl MVC, als auch API) versende ich Mails, am liebsten HTML formatiert.
Gibt es einen schönen Weg HTML formatierte EMails zu erstellen?
Mein aktuelles Vorgehen:
Was mich daran stört, ist der teils ellenlange HTML-string. Man kann ihn einfach schlecht lesen.
Wenn ich eine Kleinigkeit an der Mail ändern will suche ich mich teils blöd, oder muss dann erst wieder umständlich in VS Code editieren, kopieren, usw.
Schön wäre es ja, wenn ich mit Razor arbeiten könnte.
Welche Ansätze verfolgt ihr denn so?
An die Admins, bei Mails vom Forum steht in der Signatur "Made with ASP.Net", bezieht sich das aufs Forum, oder hab ich da bei Asp.Net eine Möglichkeit übersehen Mails zu erstellen?
Danke schonmal!
Ja genau, das klingt sinnvoller, danke fuer den Tip!
Es geht um eine Xamarin App. Dort werden die Artikel nacheinander angezeigt. Also erst der Artikel mit der Sortierung 1, der wird dann bearbeitet, gespeichert und dann kommt der nächste Artikel und so weiter. Also nicht in einer Listview, sondern jeder Artikel ist an eine ContentPage gebunden.
Nun hat der Nutzer die Möglichkeit einen Artikel von Position 8 auf Position 3 zu verschieben. Dafür öffnet sich ein PopUp, "Verschiebe den Artikel "Milch" hinter den Artikle ..." dort kann dann die Artikelnummer oder der Artikelname eingegeben werden und der Nutzer kann den Artikel dann auswählen, der als Ziel genommen werden soll.
Das muss dann auch gespeichert werden (deswegen als Property), damit die Sortierung auch beim nächsten Start der App noch vorhanden ist.
Also es geht nicht darum, wie ich die Liste sortieren kann, sondern ob es für das Umsortieren eine andere Möglichkeit gibt, als eingangs erwähnt.
Servus,
folgender Sachverhalt:
Eine List hat x Elemente mit der Property Sortierung
Beispiel:
ItemA - Sortierung:1
ItemB - Sortierung:2
ItemC - Sortierung:3
ItemD - Sortierung:4
ItemE - Sortierung:5
ItemF - Sortierung:6
Nun soll die Property Sortierung von ItemE zwischen Item B und ItemC eingefügt werden:
ItemA - Sortierung:1
ItemB - Sortierung:2
ItemC - Sortierung:4
ItemD - Sortierung:5
ItemE - Sortierung:3
ItemF - Sortierung:6
Also quasi "Verschiebe ItemE VOR ItemC"
Mein Ansatz wäre nun dass das zu verschiebende Item (ItemE) die Sortierung-Property von ItemC bekommt und bei alle folgenden Items die Sortierung Property um 1 erhöht wird, bis zum ursprünglichen Platz von ItemE.
Das wirkt mir allerdings irgendwie umständlich. Gäbe es da noch einen anderen Ansatz?
Also ich möchte keinen Code, den kann ich mir selber schreiben. Mir geht es darum, den Ansatz den ich habe, zu optimieren.
Es geht darum, dem Nutzer eine Möglichkeit zu geben, Artikel so zu sortieren, wie er es braucht.
Danke schon im Vorraus!
Gut, und jetzt haben wir uns wieder alle lieb.
Die besagten Seiten arbeiten nicht mit der Facebook API, die dort formatierten Texte kann ich auch bei Notepad formatiert einfügen. Es muss wohl also irgendeine Standartformatierung sein, bin aber in dem Thema nicht all zu fit.
Die API Dokumentation von Facebook erläutert leider nur die Formatierung bei Workspace Posts. Zu Page-Posts steht da nichts. Die Markups die bei Workspace funktionieren, haben bei Page Posts keinen Effekt.
Auch HTML Tags oder RTF haben keinen Erfolg.
Es gibt Website Tools, in denen der Text formatiert, kopiert und in Facebook mit Formatierung eingefügt werden kann, also irgendeine Möglichkeit scheint es ja zu geben.
Bis hierhin habe ich alles auch schon mehrfach ausprobiert. Da aber der Facebook Account gesperrt wird, wenn zu oft Zugriffe auf die API erfolgen (Wie auch immer der Algorithmus da anspringt) und mein Konto zwei mal in drei Stunden gesperrt wurde, wollte ich doch mal schauen, ob jemand schon etwas ähnliches versucht und vllt erfolgreich geschafft hat.
Wenn dann dumme Antworten wie "Sollen wir Dir Deine Arbeit abnehmen?" und "Versuch doch mal statt zu warten!" kommen, wundert es nicht, dass Programmierer als sozial inkompetent angesehen werden!