Précision décimale et échelle en code EF en premier

je suis en train d'expérimenter avec cette première approche de code, mais je découvre maintenant qu'une propriété du système de type.Décimal est mappé à une colonne sql de type décimal (18, 0).

comment définir la précision de la colonne de la base de données?

187
demandé sur kjbartel 2010-08-17 20:20:16

15 réponses

la réponse de Dave Van den Eynde est désormais périmée. Il y a 2 changements importants, à partir de EF 4.1 la classe ModelBuilder est maintenant DbModelBuilder et il y a maintenant une Décimalpropertyconfiguration.Méthode HasPrecision qui a une signature de:

public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )

où la précision est le nombre total de chiffres que la base de données stockera, indépendamment de l'endroit où le point décimal tombe et la balance est le nombre de décimales qu'elle stockera.

par conséquent, il n'est pas nécessaire d'itérer à travers les propriétés comme indiqué, mais le peut juste être appelé de

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

       base.OnModelCreating(modelBuilder);
   }
}
215
répondu AlexC 2015-02-18 20:46:13

si vous voulez définir la précision pour tous les decimals dans EF6, vous pouvez remplacer la convention par défaut DecimalPropertyConvention utilisée dans le DbModelBuilder :

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

par défaut DecimalPropertyConvention dans EF6 cartes decimal propriétés decimal(18,2) de colonnes.

si vous voulez seulement que les propriétés individuelles aient une précision spécifiée, vous pouvez définir la précision pour la propriété de l'entité sur le DbModelBuilder :

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

Ou, ajouter un EntityTypeConfiguration<> pour l'entité qui spécifie la précision:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new MyEntityConfiguration());
}

internal class MyEntityConfiguration : EntityTypeConfiguration<MyEntity>
{
    internal MyEntityConfiguration()
    {
        this.Property(e => e.Value).HasPrecision(38, 18);
    }
}
67
répondu kjbartel 2015-04-27 05:27:51

j'ai passé un bon moment à créer un attribut personnalisé pour cela:

[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; }

}

en l'utilisant comme ceci

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

et la magie qui se passe à la création du modèle avec une certaine réflexion

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<DecimalPrecisionAttribute>() != null).Select(
                p => new { prop = p, attr = p.GetCustomAttribute<DecimalPrecisionAttribute>(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 première partie est d'obtenir toutes les classes dans le modèle (mon attribut personnalisé est défini dans cette assemblée, j'ai donc utilisé que pour obtenir de l'assemblée avec le modèle)

le deuxième foreach obtient toutes les propriétés dans cette classe avec l'attribut personnalisé, et l'attribut lui-même afin que je puisse obtenir la précision et l'échelle des données

après je dois appeler

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

alors j'appelle le modéliste.Entity () par réflexion et le stocker dans la variable entityConfig puis je construis le " c = >C. PROPERTY_NAME "lambda expression

après cela, si le décimal est nul, j'appelle le

Property(Expression<Func<TStructuralType, decimal?>> propertyExpression) 

(je l'appelle par le position dans le tableau, ce n'est pas idéal, je sais, toute aide sera appréciée)

et s'il n'est pas nul j'appelle le

Property(Expression<Func<TStructuralType, decimal>> propertyExpression)

la méthode.

ayant la Décimalpropertyconfiguration j'appelle la méthode HasPrecision.

67
répondu KinSlayerUY 2015-05-21 12:52:05

en utilisant le DecimalPrecisonAttribute de KinSlayerUY, dans EF6 vous pouvez créer une convention qui traitera les propriétés individuelles qui ont l'attribut (au lieu de définir le DecimalPropertyConvention comme dans cette réponse qui affectera toutes les propriétés décimales).

[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<DecimalPrecisionAttribute>
{
    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);
    }
}

puis dans votre DbContext :

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
}
45
répondu kjbartel 2017-05-23 11:55:07

apparemment, vous pouvez annuler le DbContext.Méthode OnModelCreating () et configurez la précision comme ceci:

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

mais c'est assez fastidieux code quand vous devez le faire avec toutes vos propriétés liées au prix, donc je suis venu avec ceci:

    protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
    {
        var properties = new[]
        {
            modelBuilder.Entity<Product>().Property(product => product.Price),
            modelBuilder.Entity<Order>().Property(order => order.OrderTotal),
            modelBuilder.Entity<OrderDetail>().Property(detail => detail.Total),
            modelBuilder.Entity<Option>().Property(option => option.Price)
        };

        properties.ToList().ForEach(property =>
        {
            property.Precision = 10;
            property.Scale = 2;
        });

        base.OnModelCreating(modelBuilder);
    }

c'est une bonne pratique que vous appeliez la méthode de base quand vous outrepassez une méthode, même si l'implémentation de base ne fait rien.

mise à jour: Cet article a également été très utile.

44
répondu Dave Van den Eynde 2013-03-22 10:22:30

Entity Framework Ver 6 (Alpha, rc1) a quelque chose appelé Custom Conventions . Pour définir la précision décimale:

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

référence:

29
répondu mxasim 2013-09-18 14:49:52

cette ligne de code pourrait être un moyen plus simple d'accomplir la même chose:

 public class ProductConfiguration : EntityTypeConfiguration<Product>
    {
        public ProductConfiguration()
        {
            this.Property(m => m.Price).HasPrecision(10, 2);
        }
    }
12
répondu armadillo.mx 2011-10-13 01:52:27

dans EF6

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

        c.HasPrecision(attr.Precision, attr.Scale);
    });
