Precisión decimal y escala en EF Code First

230

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

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

Dave Van den Eynde
fuente
11
una forma es usar el [Column(TypeName = "decimal(18,4)")]atributo para sus propiedades decimales
S.Serpooshan
[Column (TypeName = "decimal (18,4)")] funcionó muy bien !!!
Brian Rice,

Respuestas:

257

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

public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )

donde precisión es el número total de dígitos que almacenará la base de datos, independientemente de dónde caiga el punto decimal y la escala sea el número de lugares decimales que almacenará.

Por lo tanto, no es necesario recorrer las propiedades como se muestra, pero solo se puede llamar 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);
   }
}
AlexC
fuente
Para cualquiera que tenga problemas con el DbModelBuilder, intenteSystem.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder
Lloyd Powell el
1
Me di cuenta de que nunca llamaste base.OnModelCreating(modelBuilder);. ¿Fue intencional o simplemente una víctima de escribir código en línea en lugar de en un IDE?
BenSwayne
1
@BenSwayne gracias por el lugar, esta es mi omisión, no es nada intencional. Editaré la respuesta.
AlexC
26
Los 2 argumentos de HasPrecision (precisión, escala) están poco documentados. precisión es el número total de dígitos que almacenará, independientemente de dónde caiga el punto decimal. escala es el número de decimales que almacenará.
Chris Moschini
1
¿Existe una configuración EF para establecerlo para todas las propiedades decimales en todas las entidades en un solo lugar? Generalmente usamos (19,4). Sería bueno que esto se aplique automáticamente a todas las propiedades decimales, por lo que no podemos olvidar establecer una precisión de propiedad y perder la precisión anticipada en los cálculos.
Mike de Klerk
89

Si desea establecer la precisión para todos decimalsen EF6, puede reemplazar la DecimalPropertyConventionconvención predeterminada utilizada en DbModelBuilder:

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

El valor predeterminado DecimalPropertyConventionen EF6 asigna decimalpropiedades a decimal(18,2)columnas.

Si solo desea que las propiedades individuales tengan una precisión especificada, puede establecer la precisión de la propiedad de la entidad en DbModelBuilder:

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

O agregue un 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);
    }
}
kjbartel
fuente
1
Mi solución favorita Funciona perfecto cuando se usa CodeFirst y migraciones: EF busca todas las propiedades en todas las clases donde se usa "decimal" y genera una migración para estas propiedades. ¡Excelente!
okieh
75

Me divertí mucho creando 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; }

}

usándolo así

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

y la magia ocurre en la creación del modelo con cierta 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 obtener todas las clases en el modelo (mi atributo personalizado se define en ese ensamblaje, así que lo usé para obtener el ensamblaje con el modelo)

el segundo foreach obtiene todas las propiedades de esa clase con el atributo personalizado, y el atributo en sí mismo 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);

entonces llamo al modelBuilder.Entity () por reflexión y lo almaceno en la variable entityConfig luego construyo la expresión lambda "c => c.PROPERTY_NAME"

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

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

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

y si no es nulable llamo al

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

método.

Teniendo DecimalPropertyConfiguration llamo al método HasPrecision.

KinSlayerUY
fuente
3
Gracias por esto. Me salvó de generar miles de expresiones lambda.
Sean
1
¡Esto funciona muy bien y está súper limpio! Para EF 5, cambié System.Data.Entity.ModelConfiguration.ModelBuilder a System.Data.Entity.DbModelBuilder
Colin el
3
Yo uso MethodInfo methodInfo = entityConfig.GetType().GetMethod("Property", new[] { lambdaExpression.GetType() });para obtener la sobrecarga correcta. Parece funcionar hasta ahora.
fscan
3
He incluido esto en una biblioteca y he facilitado la llamada desde el DbContext: github.com/richardlawley/EntityFrameworkAttributeConfig (también disponible a través de nuget)
Richard
Richard, me encanta la idea de tu proyecto, pero ¿hay algo al respecto que requiera EF6? Lo usaría si hubiera una versión compatible con EF5, para poder usarlo con mi versión de ODP.NET.
Patrick Szalapski el
50

