Pregunta

estoy experimentando con este enfoque de código primero, pero estoy averiguar ahora que una propiedad de tipo System.Decimal se asigna a una columna de SQL de tipo decimal (18, 0).

¿Cómo puedo configurar la precisión de la columna de la base de datos?

¿Fue útil?

Solución

La respuesta de Dave Van den Eynde ahora está fuera de fecha. Hay 2 cambios importantes, de EF 4.1 en adelante la clase ModelBuilder es ahora DbModelBuilder y ahora hay un Método DecimalPropertyConfiguration.HasPrecision que tiene una firma de:

public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )

donde la precisión es el número total de dígitos que el PP va a almacenar, independientemente del lugar de la coma decimal se cae y la escala es el número de decimales que se almacenará.

Por lo tanto no hay necesidad para iterar a través de las propiedades como se muestra mas el justo puede ser llamado desde

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

Otros consejos

Si desea establecer la precisión para todos decimals en EF6 podría reemplazar la convención DecimalPropertyConvention predeterminado que se utiliza en el DbModelBuilder:

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

El DecimalPropertyConvention defecto en EF6 mapea propiedades decimal a columnas decimal(18,2).

Si sólo desea propiedades individuales para tener una precisión especificada entonces se puede establecer la precisión de la propiedad de la entidad en la DbModelBuilder:

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

O, añada una EntityTypeConfiguration<> para la entidad que especifica la precisión:

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

Yo tenía un buen tiempo la creación de un atributo personalizado para esto:

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

}

usarla como esto

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

y la magia sucede en la creación del modelo con un poco de reflexión

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 primera parte es conseguir todas las clases en el modelo (mi atributo personalizado se define en esa asamblea así que utilicé eso para conseguir el conjunto con el modelo)

la segunda foreach recibe todas las propiedades en esa clase con el atributo personalizado, y el atributo en sí para que pueda obtener los datos de precisión y escala

Después de eso tengo que llamar

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

por lo que yo llamo el modelBuilder.Entity () por la reflexión y la almacenamos en la variable entityConfig entonces construyo la expresión lambda "c => c.PROPERTY_NAME"

Después de eso, si el decimal es anulable que llamo el

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

método (i llamo a esto por la posición en la matriz, no es ideal sé, cualquier ayuda será muy apreciada)

y si no es anulable que llamo el

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

método.

Tener la DecimalPropertyConfiguration yo llamo el método HasPrecision.

Al parecer, se puede reemplazar el método DbContext.OnModelCreating () y configurar la precisión de esta manera:

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

Pero esto es bastante tedioso código cuando tiene que hacerlo con todas sus propiedades relacionadas con los precios, por lo que se me ocurrió esto:

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

Es una buena práctica que se llama al método base cuando se reemplaza un método, a pesar de que la implementación base no hace nada.

Actualización: Este artículo también fue muy útil.

Uso de la DecimalPrecisonAttribute de KinSlayerUY, en EF6 puede crear una convención que se encargará de las propiedades individuales que tienen el atributo (en contraposición a establecer el DecimalPropertyConvention como en esta respuesta lo que afectará a todas las propiedades decimales).

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

A continuación, en su DbContext:

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

Entity Framework Ver 6 (alfa, RC1) tiene algo que se llama Convenciones personalizados . Para establecer la precisión decimal:

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

Referencia:

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

esto funcionará con el código de primeras migraciones como se describe aquí .

esta línea de código podría ser una manera más sencilla de lograr lo mismo:

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

- PARA EF CORE - con utilizando System.ComponentModel.DataAnnotations;

uso [Column (TypeName = "decimal ( precisión , escala ) ")]

Precisión = Número total de caracteres usado

Escala = Número total después del punto. (Fácil confundirse)

Ejemplo :

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

Más detalles aquí: https: // docs .microsoft.com / es-es / ef / core / modelado / relacional / tipos de datos

En 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);
    });

Siempre podrás EF para hacer esto con las convenciones de la clase de contexto en la función OnModelCreating de la siguiente manera:

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

Esto sólo se aplica a Primera Código EF para tu información y se aplica a todos los tipos decimales asignados a la base de datos.

Uso

System.ComponentModel.DataAnnotations;

Usted simplemente puede poner ese atributo en su modelo:

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

Se puede obtener más información encontrado en MSDN - faceta de Entity Data Model. http://msdn.microsoft.com/en-us/library/ee382834.aspx Completo recomendado.

atributo personalizado de KinSlayerUY trabajado muy bien para mí, pero tuve problemas con complexTypes. Estaban siendo mapeados como entidades en el código de atributo por lo que no podrían entonces ser asignada como ComplexType.

Por lo tanto, se extendió el código para permitir esto:

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

@ Mark007, he cambiado los criterios de selección de tipo de montar de la DbSet <> propiedades del DbContext. Creo que esto es más seguro porque hay momentos en los que tienes clases del espacio de nombres dado que no deben ser parte de la definición de modelo o que son, pero no son entidades. O sus entidades podrían residir en espacios de nombres separados o conjuntos separados y pueden juntar en vez de contexto.

Además, aunque poco probable, que no creo que es seguro confiar en la ordenación de las definiciones de métodos, por lo que es mejor para sacarlos con por Lista de parámetros. (.GetTypeMethods () es un método de extensión he construido para trabajar con el nuevo paradigma TypeInfo y puede aplanar las jerarquías de clase en la búsqueda de métodos).

Ten en cuenta que OnModelCreating delegados a este método:

    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;
            }
        }
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top