Precisione e precisione decimale in primo luogo in codice EF

Sto sperimentando questo approccio code-first, ma ora sto scoprendo che una proprietà di tipo System.Decimal viene mappata a una colonna sql di tipo decimale (18, 0).

Come imposto la precisione della colonna del database?

La risposta di Dave Van den Eynde non è più aggiornata. Ci sono 2 cambiamenti importanti, da EF 4.1 in poi la class ModelBuilder è ora DbModelBuilder e ora c’è un metodo DecimalPropertyConfiguration.HasPrecision che ha una firma di:

public DecimalPropertyConfiguration HasPrecision( byte precision, byte scale ) 

dove precisione è il numero totale di cifre che il db memorizzerà, indipendentemente da dove cade il punto decimale e la scala è il numero di posizioni decimali che memorizzerà.

Pertanto non è necessario scorrere le proprietà come mostrato, ma è ansible chiamarlo semplicemente da

 public class EFDbContext : DbContext { protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder) { modelBuilder.Entity().Property(object => object.property).HasPrecision(12, 10); base.OnModelCreating(modelBuilder); } } 

Se si desidera impostare la precisione per tutti i decimals in EF6, è ansible sostituire la convenzione DecimalPropertyConvention predefinita utilizzata in DbModelBuilder :

 protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove(); modelBuilder.Conventions.Add(new DecimalPropertyConvention(38, 18)); } 

Il DecimalPropertyConvention predefinito in EF6 associa decimal proprietà decimal(18,2) alle colonne decimal(18,2) .

Se si desidera che le singole proprietà abbiano una precisione specificata, è ansible impostare la precisione per la proprietà dell’ quadro su DbModelBuilder :

 protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity().Property(e => e.Value).HasPrecision(38, 18); } 

Oppure aggiungi un EntityTypeConfiguration<> per l’ quadro che specifica la precisione:

 protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new MyEntityConfiguration()); } internal class MyEntityConfiguration : EntityTypeConfiguration { internal MyEntityConfiguration() { this.Property(e => e.Value).HasPrecision(38, 18); } } 

Mi sono divertito a creare un attributo personalizzato per questo:

 [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] public sealed class DecimalPrecisionAttribute : Attribute { public DecimalPrecisionAttribute(byte precision, byte scale) { Precision = precision; Scale = scale; } public byte Precision { get; set; } public byte Scale { get; set; } } 

usandolo in questo modo

 [DecimalPrecision(20,10)] public Nullable DeliveryPrice { get; set; } 