Usando el DecimalPrecisonAttributeKinSlayerUY, en EF6 puede crear una convención que manejará las propiedades individuales que tienen el atributo (en lugar de establecer el me DecimalPropertyConventiongusta en esta respuesta que afectará 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);
    }
}

Luego en tu DbContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
}
kjbartel
fuente
@MichaelEdenfield En realidad no, no hay ninguno de estos en EF6. Por eso agregué dos respuestas, esta y la otra a la que te refieres. Este es un atributo que puede colocar en una sola propiedad decimal en lugar de afectar todas las propiedades decimales en el modelo.
kjbartel
mi mal, no me di cuenta que los escribiste a ambos: \
Michael Edenfield
1
Si va a verificar los límites Precision, le recomiendo establecer el límite superior en 28 ( > 28en su condición). Según la documentación de MSDN, System.Decimalsolo puede representar un máximo de 28-29 dígitos de precisión ( msdn.microsoft.com/en-us/library/364x0z75.aspx ). Además, el atributo declara Scalecomo byte, lo que significa que su condición previa attribute.Scale < 0es innecesaria.
NathanAldenSr
2
@kjbartel Es cierto que algunos proveedores de bases de datos admiten precisiones superiores a 28; sin embargo, según MSDN, System.Decimalno. Por lo tanto, no tiene sentido establecer la condición previa del límite superior en algo mayor que 28; System.Decimalno puede representar números tan grandes, aparentemente. Además, tenga en cuenta que este atributo es útil para proveedores de datos que no sean SQL Server. Por ejemplo, el numerictipo de PostgreSQL admite hasta 131072 dígitos de precisión.
NathanAldenSr
1
@NathanAldenSr Como dije, las bases de datos usan un punto fijo decimal ( msdn ) mientras que System.Decimal es un punto flotante . Son completamente diferentes Por ejemplo, tener una decimal(38,9)columna se mantendrá feliz System.Decimal.MaxValuepero la decimal(28,9)columna no. No hay razón para limitar la precisión a solo 28.
kjbartel
47

Aparentemente, puede anular 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 este es un código bastante tedioso cuando tienes que hacerlo con todas tus propiedades relacionadas con el precio, así 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 llame al método base cuando anula un método, aunque la implementación base no haga nada.

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

Dave Van den Eynde
fuente
10
Gracias, esto me señaló en la dirección correcta. En CTP5, la sintaxis ha cambiado para permitir agregar Precisión y Escala en la misma instrucción: modelBuilder.Entity <Product> () .Property (product => product.Price) .HasPrecision (6, 2);
Col
2
Aún así, ¿no sería bueno tener algún tipo de "valor predeterminado" que podría establecer para todos los decimales?
Dave Van den Eynde
3
No creo que base.OnModelCreating(modelBuilder);sea ​​necesario llamar . De los metadatos de DbContext en VS: The default implementation of this method does nothing, but it can be overridden in a derived class such that the model can be further configured before it is locked down.
Matt Jenkins
@ Matt: Eso es bueno, pero como implementador no debería preocuparme por esto y siempre llamar a la base.
Dave Van den Eynde
@ Dave y @Matt: Hubo un comentario de que era "IMPORTANTE" llamar a la base. Es una buena práctica, pero cuando la fuente EF tiene una implementación vacía, es engañoso afirmar que es importante. Eso deja a la gente preguntándose qué hace la base. Tenía tanta curiosidad por lo IMPORTANTE que descompilé en el ef5.0 para verificar. Nada ahí. Tan solo un buen hábito.
phil soady
22
[Column(TypeName = "decimal(18,2)")]

esto funcionará con las primeras migraciones del código EF Core como se describe aquí .

Elnoor
fuente
1
Si solo agrega esto a su modelo, puede obtenerThe store type 'decimal(18,2)' could not be found in the SqlServer provider manifest
Savage
Parece que @Savage es un problema con su proveedor de base de datos o la versión de la base de datos
Elnoor
@Elnoor Savage es correcto, esto arrojará un error en EF Migraciones 6.x. La versión heredada, no Core no admite la especificación de precisión / escala a través del atributo Column, y no hace nada (por defecto es 18,2) si usa el atributo DataType. Para que funcione a través de Attribute en EF 6.x, necesitará implementar su propia extensión para ModelBuilder.
Chris Moschini
1
@ChrisMoschini, cambié mi respuesta mencionando EF Core. Gracias
Elnoor
14

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

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

