J'essaie de comprendre quelle colonne, ou colonnes, déclenche l'erreur ci-dessous. Quelque chose a changé dans les données entrantes, alimentées par un service tiers et cela provoque maintenant des échecs lorsque j'essaie de les enregistrer dans SQL.
Microsoft.EntityFrameworkCore.DbUpdateException: une erreur s'est produite lors de la mise à jour des entrées. Voir l'exception interne pour plus de détails. ---> System.Data.SqlClient.SqlException: erreur de dépassement arithmétique lors de la conversion du numérique en type de données numérique. La déclaration est terminée.
Un flux très simple:
La structure des données est définie comme:
[JsonObject(MemberSerialization.OptIn)]
public class RatDbAttributes
{
[JsonProperty]
[StringLength(50)]
public string block_chain { get; set; } // varchar(50)
[JsonProperty]
[StringLength(50)]
public string block_reduction { get; set; } // varchar(50)
[JsonProperty]
[StringLength(50)]
public string block_reward { get; set; } // varchar(50)
[JsonProperty]
public double block_time { get; set; } // decimal(28,6)
[JsonProperty]
[StringLength(100)]
public string consensus_method { get; set; } // varchar(100)
[JsonProperty]
public decimal decimals { get; set; } // decimal(28,6)
[JsonProperty]
[StringLength(50)]
public string difficulty_retarget { get; set; } // varchar(50)
[JsonProperty]
[StringLength(200)]
public string genesis_address { get; set; } // varchar(200)
[JsonProperty]
[StringLength(100)]
public string hash_algorithm { get; set; } // varchar(100)
[JsonProperty]
[StringLength(50)]
public string mineable { get; set; } // varchar(50)
[JsonProperty]
public long p2p_port { get; set; } // bigint
[JsonProperty]
public long rpc_port { get; set; } // bigint
[JsonProperty]
[StringLength(200)]
public string token_role { get; set; } // varchar(200)
[JsonProperty]
public decimal @float { get; set; } // decimal(28,6)
[JsonProperty]
public decimal minted { get; set; } // decimal(28,6)
[JsonProperty]
public decimal total_supply { get; set; } // decimal(28,6)
[JsonProperty]
public decimal max_supply { get; set; } // decimal(28,6)
[JsonProperty]
[StringLength(133)]
public string wallet { get; set; } // varchar(133)
[JsonProperty]
[NotMapped]
public double genesis_timestamp { get; set; } // see below
[JsonIgnore]
public DateTime Genesis_TimeStamp { get { return genesis_timestamp.ToDateTime(); } set { genesis_timestamp = value.ToEpoch(); } }
// Foregin Key Relationship (1-to-1) and Primary Key
[JsonIgnore]
public long TokenMasterId { get; set; }
[JsonIgnore]
[ForeignKey("TokenMasterId")]
public RatDbTokenMaster TokenMaster { get; set; } //foreign key to Parent
}
J'ai genesis_timestamp
le genesis_timestamp
et ce n'est pas le problème (convertir le double en datetime).
Exemple de JSON défaillant entrant:
{"block_chain":""
,"block_reduction":""
,"block_reward":"0"
,"block_time":0.0
,"consensus_method":""
,"decimals":0.0
,"difficulty_retarget":""
,"genesis_address":""
,"hash_algorithm":""
,"mineable":"False"
,"p2p_port":0
,"rpc_port":0
,"token_role":""
,"float":0.0
,"minted":0.0
,"total_supply":0.0
,"max_supply":0.0
,"wallet":""
,"genesis_timestamp":0.0
}
J'ai créé une sauvegarde itérative de basculement en cas d'échec d'un lot. Depuis que j'ai rendu ma connexion à la base de données "globale" pour cette classe, j'ai rencontré par inadvertance un problème. J'ajouterais le jeu d'enregistrements et j'essaierais de l'enregistrer (erreur apparaît), puis j'essayais d'itérer des lots de plus en plus petits pour découvrir le ou les enregistrements incriminés. Le problème était que je n'ai pas supprimé le jeu d'enregistrements incriminé avant l'itération. Par conséquent, chaque itération portait la condition d'erreur dans la connexion DB!
internal void IterateSave<TModel>(List<TModel> items) where TModel : class
{
using (LogContext.PushProperty("Data: Class", nameof(RatBaseCommandHandler)))
using (LogContext.PushProperty("Data: Method", nameof(IterateSave)))
using (LogContext.PushProperty("Data: Model", nameof(items)))
{
int max = items.Count;
int skip = 0;
int take = (max > 20) ? (max / 5) : 1;
int lastTake = take;
List<TModel> subItems = new List<TModel>();
while (skip <= max)
{
try
{
subItems = items.Skip(skip).Take(take).ToList();
Log.Verbose("Working {Max} | {Take} | {Skip}", max, take, skip);
skip += take;
_db.Set<TModel>().AddRange(subItems);
_db.SaveChanges();
}
catch (Exception ex)
{
/***** Was not removing the faulty record/recordset! *****/
_db.Set<TModel>().RemoveRange(subItems);
/***** Was not removing the faulty record/recordset! *****/
if (take == 1 && skip < max)
{
Log.Error(ex, "Error saving specific record in this data batch! {GuiltyRecord}", JsonConvert.SerializeObject(subItems));
if (skip >= max - 1)
{
depth--;
return;
}
}
else if (take > 1)
{
Log.Warning("Something is wrong saving this data batch! {RecordCount} Running a smaller batch to isolate.", take);
IterateSave(subItems);
}
}
}
}
}
Avec ces 2 lignes ajoutées (section commentée dans catch), l'erreur est littéralement apparue!
Erreur lors de l'enregistrement d'un enregistrement spécifique dans ce lot de données! "[{\" block_chain \ ": \" Ethereum \ ", \" block_reduction \ ": \" \ ", \" block_reward \ ": \" 0 \ ", \" block_time \ ": 0.0, \" consensus_method \ ": \" \ ", \" décimales \ ": 18.0, \" difficulté_retarget \ ": \" \ ", \" genesis_address \ ": \" 0x3520ba6a529b2504a28eebda47d255db73966694 \ ", \" hash_algorithm \ ": \" \ ", \" "mineable \": \ "False \", \ "p2p_port \": 0, \ "rpc_port \": 0, \ "token_role \": \ "\", \ "float \": 0.0, \ "minted \ ": 60000000000000000000000000.0, \" total_supply \ ": 60000000000000000000000000.0, \" max_supply \ ": 60000000000000000000000000.0, \" wallet \ ": \" \ ", \" genesis_timestamp \ ": 0.0}]" Microsoft.EntityFrameworkCore.DbUpdateException: une erreur s'est produite: lors de la mise à jour des entrées. Voir l'exception interne pour plus de détails. ---> System.Data.SqlClient.SqlException: erreur de dépassement arithmétique lors de la conversion du numérique en type de données numérique. La déclaration est terminée.
Alors que C # peut gérer 60000000000000000000000000.0 dans un type de données DECIMAL, notre SQL est défini sur DECIMAL (28,6). En raison de la précision à 6 chiffres qui ne laisse de l'espace que pour une valeur de 10 ^ 22.
(Il semble que SQL puisse désormais gérer DECIMAL (38,6). Il est temps de jouer avec les définitions de colonne sans perdre les données de production.)