e la magia accade alla creazione del modello con una certa riflessione

 protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder) { foreach (Type classType in from t in Assembly.GetAssembly(typeof(DecimalPrecisionAttribute)).GetTypes() where t.IsClass && t.Namespace == "YOURMODELNAMESPACE" select t) { foreach (var propAttr in classType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute() != null).Select( p => new { prop = p, attr = p.GetCustomAttribute(true) })) { var entityConfig = modelBuilder.GetType().GetMethod("Entity").MakeGenericMethod(classType).Invoke(modelBuilder, null); ParameterExpression param = ParameterExpression.Parameter(classType, "c"); Expression property = Expression.Property(param, propAttr.prop.Name); LambdaExpression lambdaExpression = Expression.Lambda(property, true, new ParameterExpression[] {param}); DecimalPropertyConfiguration decimalConfig; if (propAttr.prop.PropertyType.IsGenericType && propAttr.prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { MethodInfo methodInfo = entityConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[7]; decimalConfig = methodInfo.Invoke(entityConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration; } else { MethodInfo methodInfo = entityConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[6]; decimalConfig = methodInfo.Invoke(entityConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration; } decimalConfig.HasPrecision(propAttr.attr.Precision, propAttr.attr.Scale); } } } 

la prima parte è ottenere tutte le classi nel modello (il mio attributo personalizzato è definito in quell’assembly, quindi l’ho usato per ottenere l’assembly con il modello)

il secondo foreach ottiene tutte le proprietà in quella class con l’attributo personalizzato e l’attributo stesso in modo da poter ottenere i dati di precisione e scalabilità

dopo devo chiamare

 modelBuilder.Entity().Property(c=> c.PROPERTY_NAME).HasPrecision(PRECISION,SCALE); 

quindi chiamo il modelloBuilder.Entity () per riflessione e lo memorizzo nella variabile entityConfig, quindi costruisco l’espressione lambda “c => c.PROPERTY_NAME”

Dopodiché, se il decimale è annullabile, chiamo il

 Property(Expression> propertyExpression) 

metodo (lo chiamo dalla posizione nell’array, non è l’ideale lo so, ogni aiuto sarà molto apprezzato)

e se non è annullabile, chiamo il

 Property(Expression> propertyExpression) 

metodo.

Avendo il DecimalPropertyConfiguration, chiamo il metodo HasPrecision.

Apparentemente, puoi sovrascrivere il metodo DbContext.OnModelCreating () e configurare la precisione in questo modo:

 protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder) { modelBuilder.Entity().Property(product => product.Price).Precision = 10; modelBuilder.Entity().Property(product => product.Price).Scale = 2; } 

Ma questo è un codice piuttosto noioso quando devi farlo con tutte le tue proprietà relative al prezzo, così mi è venuto in mente questo:

  protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder) { var properties = new[] { modelBuilder.Entity().Property(product => product.Price), modelBuilder.Entity().Property(order => order.OrderTotal), modelBuilder.Entity().Property(detail => detail.Total), modelBuilder.Entity 

È buona norma chiamare il metodo di base quando si esegue l’override di un metodo, anche se l’implementazione di base non fa nulla.

Aggiornamento: questo articolo è stato anche molto utile.

Usando DecimalPrecisonAttribute da KinSlayerUY, in EF6 puoi creare una convenzione che gestirà le singole proprietà che hanno l’attributo (al contrario di impostare DecimalPropertyConvention come in questa risposta che interesserà tutte le proprietà decimal).

 [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] public sealed class DecimalPrecisionAttribute : Attribute { public DecimalPrecisionAttribute(byte precision, byte scale) { Precision = precision; Scale = scale; } public byte Precision { get; set; } public byte Scale { get; set; } } public class DecimalPrecisionAttributeConvention : PrimitivePropertyAttributeConfigurationConvention { public override void Apply(ConventionPrimitivePropertyConfiguration configuration, DecimalPrecisionAttribute attribute) { if (attribute.Precision < 1 || attribute.Precision > 38) { throw new InvalidOperationException("Precision must be between 1 and 38."); } if (attribute.Scale > attribute.Precision) { throw new InvalidOperationException("Scale must be between 0 and the Precision value."); } configuration.HasPrecision(attribute.Precision, attribute.Scale); } } 

Quindi nel tuo DbContext :

 protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention()); } 

Entity Framework Ver 6 (Alpha, rc1) ha qualcosa chiamato Convenzioni personalizzate . Per impostare la precisione decimale:

 protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Properties().Configure(config => config.HasPrecision(18, 4)); } 

Riferimento:

questa linea di codice potrebbe essere un modo più semplice per realizzare lo stesso:

  public class ProductConfiguration : EntityTypeConfiguration { public ProductConfiguration() { this.Property(m => m.Price).HasPrecision(10, 2); } } 

In EF6

 modelBuilder.Properties() .Where(x => x.GetCustomAttributes(false).OfType().Any()) .Configure(c => { var attr = (DecimalPrecisionAttribute)c.ClrPropertyInfo.GetCustomAttributes(typeof (DecimalPrecisionAttribute), true).FirstOrDefault(); c.HasPrecision(attr.Precision, attr.Scale); }); 