- PARA EF CORE - con el uso de System.ComponentModel.DataAnnotations;

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

Precisión = Número total de caracteres utilizados

Escala = Número total después del punto. (fácil de confundir)

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/en-us/ef/core/modeling/relational/data-types

sofsntp
fuente
3

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);
    });
usuario3332875
fuente
Esta respuesta parece ser una actualización a otra respuesta que define el atributo, debe editar esto en esa respuesta
Rhys Bevilaqua
3

Siempre puede decirle a EF que haga esto con convenciones en la clase Context 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 solo se aplica a Code First EF fyi y se aplica a todos los tipos decimales asignados a la base de datos.

Gecko IT
fuente
No estaba funcionando hasta que Remove<DecimalPropertyConvention>();llega antes del Add(new DecimalPropertyConvention(18, 4));. Creo que es extraño que no se anule automáticamente.
Mike de Klerk
2

Utilizando

System.ComponentModel.DataAnnotations;

Simplemente puede poner ese atributo en su modelo:

[DataType("decimal(18,5)")]
VinnyG
fuente
1
Esta es la implementación más fácil para facilitar la lectura y la simplicidad. En mi humilde opinión
ransems
11
Según msdn.microsoft.com/en-us/library/jj591583(v=vs.113).aspx , esta respuesta es objetivamente incorrecta. "No confunda el atributo TypeName de Column con DataType DataAnnotation. DataType es una anotación utilizada para la interfaz de usuario y es ignorada por Code First".
speckledcarp
2
@ransems También lo pensé, hasta que lo probé y, como se dijo anteriormente, esto no funciona para CodeFirst y no migra a la base de datos
RoLYroLLs
1

Puede encontrar más información sobre MSDN - faceta del modelo de datos de entidad. http://msdn.microsoft.com/en-us/library/ee382834.aspx Completamente recomendado.

Jaider
fuente
Eso es genial y todo, pero ¿cómo se relaciona eso con Code-First?
Dave Van den Eynde
Es útil pero aún no puedo especificar un atributo [Precisión] para un Decimal. Entonces utilicé la solución provista por @KinSlayerUY.
Colin
1

Actual para EntityFrameworkCore 3.1.3:

alguna solución en OnModelCreating:

var fixDecimalDatas = new List<Tuple<Type, Type, string>>();
foreach (var entityType in builder.Model.GetEntityTypes())
{
    foreach (var property in entityType.GetProperties())
    {
        if (Type.GetTypeCode(property.ClrType) == TypeCode.Decimal)
        {
            fixDecimalDatas.Add(new Tuple<Type, Type, string>(entityType.ClrType, property.ClrType, property.GetColumnName()));
        }
    }
}

foreach (var item in fixDecimalDatas)
{
    builder.Entity(item.Item1).Property(item.Item2, item.Item3).HasColumnType("decimal(18,4)");
}

//custom decimal nullable:
builder.Entity<SomePerfectEntity>().Property(x => x.IsBeautiful).HasColumnType("decimal(18,4)");
Azamat
fuente
0

El atributo personalizado de KinSlayerUY funcionó muy bien para mí, pero tuve problemas con ComplexTypes. Se estaban mapeando como entidades en el código de atributo, por lo que no se podían mapear como ComplexType.

Por lo tanto, extendí 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
fuente
0

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

Además, aunque es poco probable, no creo que sea seguro confiar en el orden de las definiciones de métodos, por lo que es mejor sacarlas con la lista de parámetros. (.GetTypeMethods () es un método de extensión que construí para trabajar con el nuevo paradigma TypeInfo y puede aplanar las jerarquías de clases al buscar métodos).

Tenga en cuenta que OnModelCreating delega 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;
            }
        }
    }
Eniola
fuente
Me acabo de dar cuenta de que no abordaba ComplexTypes con este enfoque. Lo revisaremos más tarde.
Eniola
Sin embargo, la solución propuesta por Brian es simple, elegante y funciona. No haré ninguna declaración categórica sobre el rendimiento, pero alejarme de PropertyInfo ya reflejado en lugar de buscar el suyo debería producir un mejor rendimiento en modelos muy grandes (del orden de 200 o más).
Eniola