Precisão e escala decimais no código EF primeiro

230

Estou experimentando essa abordagem de primeiro código, mas agora descubro que uma propriedade do tipo System.Decimal é mapeada para uma coluna sql do tipo decimal (18, 0).

Como defino a precisão da coluna do banco de dados?

Dave Van den Eynde
fonte
11
é uma maneira de usar [Column(TypeName = "decimal(18,4)")]atributo para suas propriedades decimais
S.Serpooshan
[Column (TypeName = "decimal (18,4)")] funcionou muito bem !!!
Brian Rice

Respostas:

257

A resposta de Dave Van den Eynde está desatualizada. Há duas alterações importantes: a partir do EF 4.1, a classe ModelBuilder agora é DbModelBuilder e agora existe um método DecimalPropertyConfiguration.HasPrecision que possui uma assinatura de:

public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )

onde precisão é o número total de dígitos que o banco de dados armazenará, independentemente de onde o ponto decimal cai e a escala é o número de casas decimais que ele armazenará.

Portanto, não há necessidade de iterar pelas propriedades, como mostrado, mas elas podem ser chamadas a partir 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);
   }
}
AlexC
fonte
Para alguém que está recebendo problemas com a DbModelBuilder, tenteSystem.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder
Lloyd Powell
1
Notei que você nunca ligou base.OnModelCreating(modelBuilder);. Isso foi intencional ou apenas uma vítima de digitar código on-line em vez de em um IDE?
precisa saber é o seguinte
1
@ BenSwayne obrigado pelo lugar, esta é a minha omissão, não nada intencional. Vou editar a resposta.
AlexC #
26
Os 2 argumentos para HasPrecision (precisão, escala) estão mal documentados. precisão é o número total de dígitos que ele armazenará, independentemente de onde o ponto decimal cai. scale é o número de casas decimais que ele armazenará.
Chris Moschini
1
Existe uma configuração EF para defini-la para todas as propriedades decimais em todas as entidades em um único local? Geralmente usamos (19,4). Seria bom que isso fosse aplicado automaticamente a todas as propriedades decimais; portanto, não podemos esquecer de definir uma precisão de propriedade e perder a precisão prevista nos cálculos.
Mike de Klerk 16/11
89

Se você deseja definir a precisão para todos decimalsno EF6, substitua a DecimalPropertyConventionconvenção padrão usada em DbModelBuilder:

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

O padrão DecimalPropertyConventionno EF6 mapeia decimalpropriedades para decimal(18,2)colunas.

Se você deseja que apenas propriedades individuais tenham uma precisão especificada, defina a precisão da propriedade da entidade em DbModelBuilder:

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

Ou adicione um EntityTypeConfiguration<>para a entidade que especifica a precisão:

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
fonte
1
Minha solução favorita. Funciona perfeitamente ao usar o CodeFirst e as migrações: O EF procura todas as propriedades em todas as classes em que "decimal" é usado e gera uma migração para essas propriedades. Ótimo!
okieh
75

Eu me diverti bastante criando um Atributo personalizado para isso:

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

}

usando assim

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

e a mágica acontece na criação do modelo com alguma reflexão

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

a primeira parte é obter todas as classes no modelo (meu atributo personalizado é definido nessa montagem, então eu usei isso para obter a montagem com o modelo)

o segundo foreach obtém todas as propriedades dessa classe com o atributo customizado e o próprio atributo para que eu possa obter os dados de precisão e escala

depois disso eu tenho que ligar

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

então eu chamo o modelBuilder.Entity () por reflexão e armazeno-o na variável entityConfig, então construo a expressão lambda "c => c.PROPERTY_NAME"

Depois disso, se o decimal for anulável, chamo o

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

método (eu chamo isso de posição na matriz, não é o ideal, eu sei, qualquer ajuda será muito apreciada)

e se não for anulável eu chamo o

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

método.

Tendo o DecimalPropertyConfiguration, chamo o método HasPrecision.

KinSlayerUY
fonte
3
Obrigado por isso. Isso me salvou de gerar milhares de expressões lambda.
5133 Sean
1
Isso funciona muito bem e é super limpo! Para EF 5, mudei System.Data.Entity.ModelConfiguration.ModelBuilder para System.Data.Entity.DbModelBuilder
Colin
3
Eu uso MethodInfo methodInfo = entityConfig.GetType().GetMethod("Property", new[] { lambdaExpression.GetType() });para obter a sobrecarga correta. parece funcionar até agora.
fscan 07/02
3
Eu envolto isto em uma biblioteca e tornou mais fácil para chamar a partir do DbContext: github.com/richardlawley/EntityFrameworkAttributeConfig (também disponível via NuGet)
Richard
Richard, adoro a ideia do seu projeto, mas há algo que exija EF6? Eu o usaria se houvesse uma versão compatível com EF5, para que eu possa usá-lo com minha versão do ODP.NET.
Patrick Szalapski 11/11
50

