Domanda

sto sperimentando con questo approccio in codice prima, ma sto scoprire ora che una proprietà di tipo System.Decimal viene mappata a una colonna di tipo SQL decimale (18, 0).

Come faccio a impostare la precisione della colonna del database?

È stato utile?

Soluzione

La risposta di Dave Van den Eynde è ormai fuori moda. Ci sono 2 importanti cambiamenti, da EF 4.1 in poi la classe ModelBuilder è ora DbModelBuilder e ora c'è un metodo DecimalPropertyConfiguration.HasPrecision che ha una firma:

public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )

dove la precisione è il numero totale di cifre db sarà memorizzare, indipendentemente da dove il punto decimale cade e scala è il numero di cifre decimali memorizzerà.

Non è quindi necessario iterare attraverso proprietà come mostrato ma può semplicemente essere chiamato da

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

Altri suggerimenti

Se si desidera impostare la precisione per tutti decimals in EF6 si potrebbe sostituire la convenzione di default DecimalPropertyConvention utilizzato nella DbModelBuilder:

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

Il DecimalPropertyConvention predefinito in EF6 mappe proprietà decimal alle colonne decimal(18,2).

Se si desidera solo singole proprietà per avere una precisione specificata quindi è possibile impostare la precisione per la proprietà dell'entità sul DbModelBuilder:

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

In alternativa, aggiungere un EntityTypeConfiguration<> per l'entità che specifica la precisione:

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

ho avuto un bel tempo la creazione di un attributo personalizzato per questo:

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

}

utilizzando in questo modo

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

e la magia accade al momento della creazione del modello con qualche riflessione

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 prima parte è quello di ottenere tutte le classi nel modello (il mio attributo personalizzato è definito in quell'assemblea quindi ho usato che per ottenere l'assemblaggio con il modello)

la seconda foreach ottiene tutte le proprietà in quella classe con l'attributo personalizzato, e l'attributo stesso modo da poter ottenere i dati di precisione e di scala

Dopo di che ho dovuto chiamare

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

quindi chiamo il modelBuilder.Entity () per riflessione e memorizzarlo nella variabile entityConfig poi costruisco l'espressione lambda "c => c.PROPERTY_NAME"

Dopo di che, se il decimale è annullabile io chiamo il

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

metodo (i chiamo dalla posizione nella matrice, non è ideale so, qualsiasi aiuto sarà molto apprezzato)

e se non è annullabile io chiamo la

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

metodo.

Avere la DecimalPropertyConfiguration chiamo il metodo HasPrecision.

A quanto pare, è possibile ignorare il metodo DbContext.OnModelCreating () e configurare la precisione in questo modo:

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

Ma questo è il codice abbastanza noioso quando si ha a che fare con tutte le proprietà dei prezzi relativi, così sono arrivato fino a questo:

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

E 'buona pratica che si chiama il metodo di base quando si modifica un metodo, anche se l'implementazione di base non fa nulla.

Aggiornamento: Questo articolo era anche molto disponibile.

Uso della DecimalPrecisonAttribute da KinSlayerUY, in EF6 è possibile creare una convenzione che consente di gestire le proprietà individuali che hanno l'attributo (in contrasto con l'impostazione del DecimalPropertyConvention come in questa risposta che interesserà tutte le proprietà decimali).

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

Poi, nel tuo DbContext:

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

Entity Framework Ver 6 (Alpha, RC1) ha qualcosa che si chiama Convenzioni personalizzato . Per impostare la precisione decimale:

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

Riferimento:

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

questo funzionerà con codice prime migrazioni come descritto qui .

questa riga di codice potrebbe essere un modo più semplice per ottenere lo stesso:

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

- per EF CORE - con utilizzando System.ComponentModel.DataAnnotations;

uso [Column (TypeName = "decimal ( precisione , scala ) ")]

di precisione = Numero totale di caratteri usata

Scala = Numero totale dopo il punto. (Facile confondersi)

Esempio :

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

Maggiori dettagli qui: https: // docs .microsoft.com / en-us / ef / core / modellazione / relazionale / tipi di dati

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

Si può sempre dire EF a che fare questo con le convenzioni della classe Context nella funzione OnModelCreating nel seguente modo:

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

Questo vale solo per codice Prima EF FYI e si applica a tutti i tipi di decimali mappati al db.

con

System.ComponentModel.DataAnnotations;

Si può semplicemente inserire tale attributo nel modello:

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

E 'possibile trovare maggiori informazioni su MSDN - aspetto della Entity Data Model. http://msdn.microsoft.com/en-us/library/ee382834.aspx raccomandato pieno.

attributo personalizzato di KinSlayerUY funzionato bene per me, ma ho avuto problemi con complexTypes. Sono stati mappati come entità nel codice di attributo, quindi non potrebbero quindi essere mappati come ComplexType.

pertanto esteso il codice di far sì che la:

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, ho cambiato i criteri di selezione del tipo di cavalcare del DbSet <> proprietà della DbContext. Penso che questo è più sicuro perché ci sono momenti in cui si hanno le classi in un dato spazio dei nomi che non dovrebbero essere parte della definizione del modello o sono, ma non sono entità. O il vostro entità potrebbero risiedere in spazi dei nomi separati o assemblee separate ed essere tirato insieme in una volta Contesto.

Inoltre, anche se improbabile, non credo che sia sicuro fare affidamento su ordinazione delle definizioni di metodo, quindi è meglio per tirare fuori con le liste dei parametri. (.GetTypeMethods () è un metodo di estensione che ho costruito per funzionare con il nuovo paradigma TypeInfo e può appiattire le gerarchie di classi quando alla ricerca di metodi).

Do atto che OnModelCreating delegati a questo metodo:

    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;
            }
        }
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top