Dokładność dziesiętna i skala w kodzie EF Najpierw


230

Eksperymentuję z tym podejściem opartym na pierwszym kodzie, ale teraz dowiaduję się, że właściwość typu System.Decimal jest odwzorowywana na kolumnę SQL typu dziesiętnego (18, 0).

Jak ustawić dokładność kolumny bazy danych?


11
Jednym ze sposobów jest użycie [Column(TypeName = "decimal(18,4)")]atrybutu dla właściwości dziesiętnych
S.Serpooshan

[Column (TypeName = "decimal (18,4)")] działał świetnie !!!
Brian Rice,

Odpowiedzi:


257

Odpowiedź Dave'a Van den Eynde jest już nieaktualna. Istnieją 2 ważne zmiany, od wersji EF 4.1 klasa ModelBuilder ma teraz nazwę DbModelBuilder, a obecnie istnieje metoda DecimalPropertyConfiguration.HasPrecision, która ma sygnaturę:

public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )

gdzie precyzja jest całkowitą liczbą cyfr, które baza danych będzie przechowywać, niezależnie od tego, gdzie spada kropka dziesiętna, a skala jest liczbą miejsc dziesiętnych, które zapisze.

Dlatego nie ma potrzeby powtarzania właściwości, jak pokazano, ale można po prostu wywołać z

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

Dla każdego, kto ma problemy z DbModelBuilder, spróbujSystem.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder
Lloyd Powell,

1
Zauważyłem, że nigdy nie dzwoniłeś base.OnModelCreating(modelBuilder);. Czy była to celowa czy tylko ofiara pisania kodu online zamiast w IDE?
BenSwayne,

1
@BenSwayne dziękuję za miejsce, to moje pominięcie, a nie cokolwiek celowego. Zmienię odpowiedź.
AlexC

26
2 argumenty HasPrecision (precyzja, skala) są słabo udokumentowane. precyzja to całkowita liczba cyfr, które będzie przechowywać, niezależnie od tego, gdzie przypada kropka dziesiętna. Skala to liczba miejsc dziesiętnych, które będzie przechowywać.
Chris Moschini,

1
Czy istnieje konfiguracja EF, aby ustawić ją dla wszystkich właściwości dziesiętnych dla wszystkich jednostek w jednym miejscu? Zwykle używamy (19,4). Byłoby miło mieć to automatycznie zastosowane do wszystkich właściwości dziesiętnych, więc nie możemy zapomnieć o ustawieniu precyzji właściwości i pominięciu oczekiwanej precyzji w obliczeniach.
Mike de Klerk

89

Jeśli chcesz ustawić dokładność dla wszystkich decimalsw EF6, możesz zastąpić domyślną DecimalPropertyConventionkonwencję używaną w DbModelBuilder:

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

Domyślnie DecimalPropertyConventionw EF6 odwzorowuje decimalwłaściwości na decimal(18,2)kolumny.

Jeśli chcesz, aby poszczególne właściwości miały określoną dokładność, możesz ustawić dokładność właściwości encji na DbModelBuilder:

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

Lub dodaj EntityTypeConfiguration<>dla encji, która określa precyzję:

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

1
Moje ulubione rozwiązanie. Działa idealnie podczas korzystania z CodeFirst i migracji: EF szuka wszystkich właściwości we wszystkich klasach, w których używany jest „dziesiętny” i generuje migrację dla tych właściwości. Wspaniały!
okieh

75

Miło spędziłem czas, tworząc niestandardowy atrybut dla tego:

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

}

używając tego w ten sposób

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

a magia dzieje się przy tworzeniu modelu z pewnym odbiciem

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

pierwsza część polega na uzyskaniu wszystkich klas w modelu (mój niestandardowy atrybut jest zdefiniowany w tym zestawie, więc użyłem go, aby uzyskać zestaw z modelem)

drugi foreach otrzymuje wszystkie właściwości w tej klasie z atrybutem niestandardowym, a sam atrybut, dzięki czemu mogę uzyskać dokładność i skalować dane

potem muszę zadzwonić

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

więc wywołuję modelBuilder.Entity () przez odbicie i zapisuję go w zmiennej entityConfig, a następnie buduję wyrażenie lambda „c => c.PROPERTY_NAME”

Następnie, jeśli liczba dziesiętna jest zerowalna, wywołuję

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

metoda (nazywam to pozycją w tablicy, nie jest to idealne, wiem, każda pomoc będzie mile widziana)

a jeśli nie jest to zerowalne, dzwonię pod numer

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

metoda.

Mając DecimalPropertyConfiguration wywołuję metodę HasPrecision.


3
Dzięki za to. Uratowało mnie to przed wygenerowaniem tysięcy wyrażeń lambda.
Sean

1
Działa to świetnie i jest super czyste! W przypadku EF 5 zmieniłem System.Data.Entity.ModelConfiguration.ModelBuilder na System.Data.Entity.DbModelBuilder
Colin