Usando o DecimalPrecisonAttributeKinSlayerUY, no EF6, você pode criar uma convenção que manipulará propriedades individuais que possuem o atributo (em vez de definir o DecimalPropertyConventionmesmo nesta resposta que afetará todas as propriedades decimais).

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

Então no seu DbContext:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
}
kjbartel
fonte
@MichaelEdenfield Na verdade, não há nenhum deles no EF6. Por isso, adicionei duas respostas, essa e a outra a que você se referiu. Este é um atributo que você pode colocar em uma única propriedade decimal, em vez de afetar todas as propriedades decimais no modelo.
kjbartel
meu mal, não percebeu que você escreveu os dois: \
Michael Edenfield 14/07
1
Se você for verificar os limites Precision, recomendo definir o limite superior para 28 (portanto, > 28em sua condição). De acordo com a documentação do MSDN, System.Decimalpode representar apenas um máximo de 28 a 29 dígitos de precisão ( msdn.microsoft.com/en-us/library/364x0z75.aspx ). Além disso, o atributo declara Scalecomo byte, o que significa que sua pré attribute.Scale < 0- condição é desnecessária.
NathanAldenSr
2
@kjbartel É verdade que alguns provedores de banco de dados suportam precisões maiores que 28; no entanto, de acordo com o MSDN, System.Decimalnão. Portanto, não faz sentido definir a pré-condição do limite superior para algo maior que 28; System.Decimalaparentemente não pode representar números tão grandes. Além disso, esteja ciente de que esse atributo é útil para outros provedores de dados que não o SQL Server. Por exemplo, o numerictipo do PostgreSQL suporta até 131072 dígitos de precisão.
NathanAldenSr 27/02/2015
1
@NathanAldenSr Como eu disse, os bancos de dados usam um ponto fixo decimal ( msdn ) enquanto System.Decimal é ponto flutuante . Eles são completamente diferentes. Por exemplo, ter uma decimal(38,9)coluna manterá feliz o botão, System.Decimal.MaxValuemas uma decimal(28,9)coluna não. Não há nenhuma razão para limitar a precisão com apenas 28.
kjbartel
47

Aparentemente, você pode substituir o método DbContext.OnModelCreating () e configurar a precisão assim:

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

Mas esse é um código bastante tedioso quando você precisa fazer isso com todas as suas propriedades relacionadas a preços, então eu vim com isso:

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

É uma boa prática chamar o método base quando você substitui um método, mesmo que a implementação básica não faça nada.

Atualização: este artigo também foi muito útil.

Dave Van den Eynde
fonte
10
Obrigado, isso me apontou na direção certa. No CTP5, a sintaxe foi alterada para permitir a adição de Precisão e Escala na mesma instrução: modelBuilder.Entity <Product> () .Property (product => product.Price) .HasPrecision (6, 2);
Col
2
Ainda assim, não seria bom ter algum tipo de "padrão" que você possa definir para todas as casas decimais?
Dave Van den Eynde
3
Eu não acho que ligar base.OnModelCreating(modelBuilder);é necessário. Dos metadados do DbContext no 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: Isso é legal, mas como implementador eu não deveria me importar com isso e sempre chamar a base.
Dave Van den Eynde
@ Dave e @Matt: Houve um comentário que era "IMPORTANTE" chamar a base. É uma boa prática, mas quando a fonte EF tem uma implementação vazia, é enganoso afirmar que é Importante. Isso deixa as pessoas imaginando o que a base faz. Fiquei tão curioso sobre o que era IMPORTANTE que eu descompilei no ef5.0 para verificar. Nada ali. Então, apenas um bom hábito.
phil soady
30

O Entity Framework Ver 6 (Alpha, rc1) possui algo chamado Convenções personalizadas . Para definir a precisão decimal:

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

Referência:

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

isso funcionará com as primeiras migrações do código EF Core, conforme descrito aqui .

