HandleDbUpdateConcurrencyException

pull/1/head
DJh2o2 2023-04-12 15:12:10 +07:00
parent c71607a7b5
commit aec5c8fcae
9 changed files with 112 additions and 105 deletions

@ -183,15 +183,15 @@ public class ProductConfiguration : IEntityTypeConfiguration<Product> {
productEntity.Property(p => p.OptionNumber).HasDefaultValue("");
productEntity.Property(p => p.SapShortDescription).HasDefaultValue("");
productEntity.Property(p => p.SapLongDescription).HasDefaultValue("");
productEntity.Property(p => p.Weight).HasDefaultValue(0);
productEntity.Property(p => p.Weight);
productEntity.Property(p => p.ProductStatus).HasDefaultValue("Active");
productEntity.Property(p => p.IntroductionDate).HasColumnType("TIMESTAMP").HasDefaultValueSql("CURRENT_TIMESTAMP").ValueGeneratedOnAdd();
productEntity.Property(p => p.ListPrice).IsRequired();
productEntity.Property(p => p.ListPrice);
productEntity.Property(p => p.DataCreationDate).HasColumnType("TIMESTAMP").HasDefaultValueSql("CURRENT_TIMESTAMP").ValueGeneratedOnAdd();
productEntity.Property(p => p.DataValidFrom).HasColumnType("DATETIME").HasDefaultValueSql("CURRENT_TIMESTAMP").ValueGeneratedOnAdd();
productEntity.Property(p => p.DataValidUntil).HasColumnType("DATETIME").HasDefaultValueSql("'9999-12-31 23:59:59.000000'").ValueGeneratedOnAdd();
productEntity.Property(p => p.DataVersionNumber).HasDefaultValue(1).IsRequired();
productEntity.Property(p => p.DataVersionComment).HasDefaultValue("");
productEntity.Property(p => p.DataVersionComment).HasDefaultValue("Gremlin_BlazorServer");
productEntity.Property(p => p.DataStatus).IsRequired().HasDefaultValue("Active");
productEntity.Property(p => p.DataModificationDate).HasColumnType("TIMESTAMP").HasDefaultValueSql("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP").ValueGeneratedOnAddOrUpdate().IsConcurrencyToken();
productEntity.Property(p => p.DataModificationByUser).HasColumnType("TINYTEXT").IsRequired().HasDefaultValueSql("ON INSERT CURRENT_USER() ON UPDATE CURRENT_USER()");

@ -19,13 +19,13 @@ public class Product : IMetadata
public string? OptionNumber { get; set; }
public string? SapShortDescription { get; set; }
public string? SapLongDescription { get; set; }
public float Weight { get; set; }
public float? Weight { get; set; }
public string? ProductStatus { get; set; }
public DateTime IntroductionDate { get; set; } = DateTime.Now;
public decimal ListPrice { get; set; }
public bool HasBreakPrices { get; set; }
public int BreakRangeFrom { get; set; }
public int BreakRangeTo { get; set; }
public decimal? ListPrice { get; set; }
public bool? HasBreakPrices { get; set; }
public int? BreakRangeFrom { get; set; }
public int? BreakRangeTo { get; set; }
//metadata:
public DateTime DataCreationDate { get; set; } = DateTime.Now;

@ -53,5 +53,5 @@ public class Quote : IMetadata
public DateTime DataValidFrom { get; set; } = DateTime.Now;
public DateTime DataValidUntil { get; set; } = DateTime.MaxValue;
public string? DataVersionComment { get; set; }
public uint DataVersionNumber { get; set; }
public uint DataVersionNumber { get; set; } // this is the concurrency token
}