3
używam, MethodInfo methodInfo = entityConfig.GetType().GetMethod("Property", new[] { lambdaExpression.GetType() });aby uzyskać prawidłowe przeciążenie. wydaje się działać do tej pory.
fscan

3
Spakowałem to do biblioteki i ułatwiłem wywoływanie z DbContext: github.com/richardlawley/EntityFrameworkAttributeConfig (dostępny również przez nuget)
Richard

Richard, podoba mi się pomysł twojego projektu, ale czy jest coś w tym, co wymaga EF6? Użyłbym go, gdyby istniała wersja kompatybilna z EF5, dzięki czemu mogę go używać z moją wersją ODP.NET.
Patrick Szalapski,

50

Używając DecimalPrecisonAttributez KinSlayerUY, w EF6 możesz stworzyć konwencję, która będzie obsługiwać poszczególne właściwości, które mają atrybut (w przeciwieństwie do ustawiania DecimalPropertyConventionpodobnych w tej odpowiedzi, które wpłyną na wszystkie właściwości dziesiętne).

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

Następnie w DbContext:

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

@MichaelEdenfield Właściwie nie ma jednego z nich w EF6. Dlatego dodałem dwie odpowiedzi, jedną i drugą, o której mówiłeś. Jest to atrybut, który można umieścić na pojedynczej właściwości dziesiętnej zamiast wpływać na wszystkie właściwości dziesiętne w modelu.
kjbartel

moje złe, nie zauważyłem, że napisałeś je oba: \
Michael Edenfield

1
Jeśli masz zamiar sprawdzić granice Precision, zalecamy ustawienie górnej granicy na 28 (więc > 28w twoim stanie). Zgodnie z dokumentacją MSDN System.Decimalmoże reprezentować maksymalnie 28-29 cyfr precyzji ( msdn.microsoft.com/en-us/library/364x0z75.aspx ). Ponadto atrybut określa Scalejako byte, co oznacza, że ​​warunek wstępny attribute.Scale < 0jest niepotrzebny.
NathanAldenSr

2
@kjbartel Prawdą jest, że niektórzy dostawcy baz danych obsługują dokładności większe niż 28; jednak według MSDN System.Decimalnie. Dlatego nie ma sensu ustawiać górnej granicy warunku na wartość większą niż 28; System.Decimalnajwyraźniej nie mogą reprezentować tak dużych liczb. Należy również pamiętać, że ten atrybut jest przydatny dla dostawców danych innych niż SQL Server. Na przykład numerictyp PostgreSQL obsługuje do 131072 cyfr dokładności.
NathanAldenSr

1
@NathanAldenSr Jak powiedziałem, bazy danych używają dziesiętnego punktu stałego ( msdn ), podczas gdy System.Decimal jest zmiennoprzecinkowy . Oni są zupełnie inni. Na przykład decimal(38,9)kolumna z przyjemnością przytrzyma, System.Decimal.MaxValueale decimal(28,9)kolumna nie. Nie ma powodu, aby ograniczać precyzję tylko do 28.
kjbartel

47

Najwyraźniej możesz przesłonić metodę DbContext.OnModelCreating () i skonfigurować dokładność w następujący sposób:

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

Ale jest to dość żmudny kod, gdy musisz to zrobić ze wszystkimi swoimi właściwościami związanymi z ceną, więc wymyśliłem to:

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

Dobrą praktyką jest wywoływanie metody podstawowej, gdy zastępujesz metodę, nawet jeśli implementacja podstawowa nic nie robi.

Aktualizacja: ten artykuł był również bardzo pomocny.


10
Dzięki, to wskazało mi właściwy kierunek. W CTP5 zmieniono składnię, aby umożliwić dodanie Precision i Scale w tej samej instrukcji: modelBuilder.Entity <Product> () .Property (product => product.Price) .HasPrecision (6, 2);
płk.

2
Czy jednak nie byłoby miło mieć jakieś „domyślne” ustawienie dla wszystkich miejsc po przecinku?
Dave Van den Eynde,

3
Nie sądzę, aby dzwonienie base.OnModelCreating(modelBuilder);było konieczne. Z metadanych DbContext w 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: To miłe, ale jako implementator nie powinienem się tym przejmować i zawsze dzwonię do bazy.
Dave Van den Eynde,

@ Dave i @Matt: Był komentarz, że „WAŻNE”, aby zadzwonić do bazy. Jest to dobra praktyka, ale gdy źródło EF ma pustą implementację, mylące jest twierdzenie, że jest Ważne. To sprawia, że ​​ludzie zastanawiają się, co robi baza. Byłem tak ciekawy, co było WAŻNE, że zdekompilowałem do ef5.0 do sprawdzenia. Nic. Tak dobry nawyk.
phil soady


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