Elnoor
fonte
1
Se você apenas adicionar isso ao seu modelo, poderá obter #The store type 'decimal(18,2)' could not be found in the SqlServer provider manifest
Savage
Olhares @Savage como ele é um problema com seu provedor de banco de dados ou a versão do banco de dados
Elnoor
@Elnoor Savage está correto, isso gerará um erro no EF Migrations 6.x. A versão herdada, não Core, não oferece suporte à especificação de precisão / escala por meio do atributo Column e não faz nada (o padrão é 18,2) se você usar o atributo DataType. Para que ele funcione via Attribute no EF 6.x, seria necessário implementar sua própria extensão no ModelBuilder.
Chris Moschini 20/03
1
@ ChrisMoschini, mudei minha resposta mencionando o EF Core. Obrigado
Elnoor 21/03
14

essa linha de código pode ser uma maneira mais simples de realizar o mesmo:

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

- PARA EF CORE - com o uso de System.ComponentModel.DataAnnotations;

use [Column( TypeName = "decimal( precisão , escala )")]

Precisão = número total de caracteres usados

Escala = Número total após o ponto. (fácil de se confundir)

Exemplo :

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

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

sofsntp
fonte
3

No 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);
    });
user3332875
fonte
Esta resposta parece ser um upgrade para uma outra resposta que define o atributo, você deve editar esta em que a resposta
Rhys Bevilaqua
3

Você sempre pode dizer ao EF para fazer isso com convenções na classe Context na função OnModelCreating da seguinte maneira:

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

Isso se aplica apenas ao Code First EF fyi e se aplica a todos os tipos decimais mapeados para o banco de dados.

Gecko IT
fonte
Não estava funcionando até que Remove<DecimalPropertyConvention>();venha antes do Add(new DecimalPropertyConvention(18, 4));. Eu acho estranho que não seja substituído automaticamente.
Mike de Klerk 16/11
2

Usando

System.ComponentModel.DataAnnotations;

Você pode simplesmente colocar esse atributo no seu modelo:

[DataType("decimal(18,5)")]
VinnyG
fonte
1
essa é a implementação mais fácil para facilitar a leitura e a simplicidade. IMHO
ransems 21/03
11
Por msdn.microsoft.com/en-us/library/jj591583(v=vs.113).aspx , esta resposta está incorreta. "Não confunda o atributo TypeName da coluna com o DataType DataAnnotation. DataType é uma anotação usada para a interface do usuário e é ignorada pelo Code First."
Speckledcarp
2
@ransems Eu também pensava assim, até eu só testei e, como foi dito acima, isso não funciona para CodeFirst e não migrar para o banco de dados
RoLYroLLs
1

Você pode encontrar mais informações sobre o MSDN - faceta do modelo de dados da entidade. http://msdn.microsoft.com/en-us/library/ee382834.aspx Totalmente recomendado.

Jaider
fonte
Isso é ótimo e tudo, mas como isso se relaciona com o Code-First?
Dave Van den Eynde
É útil, mas não consigo especificar um atributo [Precision] para um decimal, ainda. Então, usei a solução fornecida pelo @KinSlayerUY.
Colin
1

Real para EntityFrameworkCore 3.1.3:

alguma solução no 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
fonte
0

O atributo personalizado do KinSlayerUY funcionou muito bem para mim, mas tive problemas com o ComplexTypes. Eles estavam sendo mapeados como entidades no código do atributo e, portanto, não podiam ser mapeados como um ComplexType.

Portanto, estendi o código para permitir isso:

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
fonte
0

@ Mark007, alterei os critérios de seleção de tipo para rodar nas propriedades DbSet <> do DbContext. Eu acho que isso é mais seguro, porque há momentos em que você tem classes no namespace fornecido que não devem fazer parte da definição do modelo ou são, mas não são entidades. Ou suas entidades podem residir em namespaces ou assemblies separados e serem reunidos em um único contexto.

Além disso, apesar de improvável, não acho seguro confiar na ordenação de definições de métodos, por isso é melhor retirá-las pela lista de Parâmetros. (.GetTypeMethods () é um método de extensão que eu criei para trabalhar com o novo paradigma TypeInfo e pode nivelar hierarquias de classes ao procurar métodos).

Observe que OnModelCreating delega para esse 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
fonte
Acabei de perceber que não lidei com ComplexTypes por essa abordagem. Irá revisá-lo mais tarde.
Eniola 23/10
No entanto, a solução proposta por Brian é simples, elegante e funciona. Não farei nenhuma declaração categórica sobre desempenho, mas sair do PropertyInfo já refletido, em vez de caçar o seu, deve render melhor desempenho em modelos muito grandes (da ordem de 200 e acima).
Eniola 23/10