@ -71,15 +71,10 @@ public partial class QuoteAdd {
newQuote.QuoteId = lastQuote is not null ? lastQuote.QuoteId + 1 : 1;
newQuote.QuotationNumber = $"DE-{newQuote.SalesRep?.TerritoryId}-{DateTime.Now:My}-{newQuote.QuoteId}";
newQuote.Description = "Gerät";
await TryToSaveQuote(newQuote);
return newQuote;
}
private async Task TryToSaveQuote(Quote newQuote) {
Debug.WriteLine(await GenericController.UpdateAsync(newQuote) > 0 ? "Speichern der Quote erfolgreich." : "Fehler beim Speichern der Quote!");
}
private async Task SelectQuoteOnChanged(FileChangedEventArgs e) {
try {
foreach (IFileEntry? file in e.Files) {
@ -112,13 +107,12 @@ public partial class QuoteAdd {
_ = await GenericController.InsertAsync(newCustomDescription);
}
lineItem.Product = new() {
ProductNumber = lineItem.ProductNumber,
OptionNumber = lineItem.OptionNumber,
CustomDescription = await GenericController.GetAsync<CustomDescription>(cD => cD.ProductNumber.Equals(lineItem.ProductNumber) && cD.OptionNumber.Equals(lineItem.OptionNumber))
};
// lineItem.Product = await GenericController.GetAsync<Product>(p => p.ProductNumber.Equals(lineItem.ProductNumber) && p.OptionNumber.Equals(lineItem.OptionNumber), "CustomDescription");
// quote.LineItems[i].Product.CustomDescription = await genericController.GetAsync<CustomDescription>(cD => cD.CustomDescriptionId.Equals(newCustomDescription.CustomDescriptionId));
lineItem.Product = await GenericController.GetAsync<Product>(p => p.ProductNumber.Equals(lineItem.ProductNumber) && p.OptionNumber.Equals(lineItem.OptionNumber));
if (lineItem.Product is null) return;
lineItem.ProductId = lineItem.Product.ProductId;
lineItem.Product.CustomDescription = await GenericController.GetAsync<CustomDescription>(cD => cD.ProductNumber.Equals(lineItem.ProductNumber) && cD.OptionNumber.Equals(lineItem.OptionNumber));
lineItem.Quote = quote;
lineItem.QuoteId = lineItem.Quote.QuoteId;
}
}
}
@ -177,7 +171,7 @@ public partial class QuoteAdd {
isCreatingTex = true;
StringBuilder? tex = await QuoteHandling.CreateTexAsync(quote);
if (tex == null) return;
if (tex is null) return;
quote.Tex = tex.ToString();
if (quote.Tex == null) return;

@ -49,7 +49,8 @@ public class GenericController {
return await gremlinDb.Set<TResult>().ToListAsync();
}
}
catch (Exception exception) {
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<TResult>(exception);
Console.WriteLine(exception.InnerException);
return null;
}
@ -61,7 +62,8 @@ public class GenericController {
return await gremlinDb.Set<TResult>().Include(include).ToListAsync();
}
}
catch (Exception exception) {
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<TResult>(exception);
Console.WriteLine(exception.InnerException);
return null;
}
@ -73,7 +75,8 @@ public class GenericController {
return await gremlinDb.Set<TResult>().Include(include1).Include(include2).ToListAsync();
}
}
catch (Exception exception) {
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<TResult>(exception);
Console.WriteLine(exception.InnerException);
return null;
}
@ -86,7 +89,8 @@ public class GenericController {
return await Task.Run(() => gremlinDb.Set<TResult>().AsEnumerable().Where(t => search(t)).ToList());
}
}
catch (Exception exception) {
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<TResult>(exception);
Console.WriteLine(exception.InnerException);
return null;
}
@ -99,7 +103,8 @@ public class GenericController {
return await Task.Run(() => gremlinDb.Set<TResult>().Include(include).AsEnumerable().Where(t => search(t)).ToList());
}
}
catch (Exception exception) {
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<TResult>(exception);
Console.WriteLine(exception.InnerException);
return null;
}
@ -138,7 +143,8 @@ public class GenericController {
return await Task.Run(() => gremlinDb.Set<TResult>().AsEnumerable().FirstOrDefault(t => search(t)));
}
}
catch (Exception exception) {
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<TResult>(exception);
Console.WriteLine(exception.InnerException);
return null;
}
@ -164,23 +170,26 @@ public class GenericController {
return await Task.Run(() => gremlinDb.Set<TResult>().Include(include1).AsEnumerable().FirstOrDefault(t => search(t)));
}
}
catch (Exception exception) {
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<TResult>(exception);
Console.WriteLine(exception.InnerException);
return null;
}
}
public async Task<TResult?> GetAsync<TResult>(Predicate<TResult> search, string include1, string include2) where TResult : class, IMetadata {
ArgumentNullException.ThrowIfNull(search);
try {
await using (GremlinDb gremlinDb = new()) {
return await Task.Run(() => gremlinDb.Set<TResult>().Include(include1).Include(include2).AsEnumerable().FirstOrDefault(t => search(t)));
}
}
catch (Exception exception) {
Console.WriteLine(exception.InnerException);
return null;
ArgumentNullException.ThrowIfNull(search);
try {
await using (GremlinDb gremlinDb = new()) {
return await Task.Run(() => gremlinDb.Set<TResult>().Include(include1).Include(include2).AsEnumerable().FirstOrDefault(t => search(t)));
}
}
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<TResult>(exception);
Console.WriteLine(exception.InnerException);
return null;
}
}
public TResult? GetLast<TResult>() where TResult : class, IMetadata {
@ -225,19 +234,15 @@ public class GenericController {
try {
await using (GremlinDb gremlinDb = new()) {
gremlinDb.Set<T>().Add(entity);
//DEBUG
List<EntityEntry> changedEntries = gremlinDb.ChangeTracker.Entries().ToList();
foreach (EntityEntry changedEntry in changedEntries)
Console.WriteLine(changedEntry);
return await gremlinDb.SaveChangesAsync();
}
}
catch (Exception exception) {
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<T>(exception);
Console.WriteLine(exception.InnerException);
return 0;
}
}
public async Task<int> InsertAsync<T>(IEnumerable<T> entities) where T : class, IMetadata {
@ -247,23 +252,25 @@ public class GenericController {
return await gremlinDb.SaveChangesAsync();
}
}
catch (Exception exception) {
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<T>(exception);
Console.WriteLine(exception.InnerException);
return 0;
}
}
public static bool IsExisting<T>(Predicate<T> search) where T : class, IMetadata {
ArgumentNullException.ThrowIfNull(search);
try {
using (GremlinDb gremlinDb = new()) {
return gremlinDb.Set<T>().AsEnumerable().Any(t => search(t));
}
}
catch (Exception exception) {
Console.WriteLine(exception.InnerException);
return false;
ArgumentNullException.ThrowIfNull(search);
try {
using (GremlinDb gremlinDb = new()) {
return gremlinDb.Set<T>().AsEnumerable().Any(t => search(t));
}
}
catch (Exception exception) {
Console.WriteLine(exception.InnerException);
return false;
}
}
public int Update<T>(T entity) where T : class, IMetadata {
@ -280,15 +287,42 @@ public class GenericController {
}
public async Task<int> UpdateAsync<T>(T entity) where T : class, IMetadata {
try {
await using (GremlinDb gremlinDb = new()) {
await using (GremlinDb gremlinDb = new()) {
try {
gremlinDb.Set<T>().Update(entity);
return await gremlinDb.SaveChangesAsync(false);
}
catch (DbUpdateConcurrencyException exception) {
await HandleDbUpdateConcurrencyException<T>(exception);
Console.WriteLine(exception.InnerException);
return 0;
}
}
catch (Exception exception) {
Console.WriteLine(exception.InnerException);
return 0;
}
private async Task HandleDbUpdateConcurrencyException<T>(DbUpdateConcurrencyException exception) where T : class, IMetadata {
// Loop through the entities that caused the concurrency conflict
foreach (EntityEntry? entry in exception.Entries) {
if (entry.Entity is T) {
T? clientValues = (T)entry.Entity;
PropertyValues? databaseEntry = await entry.GetDatabaseValuesAsync();
if (databaseEntry is null)
// The record has been deleted from the database
// Notify the user or handle the error in some other way
throw new("The record has been deleted from the database.");
T? databaseValues = (T)databaseEntry.ToObject();
// Compare the database values with the client values
if (databaseValues.DataVersionNumber != clientValues.DataVersionNumber)
// The name has been changed by another user
// Notify the user or handle the error in some other way
throw new("The record has been modified by another user.");
// The conflict is caused by a property other than the name
// Notify the user or handle the error in some other way
throw new("A concurrency conflict occurred.");
}
// Handle concurrency conflicts for other entity types, if necessary
throw new NotSupportedException($"Concurrency conflicts for entities of type {entry.Entity.GetType().Name} are not supported.");
}
}

@ -35,7 +35,7 @@
\par
\begin{flushright}
\colorbox{AgilentBlau}{\textcolor{white}{\textsc{\Huge{Preisinformation}}}}
\colorbox{AgilentBlau}{\textcolor{white}{\textsc{\Huge{Angebot}}}}
\end{flushright}
\begin{tabular}{p{0.4\hsize}p{0.6\hsize}}
@ -46,8 +46,8 @@
\textbf{Angebotsnummer:}&DE-83PE89-423-223\\
Angebotdatum:&\today\\
Angebotsgültigkeit:&60 Tage\\\textbf{Ansprechpartner:}&Sascha Woitschetzki\\
Telefon: &+49 208 74129134\\
Mobil:&+49 176 22285334\\
Telefon: &+49 176 22285334\\
Mobil:&\\
E-Mail:&\href{mailto:sascha.woitschetzki@non.agilent.com}{sascha.woitschetzki@non.agilent.com}\\
\textbf{Auftragsannahme:}&\href{mailto:salesservices\_germany@agilent.com}{salesservices\_germany@agilent.com}\\
\hline

@ -69,39 +69,39 @@ Sehr geehrter Herr De Jong,\par
nachfolgend erhalten Sie Ihr gewünschtes Angebot über ein(e) Gerät.\\
Es umfasst im Einzelnen:
\begin{itemize}
\item quaternäre Pumpe mit integriertem Entgaser (\#1)
\item Product missing (\#1)
\begin{itemize}
\item Werkzeugsatz (\#2)
\item Aktive Kolbenhinterspülung (\#3)
\item Aktives Einlassventil (\#4)
\item Säule (\#5)
\item Product missing (\#2)
\item Product missing (\#3)
\item Product missing (\#4)
\item Product missing (\#5)
\end{itemize}
\item Vialsampler (\#6)
\item Product missing (\#6)
\begin{itemize}
\item Probenteller für 6x11 2,0 ml Vials (\#7)
\item Ohne Gerätetreiber (\#8)
\item Product missing (\#7)
\item Product missing (\#8)
\end{itemize}
\item Säulenthermostat (\#9)
\item Product missing (\#9)
\begin{itemize}
\item Ventilantrieb (\#10)
\item Product missing (\#10)
\end{itemize}
\item 4-Säulenauswahlventil (\#11)
\item Product missing (\#11)
\begin{itemize}
\item Kapillarkit (\#12)
\item Product missing (\#12)
\end{itemize}
\item Diodenarraydetektor High Sensitivity (\#13)
\item Fluoreszenzspektrendetektor (\#14)
\item OpenLab CDS 2 Instrument Connection (\#15)
\item Product missing (\#13)
\item Product missing (\#14)
\item Product missing (\#15)
\begin{itemize}
\item OpenLab CDS 2 IC für LC (\#16)
\item OpenLab CDS 2 IC für 3D-UV/DAD (\#17)
\item Product missing (\#16)
\item Product missing (\#17)
\end{itemize}
\item 1260 Infinity II HPLC mit Zusatzfunktionen (\#18)
\item Product missing (\#18)
\begin{itemize}
\item Einführung (\#19)
\item Zwei Jahre Gewährleistung (\#20)
\item Product missing (\#19)
\item Product missing (\#20)
\end{itemize}
\item Überprüfung, Deinstallation und Versand (\#21)
\item Product missing (\#21)
\end{itemize}
Für Rückfragen und Änderungswünsche stehe ich Ihnen gerne zur Verfügung.\par
Mit freundlichen Grüßen\\
@ -112,27 +112,6 @@ Mit freundlichen Grüßen\\
\begin{longtable}
{| cp{0.595\textwidth} crr |} \hline
\textbf{\#} & \textbf{Produktbeschreibung} (Produktnummer) & \textbf{Menge} & \textbf{Discount} & \textbf{Preis}\\ \hline \endhead
1 &\textbf{Quaternäre Pumpe} (G7111B)\newline 1260 Infinity II quaternäre Pumpe, Maximaldruck 600 bar. Mit integriertem 4-Kanal-Entgaser, vergünstigter Säule, Verbindungskapillaren, Lösemittelwanne und -flaschen.\newline Listenpreis: \SI{25079}{\sieuro}&1&\SI{45}{\%}&\SI{13793.45}{\sieuro}\\
2 &\textbf{Werkzeugsatz} (G7111B\#001)\newline HPLC System Tool Kit, für Agilent 1260/1290 Infinity II LC.\newline Listenpreis: \SI{400}{\sieuro}&1&\SI{45}{\%}&\SI{220}{\sieuro}\\
3 &\textbf{Aktive Kolbenhinterspülung} (G7111B\#030)\newline Automatische Reinigung und Pflege der Pumpenköpfe, -dichtungen und -ventile für maximale Lebensdauer. Besonders empfohlen bei salzhaltigen Analyten oder Puffern.\newline Listenpreis: \SI{1463}{\sieuro}&1&\SI{45}{\%}&\SI{804.65}{\sieuro}\\
4 &\textbf{Aktives Einlassventil} (G7111B\#032)\newline Aktives Einlassventil (AIV) für einen zuverlässigen Pumpenbetrieb bei der Verwendung von Puffern.\newline Listenpreis: \SI{889}{\sieuro}&1&\SI{45}{\%}&\SI{488.95}{\sieuro}\\
5 &\textbf{Säule} (G7111B\#094)\newline InfinityLab Poroshell 120 EC-C18, 3.0 x 150 mm, 2.7 µm.\newline Listenpreis: \SI{1}{\sieuro}&1&\SI{45}{\%}&\SI{0.55}{\sieuro}\\
6 &\textbf{Vialsampler} (G7129A)\newline 1260 Infinity II automatischer Flüssigprobengeber zur Verwendung bei bis zu 600 bar. Mit integriertem Nadelspülanschluss zur Minimierung der Verschleppung, 100 µl Dosiereinheit und 100 µl Probenschleife. Inklusive Gerätetreiber für ein LC-System (2D-UV).\newline Listenpreis: \SI{19905}{\sieuro}&1&\SI{45}{\%}&\SI{10947.75}{\sieuro}\\
7 &\textbf{Probenteller für 6x11 2,0 ml Vials} (G7129A\#010)\newline \newline Listenpreis: \SI{375}{\sieuro}&1&\SI{45}{\%}&\SI{206.25}{\sieuro}\\
8 &\textbf{Ohne Gerätetreiber} (G7129A\#060)\newline Es wird ein vorhandener Gerätetreiber verwendet oder mit der Software angeboten.\newline Listenpreis: \SI{-1793}{\sieuro}&1&\SI{45}{\%}&\SI{-986.15}{\sieuro}\\
9 &\textbf{Säulenthermostat} (G7116A)\newline 1260 Infinity II Thermostat für bis zu vier 30 cm Säulen, Temperaturbereich: 10° unter Raumtemperatur (min. 4 °C) bis max. 85 °C, inkl. Säulenidentifikations-Kit. Ventilantrieb optional.\newline Listenpreis: \SI{6494}{\sieuro}&1&\SI{45}{\%}&\SI{3571.7}{\sieuro}\\
10 &\textbf{Ventilantrieb} (G7116A\#058)\newline Ventilantrieb für 1260 Infinity II Thermostat G7116A. Zur Verwendung mit QuickChange-Ventilköpfen.\newline Listenpreis: \SI{1541}{\sieuro}&1&\SI{45}{\%}&\SI{847.55}{\sieuro}\\
11 &\textbf{4-Säulenauswahlventil} (G4237A)\newline QuickChange-Ventilkopf zur Auswahl von 4 Säulen (4Pos./10 Anschlüsse), max. 800 bar. Zum schnellen Säulenwechsel oder Schaltung eines Bypasses zur Direktaufgabe auf das Massenspektrometer.\newline Listenpreis: \SI{4808}{\sieuro}&1&\SI{45}{\%}&\SI{2644.4}{\sieuro}\\
12 &\textbf{Kapillarkit} (G4237A\#006)\newline 4-Säulenauswahlventil-Kapillarkit, Inhalt: 0,17 mm ID Kapillaren und vier Quick Connect-Wärmetauscher, zur Verwendung in G7116A.\newline Listenpreis: \SI{3474}{\sieuro}&1&\SI{45}{\%}&\SI{1910.7}{\sieuro}\\
13 &\textbf{Diodenarraydetektor HS} (G7117C)\newline 1260 Infinity II Diodenarray-Detektor high sensitivity für höchste Empfindlichkeit, 120 Hz Datenrate für schnelle Multiwellenlängen- und Spektralanalysen. Messbereich 190 640 nm, RFID-Tags für Zelle und Lampe. Inklusive Standard-Max-Light-Flusszelle (10 mm, V = 1 µl, max. 70 bar).\newline Listenpreis: \SI{24906}{\sieuro}&1&\SI{45}{\%}&\SI{13698.3}{\sieuro}\\
14 &\textbf{Fluoreszenzspektrendetektor} (G7121B)\newline 1260 Infinity II Fluoreszenzdetektor, spektrenfähig.\newline Für Multiwellenlängen-Detektion, Online-Aufnahme von  Anregungs- und Emissionsspektren (200 1200 nm), Datenrate bis 148 Hz. Inkl. Standard-Durchflusszelle (V = 8 µl, max. 20 bar).\newline Listenpreis: \SI{21984}{\sieuro}&1&\SI{45}{\%}&\SI{12091.2}{\sieuro}\\
15 &\textbf{OpenLab CDS 2 Instrument Connection} (M8431AA)\newline \newline Listenpreis: \SI{1364}{\sieuro}&1&\SI{45}{\%}&\SI{750.2}{\sieuro}\\
16 &\textbf{OpenLab CDS 2 IC für LC} (M8431AA\#001)\newline Lizenz und Treiber zur Gerätesteuerung unter OpenLab CDS. Gerätetyp: LC (ohne DAD)\newline Listenpreis: \SI{0}{\sieuro}&1&\SI{45}{\%}&\SI{0}{\sieuro}\\
17 &\textbf{OpenLab CDS 2 IC für 3D-UV/DAD} (M8431AA\#005)\newline Lizenz und Treiber zur Gerätesteuerung unter OpenLab CDS. Gerätetyp: 3D-UV/DAD\newline Listenpreis: \SI{0}{\sieuro}&1&\SI{45}{\%}&\SI{0}{\sieuro}\\
18 &\textbf{1260 Infinity II HPLC mit Zusatzfunktionen} (SYS-LC-1260IIE)\newline \newline Listenpreis: \SI{0}{\sieuro}&1&\SI{20}{\%}&\SI{0}{\sieuro}\\
19 &\textbf{Einführung} (SYS-LC-1260IIE\#2A9)\newline Standardeinweisung für neue Anwender im Rahmen der Installation.\newline Listenpreis: \SI{1193}{\sieuro}&1&\SI{20}{\%}&\SI{954.4}{\sieuro}\\
20 &\textbf{Erweiterte Gewährleistung für akademische Kunden, zwei Jahre komplett} (SYS-LC-1260IIE\#9EC)\newline Verlängerung der Gewährleistung von 12 auf 24 Monate.\newline Listenpreis: \SI{5215}{\sieuro}&1&\SI{40}{\%}&\SI{3129}{\sieuro}\\
21 &\textbf{Überprüfung, Deinstallation und Versand} (R2005A)\newline Überprüfung, Deinstallation und Versand der in Zahlung gegebenen Geräte.\newline Die erfolgreiche Überprüfung der Geräte durch einen Agilent Techniker ist Voraussetzung für die Annahme der Inzahlungnahme durch Agilent.\newline Listenpreis: \SI{0}{\sieuro}&1&\SI{0}{\%}&\SI{0}{\sieuro}\\
\hline
\end{longtable}
\end{center}