3
répondu user3332875 2014-02-20 13:45:12

vous pouvez toujours dire à EF de le faire avec les conventions de la classe Context dans la fonction OnModelCreating comme suit:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // <... other configurations ...>
    // modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    // modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
    // modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

    // Configure Decimal to always have a precision of 18 and a scale of 4
    modelBuilder.Conventions.Remove<DecimalPropertyConvention>();
    modelBuilder.Conventions.Add(new DecimalPropertyConvention(18, 4));

    base.OnModelCreating(modelBuilder);
}

ceci ne s'applique qu'au Code First EF fyi et s'applique à tous les types décimaux mappés sur le db.

3
répondu Gecko IT 2016-08-24 11:42:04
[Column(TypeName = "decimal(18,2)")]

cela fonctionnera avec les premières migrations de code comme décrit ici .

2
répondu Elnoor 2018-09-17 14:54:25

vous pouvez trouver plus d'informations sur MSDN - facet du modèle de données D'entité. http://msdn.microsoft.com/en-us/library/ee382834.aspx Recommandé.

1
répondu Jaider 2011-06-09 16:57:39

utilisant

System.ComponentModel.DataAnnotations;

vous pouvez simplement mettre cet attribut dans votre modèle:

[DataType("decimal(18,5)")]
1
répondu VinnyG 2017-03-09 21:22:20

L'attribut personnalisé de KinSlayerUY fonctionnait bien pour moi mais j'avais des problèmes avec les types complexes. Ils étaient mappés en tant qu'entités dans le code d'attribut donc ne pouvait pas être alors mappé en tant que ComplexType.

j'ai donc étendu le code pour tenir compte de ceci:

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<DecimalPrecisionAttribute>() != null).Select(
                   p => new { prop = p, attr = p.GetCustomAttribute<DecimalPrecisionAttribute>(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<ComplexTypeAttribute>() != 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);
            }
        }
    }
0
répondu Mark007 2014-08-11 22:11:54

@Mark007, j'ai changé les critères de sélection de type pour piloter les propriétés DbSet<> du DbContext. Je pense que c'est plus sûr parce qu'il y a des moments où vous avez des classes dans l'espace de noms donné qui ne devraient pas faire partie de la définition du modèle ou qui ne sont pas des entités. Ou vos entités pourraient résider dans des espaces de noms séparés ou des assemblées séparées et être rassemblées dans un contexte unique.

aussi, même si peu probable, Je ne pense pas qu'il est sûr de compter sur l'ordre des définitions de méthode, il est donc préférable de les retirer avec by Parameter list. (.GetTypeMethods () est une méthode d'extension que j'ai construite pour travailler avec le nouveau paradigme TypeInfo et qui peut aplatir les hiérarchies de classe lors de la recherche de méthodes).

notez que OnModelCreating délégués à cette méthode:

    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<DecimalPrecisionAttribute>(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<MethodInfo> 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;
            }
        }
    }
0
répondu Eniola 2014-10-23 13:11:50

Pour Entity Framework Core utiliser [Column(TypeName = "decimal(precision, scale)")]

exemple:

public class Blog
{
    public int BlogId { get; set; }
    [Column(TypeName = "varchar(200)")]
    public string Url { get; set; }
    [Column(TypeName = "decimal(5, 2)")]
    public decimal Rating { get; set; }
}

voir: https://docs.microsoft.com/en-us/ef/core/modeling/relational/data-types

0
répondu sofsntp 2018-09-13 17:32:53