będzie to działać z pierwszymi migracjami kodu EF Core, jak opisano tutaj .


1
Jeśli dodasz to do swojego modelu, możesz uzyskaćThe store type 'decimal(18,2)' could not be found in the SqlServer provider manifest
Savage

@ Savage wygląda na problem z dostawcą bazy danych lub wersją bazy danych
Elnoor

@Elnoor Savage jest poprawny, spowoduje to zgłoszenie błędu w EF Migrations 6.x. Starsza wersja non-Core nie obsługuje określania precyzji / skali za pomocą atrybutu Column i nie robi nic (domyślnie 18,2), jeśli użyjesz atrybutu DataType. Aby to działało poprzez Attribute w EF 6.x, musisz zaimplementować własne rozszerzenie do ModelBuilder.
Chris Moschini

1
@ChrisMoschini, zmieniłem swoją odpowiedź, wspominając EF Core. Dzięki
Elnoor

14

ta linia kodu może być prostszym sposobem na osiągnięcie tego samego:

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

9

- DLA EF CORE - przy użyciu System.ComponentModel.DataAnnotations;

use [Column( TypeName = "decimal( precyzja , skala )")]

Precyzja = całkowita liczba użytych znaków

Skala = całkowita liczba po kropce. (łatwo się pomylić)

Przykład :

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

Więcej informacji tutaj: https://docs.microsoft.com/en-us/ef/core/modeling/relational/data-types


3

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

Ta odpowiedź wydaje się być uaktualnieniem do innej odpowiedzi, która definiuje atrybut, należy ją edytować w tej odpowiedzi
Rhys Bevilaqua,

3

Zawsze możesz powiedzieć EF, aby zrobił to z konwencjami w klasie Context w funkcji OnModelCreating w następujący sposób:

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

Dotyczy to tylko Code First EF fyi i dotyczy wszystkich typów dziesiętnych odwzorowanych na db.


Nie działało, dopóki nie Remove<DecimalPropertyConvention>();nadejdzie Add(new DecimalPropertyConvention(18, 4));. Myślę, że to dziwne, że nie jest po prostu automatycznie zastępowane.
Mike de Klerk

2

Za pomocą

System.ComponentModel.DataAnnotations;

Możesz po prostu umieścić ten atrybut w swoim modelu:

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

1
jest to najłatwiejsza implementacja pod względem czytelności i prostoty. IMHO
ransems

11
Na msdn.microsoft.com/en-us/library/jj591583(v=vs.113).aspx ta odpowiedź jest nieprawidłowa. „Nie należy mylić atrybutu TypeName kolumny z DataType DataAnnotation. DataType jest adnotacją używaną w interfejsie użytkownika i jest ignorowana przez Code First”.
speckledcarp

2
@ransems Też tak myślałem, dopóki go nie przetestowałem i jak powiedziano powyżej, nie działa to w CodeFirst i nie migruje do bazy danych
RoLYroLLs


1

Rzeczywiste dla EntityFrameworkCore 3.1.3:

jakieś rozwiązanie w 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)");

0

Niestandardowy atrybut KinSlayerUY działał dla mnie dobrze, ale miałem problemy ze ComplexTypes. Były one mapowane jako byty w kodzie atrybutu, więc nie mogły być następnie mapowane jako ComplexType.

Dlatego rozszerzyłem kod, aby to umożliwić:

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

@ Mark007, zmieniłem kryteria wyboru typu, aby uzyskać dostęp do właściwości DbSet <> DbContext. Myślę, że jest to bezpieczniejsze, ponieważ zdarza się, że w podanej przestrzeni nazw istnieją klasy, które nie powinny być częścią definicji modelu lub są, ale nie są jednostkami. Albo twoje byty mogą znajdować się w oddzielnych przestrzeniach nazw lub osobnych zestawach i być połączone razem w jeden Kontekst.

Ponadto, choć mało prawdopodobne, nie sądzę, że można bezpiecznie polegać na porządkowaniu definicji metod, dlatego lepiej jest wyciągać je według listy parametrów. (.GetTypeMethods () to metoda rozszerzenia, którą zbudowałem do pracy z nowym paradygmatem TypeInfo i może spłaszczać hierarchie klas podczas wyszukiwania metod).

Należy pamiętać, że OnModelCreating deleguje do tej metody:

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

Właśnie zdałem sobie sprawę, że przy takim podejściu nie radziłem sobie z ComplexTypes. Zmienię to później.
Eniola

Jednak rozwiązanie zaproponowane przez Briana jest proste, eleganckie i działa. Nie będę przedstawiał żadnych kategorycznych stwierdzeń dotyczących wydajności, ale jazda z już odzwierciedloną właściwością PropertyInfo zamiast polowania na nią powinna dać lepszą wydajność na bardzo dużych modelach (rzędu 200 i więcej).
Eniola
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.