Puoi sempre dire a EF di farlo con le convenzioni nella class Context nella funzione OnModelCreating come segue:

 protected override void OnModelCreating(DbModelBuilder modelBuilder) { // < ... other configurations ...> // modelBuilder.Conventions.Remove(); // modelBuilder.Conventions.Remove(); // modelBuilder.Conventions.Remove(); // Configure Decimal to always have a precision of 18 and a scale of 4 modelBuilder.Conventions.Remove(); modelBuilder.Conventions.Add(new DecimalPropertyConvention(18, 4)); base.OnModelCreating(modelBuilder); } 

Questo vale solo per Code First EF fyi e si applica a tutti i tipi decimal mappati al db.

utilizzando

 System.ComponentModel.DataAnnotations; 

Puoi semplicemente inserire quell’attributo nel tuo modello:

 [DataType("decimal(18,5)")] 

È ansible trovare ulteriori informazioni su MSDN – facet di Entity Data Model. http://msdn.microsoft.com/en-us/library/ee382834.aspx Completamente consigliato.

 [Column(TypeName = "decimal(18,2)")] 

questo funzionerà prima le migrazioni del codice come descritto qui .

L’attributo personalizzato di KinSlayerUY ha funzionato bene per me ma ho riscontrato problemi con ComplexTypes. Stavano mappando come entity framework nel codice dell’attributo, quindi non potevano quindi essere mappate come ComplexType.

Ho quindi esteso il codice per consentire ciò:

 public static void OnModelCreating(DbModelBuilder modelBuilder) { foreach (Type classType in from t in Assembly.GetAssembly(typeof(DecimalPrecisionAttribute)).GetTypes() where t.IsClass && t.Namespace == "FA.f1rstval.Data" select t) { foreach (var propAttr in classType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute() != null).Select( p => new { prop = p, attr = p.GetCustomAttribute(true) })) { ParameterExpression param = ParameterExpression.Parameter(classType, "c"); Expression property = Expression.Property(param, propAttr.prop.Name); LambdaExpression lambdaExpression = Expression.Lambda(property, true, new ParameterExpression[] { param }); DecimalPropertyConfiguration decimalConfig; int MethodNum; if (propAttr.prop.PropertyType.IsGenericType && propAttr.prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) { MethodNum = 7; } else { MethodNum = 6; } //check if complextype if (classType.GetCustomAttribute() != null) { var complexConfig = modelBuilder.GetType().GetMethod("ComplexType").MakeGenericMethod(classType).Invoke(modelBuilder, null); MethodInfo methodInfo = complexConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[MethodNum]; decimalConfig = methodInfo.Invoke(complexConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration; } else { var entityConfig = modelBuilder.GetType().GetMethod("Entity").MakeGenericMethod(classType).Invoke(modelBuilder, null); MethodInfo methodInfo = entityConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[MethodNum]; decimalConfig = methodInfo.Invoke(entityConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration; } decimalConfig.HasPrecision(propAttr.attr.Precision, propAttr.attr.Scale); } } } 

@ Mark007, ho modificato i criteri di selezione del tipo per utilizzare le proprietà DbSet <> del DbContext. Penso che questo sia più sicuro perché ci sono momenti in cui ci sono classi nello spazio dei nomi dato che non dovrebbero far parte della definizione del modello o sono ma non sono entità. Oppure le tue entità potrebbero risiedere in spazi dei nomi separati o in assiemi separati e essere riuniti in una volta Context.

Inoltre, anche se improbabile, non penso che sia ansible fare affidamento sull’ordinamento delle definizioni dei metodi, quindi è meglio estrarli con l’elenco dei parametri. (.GetTypeMethods () è un metodo di estensione che ho creato per lavorare con il nuovo paradigma TypeInfo e può appiattire le gerarchie di classi quando si cercano i metodi).

Si noti che OnModelCreating delega a questo metodo:

  private void OnModelCreatingSetDecimalPrecisionFromAttribute(DbModelBuilder modelBuilder) { foreach (var iSetProp in this.GetType().GetTypeProperties(true)) { if (iSetProp.PropertyType.IsGenericType && (iSetProp.PropertyType.GetGenericTypeDefinition() == typeof(IDbSet<>) || iSetProp.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))) { var entityType = iSetProp.PropertyType.GetGenericArguments()[0]; foreach (var propAttr in entityType .GetProperties(BindingFlags.Public | BindingFlags.Instance) .Select(p => new { prop = p, attr = p.GetCustomAttribute(true) }) .Where(propAttr => propAttr.attr != null)) { var entityTypeConfigMethod = modelBuilder.GetType().GetTypeInfo().DeclaredMethods.First(m => m.Name == "Entity"); var entityTypeConfig = entityTypeConfigMethod.MakeGenericMethod(entityType).Invoke(modelBuilder, null); var param = ParameterExpression.Parameter(entityType, "c"); var lambdaExpression = Expression.Lambda(Expression.Property(param, propAttr.prop.Name), true, new ParameterExpression[] { param }); var propertyConfigMethod = entityTypeConfig.GetType() .GetTypeMethods(true, false) .First(m => { if (m.Name != "Property") return false; var methodParams = m.GetParameters(); return methodParams.Length == 1 && methodParams[0].ParameterType == lambdaExpression.GetType(); } ); var decimalConfig = propertyConfigMethod.Invoke(entityTypeConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration; decimalConfig.HasPrecision(propAttr.attr.Precision, propAttr.attr.Scale); } } } } public static IEnumerable GetTypeMethods(this Type typeToQuery, bool flattenHierarchy, bool? staticMembers) { var typeInfo = typeToQuery.GetTypeInfo(); foreach (var iField in typeInfo.DeclaredMethods.Where(fi => staticMembers == null || fi.IsStatic == staticMembers)) yield return iField; //this bit is just for StaticFields so we pass flag to flattenHierarchy and for the purpose of recursion, restrictStatic = false if (flattenHierarchy == true) { var baseType = typeInfo.BaseType; if ((baseType != null) && (baseType != typeof(object))) { foreach (var iField in baseType.GetTypeMethods(true, staticMembers)) yield return iField; } } }