Ich glaube ich habe mein Problem nicht deutlich genug erläutert.
Als Beispiel habe ich ein Objekt der ViewModel-Liste gebunden, und zeig in einer TextBox ein Feld davon. Wenn ich zwischenzeitlich die Objekte neu lade und danach die TextBox änder, konnte ich in der Liste die Änderung nicht mehr nachverfolgen, da ja die Referenz verloren ging (durch Clear).
Mit dem Code geht es jetzt. Also die "Referenz" ist scheinbar die selbe, sie wurde nur ersetzt.
habe es mir jetzt doch nochmal etwas umgeschrieben, das funktioniert soweit jetzt. Falls jemand es brauchen sollte:
// Modelle von Datenbank beziehen:
var orders = _SqlRepo.GetOrders();
orders.Wait();
// Modelle die in ViewModel Liste bereits referenziert sind:
var ordersToReplace = _Orders.Where(
c =>
orders.Result.FirstOrDefault(old => old.OrderID == c.OrderID) != null)
.ToList();
// vorhandene ViewModels ersetzen:
ordersToReplace.Select(
c =>
c = new OrderViewModel(
orders.Result.Where(c => c.OrderID == c.OrderID).First()
)
);
// ViewModels, für die es kein Modell gibt ==> löschen
var ordersToDelete = _Orders.Where(
vm =>
orders.Result.FirstOrDefault(model => model.OrderID == vm.OrderID) == null)
.ToList();
foreach(var orderToDelte in ordersToDelete)
_Orders.Remove(orderToDelte);
// Models, für die es kein ViewModel gibt ==> hinzufügen:
var ordersToAdd = orders.Result.Where(
c => _Orders.FirstOrDefault(vm => vm.OrderID == c.OrderID) == null)
.ToList();
// ViewModel für Modell erstellen & hinzufügen:
foreach (var order in ordersToAdd)
_Orders.Add(new OrderViewModel(order));
Ich arbeite in meiner Datenbank mit "Order"-Objekten, diese mappe ich mir in "OrderViewModels" für WPF,...
Speichere ich z.B. ein Objekt ab, lese ich danach wieder die Datenbank aus.
// "Order"-Objekte aus der Datenbank
var orders = await _SqlRepo.GetOrders();
// Liste mit ViewModels (werden in Window abgerufen)
_Orders.Clear();
foreach(var order in orders)
{
var vm = new OrderViewModel(order);
_Orders.Add(vm);
}
Durch das Clearen gehen ja jedesmal die Referenzen verloren, wenn z.B. ein Fenster mit Details das jeweilige OrderViewModel noch verwendet. Das funktioniert zwar, ich kann Änderungen dann aber nur noch an einer Stelle tracken.
Gibt es ein fertiges Pattern mit dem ich erst die bestehenden ersetze (z.B. anhand OrderID identifiziere), dann nicht mehr vorhandene (gelöschte) entferne und neu hinzugefügte, neu erstelle?
Das kann man natürlich alles von Hand machen, ich schätze das Problem ist aber sicher schon öfter vorgekommen, ich weiß nur nicht nach was ich suchen soll.
Zudem schreibt man in Unity ja auch eher Scripts statt vollwertige Programme.
Bei mir ist es so das ich mit 24 gerne noch eine Ausbildung als Anwendungsentwickler starten möchte.
Da ich leider nur einen Hauptschulabschluss (und diesen auch nicht gut) möchte ich mein Wissen über C# ausbauen. ...
Allerdings ist es ja auch so das Enterprise C# Entwicklung eine völlig andere ist als Unity Entwicklung.
Also ich will dir nicht den Mut nehmen, aber bei größeren Firmen, die z.B. Enterprise Software im B2B entwickeln, zählt ehr größere Erfahrung oder coole Projekte zum vorzeigen (GitHub Profil,...) wenn kein Studium vorhanden ist. Bei einem kleinen Betrieb der für interne Zwecke entwickelt mag das keine Rolle spielen. Aus eigener Erfahrung kann ich aber sagen dass so eine Ausbildung nicht gerade ein Qualitätsmerkmal ist, Erfahrung ist da viel wichtiger.
Bzgl. dem Lernen, ich rate dir wirklich ein umfangreicheres Buch an die Hand zu nehmen, in dem möglichst viel behandelt wird. Wenn du das mit Tutorials oder YouTube-Videos lernen willst verschwendest du eigentlich nur Zeit, da du irgendwann merkst dass Grundlagen fehlen. Ich habe zumindest noch keine online-Videoreihe gesehen die den Umfang und Qualität von z.B. "C# 8 mit Visual Studio 2019" hat.
Der dort erwähnte ViewManager entspricht dabei dem WindowsService, den MrSparkle nannte.
Danke für den Tipp!
public interface IWindowManager
{
void ShowOrderWindow(OrderViewModel order);
...
}
So habe ich es jetzt gemacht, das MainViewModel initalisiert den WindowManager, und nutzt den Manager dann um die Fenster aufzurufen.
Dann ist der Manager ein Ausbruch aus dem Pattern. Aber ich denke noch ein recht übersichtlicher
Bisher habe ich es so gemacht:
im MainWindowViewModel habe ich einen Command der ein neues Fenster öffnet, dem das zugehörige ViewModel zuweist und den DbService mitgibt.
Jetzt sollte die UI Schicht ja eigentlich von den ViewModels komplett getrennt sein. Also das MainWindoViewModel das zweite Fenster gar nicht kennt.
Wie mache ich das am besten, dass ich das in XAML definiere? Also einen Command der ein neues Fenster öffnet, ViewModel mitgibt. Brauche ich für sowas dann doch Codebehind?
Meiner Meinung hängt der langfristige Erfolg ehr von Azure und dessen Entwicklung ab.
Was ich noch erstaunlich häufig in Unternehmen sehe: Programme die mit Visual Basic 6 oder Visual C++ 6,... entwickelt wurden und noch regelmäßig gepflegt werden! Also ich denke C# bzw. .NET bleibt uns auch noch eine Zeit lang
Du hast eine Liste mit Mails, da weißt du die Anzahl.
Anstatt die zusammen in die Methode zu geben, kannst du sie ja einzeln abarbeiten und den Status aktualisieren.
Funktionieren deine Methoden wirklich?! Du gibst ja nicht mal einen Task zurück. Da macht der Aufruf dann halt auch keinen Sinn.
Nur mal interessehalber, was wird das für ein Projekt? Ich verstehe nicht ganz wieso man Nutzer Adminrechte gibt aber sowas dann so stark kontrollieren will. Hört sich ehr nach so einem Schul-PC Schutzprogramm oder Mitarbeiterüberwachung an :D
Prinzipiell funktioniert das schon, wenn ein Mitarbeiter ausgewählt ist.
Wenn jedoch kein Mitarbeiter ausgewählt ist, wird der Button nicht ausgegraut, was nicht sonderlich schön ist.
Woher soll der Button das auch wissen? Dafür kannst du es ja an die IsEnabled Eigenschaft binden.
nur ganz kurz eine Frage, ich weiß das wurde schon mehrfach durchgekaut, ich bin mir aber nicht sicher was "sauberer" ist:
Aus dem MainWindowViewModel rufe ich per Command ein zweites Fenster auf (OrderDetails), welches ein OrderDetailsViewModel als Context hat. Dem übergebe ich im Konstruktor das gewählte Order Objekt und das DB-Repository.
public OrderDetailsViewModel(Order order, IDBRepo dbRepo)
Beim Speichern-Command im OrderDetailsViewModel will ich eine Aktualisierung im MainWindowViewModel anstoßen.
Mir fallen jetzt zwei Möglichkeiten ein:
public OrderDetailsViewModel(Order order, IDBRepo dbRepo, MainWindowViewModel parent)
==> ich übergebe die Referenz des Parents im Konstruktor und rufe dann eine Methode auf (z.B. refreshOrders,...)
Oder ich abonniere im MainWindowViewModel ein Event des OrderDetailsViewModels (z.B. DetailsSaved,...)
Was ist besser? Bzw. habt ihr einen besseren Vorschlag?
Danke!
Ich habe es mittlerweile bei vier DAX Unternehmen gesehen, dass auch dort das Thema auf dem Tisch liegt. Da wird es von den Mitarbeitern zwar auch nur belächelt, langfristig aber übernommen. Aber nur um mögliche Konsequenzen zu vermeiden und das Thema schnell wieder vergessen zu können.
Problem es gibt keine einfache Beschreibung was der Unterschied zwischen Parallel und Asynchron ist.
Ohne solche Sätze wie:
Parallele Verarbeitung vs. Order Preservation sind gegensätzliche Anforderungen.
Das Einhalten einer Order ist extrem kostenintensiv; daher gibt es weder in PLINQ noch in der TPL entsprechende Funktionalitäten
Asynchron --> man macht weiter ohne zu warten
Parallel --> es läuft zur selben Zeit ab
Bei deinem Fall macht das parallele arbeiten absolut keinen Sinn, das könntest du z.B. machen wenn du Arrays mit Pixeln hast und die mittels Interpolation skalieren willst. --> hohe Rechenpower notwendig, kann nebeneinander laufen, nicht abhängig,...
Bei einer Datenbankabfrage macht es halt gar keinen Sinn, das wird auch nicht schneller. Und du lastest damit deine CPU-Kerne auch nicht aus Alles wie oben schon mehrfach erwähnt.
Bei dir bietet es sich an asynchron zu machen, dass die GUI oder was auch immer nicht hängen bleibt. Was jetzt genau dein Problem damit ist, verstehe ich aber immer noch nicht.
Da wird aber auch nicht die richtige Reihenfolge garantiert, und es lohnt sich glaub an erst ab sehr hohen Anzahlen.
Warum wartest du nicht einfach mit await? Warum muss das parallel ablaufen? Vorallem bei den selben Datenbankzugriffen. Oder am Besten mehrere auf einmal wie T-Virus sagt.
Ist zwar auch nicht optimal, aber die einzige Lösung die mir einfällt OnTheFly die Zugangsdaten der Datenbankserver ändern zu können ohne die Application an sich zu updaten.
Ich wollte nur sagen, dass ich das Abrufen von Credentials über eine HTTP Schnittstelle schöner finde als das zusätzliche Verbinden auf eine Datenbank. Dabei ist man auch nicht nur auf Microsoft angewiesen (Open-Source gibt es ja auch)...
Mir kommt das irgendwie sehr bekannt vor, wenn z.B. irgend ein Leiter nur die Technologie nutzen will mit der er auch vertraut ist obwohl es bessere Alternativen gibt (auch bei DAX Unternehmen)
Wie gesagt, ist nur meine eigene Meinung. Ich denke die Diskussion hilft dir auch nicht weiter, darum sollten wir es jetzt dabei belassen.
Also da finde ich es schon schöner, wenn man so einen Zugriff hat, anstelle dass jeder Client auf irgendeine Datenbank connected. Naja das muss dann halt jeder selber wissen
aber wir handeln dass jetzt über eine zentrale Datenbank auf die immer zugegriffen wird, welche dann den ensprechenden Connectionstring zurückgibt. Ist zwar auch nicht optimal, aber die einzige Lösung die mir einfällt OnTheFly die Zugangsdaten der Datenbankserver ändern zu können ohne die Application an sich zu updaten.
Wäre es nicht viel eleganter die Connectionstrings z.B. über eine kleine HTTP-API zu beziehen anstatt nochmal in einer Datenbank?
Also die API gibt sie als JSON zurück. Geht glaube ich in Azure sogar ohne eine Zeile Code.
Sind diese Addxxx-Methoden wirklich asynchron? Warum verwendest du dann nicht await (mit Task.ConfigureAwait(false))?
Nein, ich wollte es im Interface aber als Task definieren, dass ich später evtl. mal eine asynchrone REST-, RPC-Schnittstelle oder ähnliches dazwischen klemmen könnte.
Der Aufruf soll später auch mit await stattfinden, das war nur ein Testaufruf in einer Konsole.
Zitat von Abt
Im Endeffekt ist das weder ein Service, noch eine Db - sondern im Prinzip nichts anderes als (eine sehr einfache Variante) des Repository Pattern.
Bzgl. Klasse: Die Methode verwendest du ja bereits in irgendeiner Klasse. Wenn du es an mehreren Stellen brauchst, solltest du es natürlich auslagern. Die Frage ist dann ehr statisch oder nicht. Das kommt auf den Input, Funktionen,... an.
vielleicht kennt ja der ein oder andere LiteDB.
Da ein SQL-Server für dieses Projekt zu aufwendig wäre, würde ich gerne eine lokale Datenbank einsetzen. Vielleicht kennt ja jemand auch eine bessere Alternative.
Ich nehme als Beispiel wieder eine kleine Auftragsverwaltung - Auftrag - Kunde - Artikel.
public class Article
{
public int ArticleID { get; set; }
public string Title { get; set; }
public Order Order { get; set; }
...
public class Customer
{
public int CustomerID { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public List<Order> Orders { get; set; } = new List<Order>();
...
public class Order
{
public int OrderID { get; set; }
public string Title { get; set; }
public Customer Customer { get; set; }
public List<Article> Articles { get; set; } = new List<Article>();
...
Für die Datenabfrage habe ich mir einen kleine Abstraktionsschicht geschrieben:
public class LocalDb : IAvService
{
private LiteDB.LiteDatabase _db;
private ILiteCollection<Order> GetOrderCollection
{
get { return _db.GetCollection<Order>("orders"); }
}
private ILiteCollection<Customer> GetCustomerCollection
{
get { return _db.GetCollection<Customer>("customers"); }
}
private ILiteCollection<Article> GetArticleCollection
{
get { return _db.GetCollection<Article>("articles"); }
}
public LocalDb(string dbFile)
{
_db = new LiteDatabase(dbFile);
BsonMapper.Global.Entity<Order>()
.DbRef(x => x.Customer, "customers");
BsonMapper.Global.Entity<Order>()
.DbRef(x => x.Articles, "articles");
BsonMapper.Global.Entity<Customer>()
.DbRef(x => x.Orders, "orders");
BsonMapper.Global.Entity<Article>()
.DbRef(x => x.Order, "orders");
}
public Task<Customer> AddCustomer(Customer customer)
{
return Task<Customer>.Run(() =>
{
var col = GetCustomerCollection;
var id = col.Insert(customer);
return col.FindById(id);
});
}
...
Erstelle ich einen neuen Auftrag mit Artikeln und Kunde löse ich das bisher so.
Da die Objekte (Artikel, Customer....) noch keine ID haben, muss ich sie erst separat in die Datenbank geben.
IAvService av = new LocalDb("test.db");
Order order = new Order
{
Title = "Test"
};
Article article1 = new Article
{
Title = "Arbeitszeit",
Price = 60,
Tax = 0.16
};
var dbArticle = av.AddArticle(article1).GetAwaiter().GetResult();
Console.WriteLine($"Artikel ID: {dbArticle.ArticleID}");
order.Articles.Add(dbArticle);
order.Customer = av.AddCustomer(new Customer()
{
Firstname = "Max",
Lastname = "Mustermann"
}).GetAwaiter().GetResult();
av.AddOrder(order);
Hier wollte ich fragen ob das soweit alles passt, da sich das ja durch mein komplettes Programm ziehen wird.
Was mich noch etwas stört ist bzw. was etwas unschön aussieht, dass ich alles erst Adden muss,
also es nicht direkt hinzufügen kann, wie bei EF: (Oder geht das doch irgendwie damit?)
Order order = new Order{
Customer = new Customer{...}
}
Ein Kollege von mir hat ein Tool in Delphi geschrieben und das ist gerade einmal 4 MB groß. Als Vergleich habe ich nur mal die GUI nachgebaut und komme allein da schon auf 120 MB? Und ich habe auf meinem System kein Delphi installiert, daher muss da ja auch alles mitkommen. Kann mir das bitte jemand erklären?
Absolute .NET Grundlagen, kannst ja auch Programme in Assembler schreiben, dann sind sie noch kleiner.