Saya bereksperimen dengan pendekatan kode-pertama ini, tapi saya sekarang tahu bahwa properti tipe System.Decimal akan dipetakan ke kolom sql tipe desimal (18, 0).
Bagaimana cara mengatur ketepatan kolom database?
Saya bereksperimen dengan pendekatan kode-pertama ini, tapi saya sekarang tahu bahwa properti tipe System.Decimal akan dipetakan ke kolom sql tipe desimal (18, 0).
Bagaimana cara mengatur ketepatan kolom database?
Jawaban:
Jawaban dari Dave Van den Eynde sudah ketinggalan zaman. Ada 2 perubahan penting, dari EF 4.1 dan seterusnya kelas ModelBuilder sekarang DbModelBuilder dan sekarang ada DecimalPropertyConfiguration.HasPrecision Method yang memiliki tanda tangan:
public DecimalPropertyConfiguration HasPrecision(
byte precision,
byte scale )
di mana presisi adalah jumlah total digit yang akan disimpan db, terlepas dari di mana titik desimal jatuh dan skala adalah jumlah tempat desimal yang akan disimpannya.
Oleh karena itu tidak perlu untuk beralih melalui properti seperti yang ditunjukkan tetapi hanya dapat dipanggil dari
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);
}
}
System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder
base.OnModelCreating(modelBuilder);
. Apakah itu disengaja atau hanya menjadi korban pengetikan kode online alih-alih dalam IDE?
Jika Anda ingin mengatur presisi untuk semua decimals
di EF6, Anda dapat mengganti DecimalPropertyConvention
konvensi default yang digunakan di DbModelBuilder
:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<DecimalPropertyConvention>();
modelBuilder.Conventions.Add(new DecimalPropertyConvention(38, 18));
}
Default DecimalPropertyConvention
di EF6 memetakan decimal
properti ke decimal(18,2)
kolom.
Jika Anda hanya ingin masing-masing properti memiliki presisi yang ditentukan, maka Anda dapat mengatur presisi untuk properti entitas pada DbModelBuilder
:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyEntity>().Property(e => e.Value).HasPrecision(38, 18);
}
Atau, tambahkan EntityTypeConfiguration<>
entitas untuk yang menentukan presisi:
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);
}
}
Saya bersenang-senang membuat Atribut Khusus untuk ini:
[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; }
}
menggunakannya seperti ini
[DecimalPrecision(20,10)]
public Nullable<decimal> DeliveryPrice { get; set; }
dan keajaiban terjadi pada pembuatan model dengan beberapa refleksi
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);
}
}
}
bagian pertama adalah untuk mendapatkan semua kelas dalam model (atribut khusus saya didefinisikan dalam perakitan itu jadi saya menggunakannya untuk mendapatkan perakitan dengan model)
foreach kedua mendapatkan semua properti di kelas itu dengan atribut khusus, dan atribut itu sendiri sehingga saya bisa mendapatkan data presisi dan skala
setelah itu saya harus menelpon
modelBuilder.Entity<MODEL_CLASS>().Property(c=> c.PROPERTY_NAME).HasPrecision(PRECISION,SCALE);
jadi saya memanggil modelBuilder.Entity () dengan refleksi dan menyimpannya dalam variabel entityConfig kemudian saya membangun ekspresi lambda "c => c.PROPERTY_NAME"
Setelah itu, jika desimalnya dapat dibatalkan saya panggil
Property(Expression<Func<TStructuralType, decimal?>> propertyExpression)
metode (saya menyebutnya dengan posisi dalam array, itu tidak ideal saya tahu, bantuan apa pun akan sangat dihargai)
dan jika itu tidak dapat dibatalkan saya sebut
Property(Expression<Func<TStructuralType, decimal>> propertyExpression)
metode.
Memiliki DecimalPropertyConfiguration saya memanggil metode HasPrecision.
MethodInfo methodInfo = entityConfig.GetType().GetMethod("Property", new[] { lambdaExpression.GetType() });
untuk mendapatkan kelebihan yang benar. tampaknya bekerja sejauh ini.
Dengan menggunakan DecimalPrecisonAttribute
dari KinSlayerUY, di EF6 Anda dapat membuat konvensi yang akan menangani masing-masing properti yang memiliki atribut (yang bertentangan dengan pengaturan DecimalPropertyConvention
seperti pada jawaban ini yang akan memengaruhi semua properti desimal).
[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);
}
}
Kemudian di Anda DbContext
:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
}
Precision
, maka saya sarankan mengatur batas atas ke 28 (jadi > 28
dalam kondisi Anda). Menurut dokumentasi MSDN, System.Decimal
hanya dapat mewakili maksimum 28-29 digit presisi ( msdn.microsoft.com/en-us/library/364x0z75.aspx ). Atribut juga menyatakan Scale
sebagai byte
, yang berarti prasyarat Anda attribute.Scale < 0
tidak perlu.
System.Decimal
tidak. Oleh karena itu tidak masuk akal untuk menetapkan prasyarat batas atas untuk sesuatu yang lebih besar dari 28; System.Decimal
tampaknya tidak dapat mewakili angka sebesar itu. Perlu diketahui juga bahwa atribut ini berguna untuk penyedia data selain SQL Server. Misalnya, numeric
tipe PostgreSQL mendukung hingga 131072 digit presisi.
decimal(38,9)
kolom akan senang menahan System.Decimal.MaxValue
tetapi decimal(28,9)
kolom tidak akan. Tidak ada alasan untuk membatasi presisi hanya 28.
Rupanya, Anda dapat mengganti metode DbContext.OnModelCreating () dan mengkonfigurasi presisi seperti ini:
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;
}
Tapi ini kode yang cukup membosankan ketika Anda harus melakukannya dengan semua properti Anda yang berkaitan dengan harga, jadi saya datang dengan ini:
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);
}
Adalah praktik yang baik bahwa Anda memanggil metode dasar ketika Anda menimpa metode, meskipun implementasi basis tidak melakukan apa pun.
Pembaruan: Artikel ini juga sangat membantu.
base.OnModelCreating(modelBuilder);
perlu menelepon . Dari metadata DbContext dalam 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.
Entity Framework Ver 6 (Alpha, rc1) memiliki sesuatu yang disebut Konvensi Kustom . Untuk mengatur presisi desimal:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<decimal>().Configure(config => config.HasPrecision(18, 4));
}
Referensi:
[Column(TypeName = "decimal(18,2)")]
ini akan bekerja dengan migrasi pertama kode EF Core seperti dijelaskan di sini .
The store type 'decimal(18,2)' could not be found in the SqlServer provider manifest
baris kode ini bisa menjadi cara yang lebih sederhana untuk melakukan hal yang sama:
public class ProductConfiguration : EntityTypeConfiguration<Product>
{
public ProductConfiguration()
{
this.Property(m => m.Price).HasPrecision(10, 2);
}
}
- UNTUK EF CORE - dengan menggunakan System.ComponentModel.DataAnnotations;
gunakan [Column
( TypeName
= "decimal
( presisi , skala )")]
Presisi = Total jumlah karakter yang digunakan
Skala = Jumlah total setelah titik. (mudah bingung)
Contoh :
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; }
}
Lebih detail di sini: https://docs.microsoft.com/en-us/ef/core/modeling/relational/data-types
Di 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);
});
Anda selalu dapat memberi tahu EF untuk melakukan ini dengan konvensi di kelas Konteks di fungsi OnModelCreating sebagai berikut:
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);
}
Ini hanya berlaku untuk Kode F EF Pertama dan berlaku untuk semua jenis desimal yang dipetakan ke db.
Remove<DecimalPropertyConvention>();
datang sebelum Add(new DecimalPropertyConvention(18, 4));
. Saya pikir itu aneh bahwa tidak hanya ditimpa secara otomatis.
Menggunakan
System.ComponentModel.DataAnnotations;
Anda cukup menaruh atribut itu di model Anda:
[DataType("decimal(18,5)")]
Anda dapat menemukan informasi lebih lanjut tentang MSDN - segi Model Data Entitas. http://msdn.microsoft.com/en-us/library/ee382834.aspx Direkomendasikan penuh.
Sebenarnya untuk EntityFrameworkCore 3.1.3:
beberapa solusi di 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)");
Atribut khusus KinSlayerUY bekerja dengan baik untuk saya, tetapi saya memiliki masalah dengan ComplexTypes. Mereka dipetakan sebagai entitas dalam kode atribut sehingga tidak dapat dipetakan sebagai ComplexType.
Karena itu saya memperpanjang kode untuk memungkinkan ini:
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, saya telah mengubah kriteria pemilihan jenis untuk naik dari properti DbSet <> dari DbContext. Saya pikir ini lebih aman karena ada kalanya Anda memiliki kelas di namespace yang diberikan yang seharusnya tidak menjadi bagian dari definisi model atau mereka tetapi bukan entitas. Atau entitas Anda dapat berada di ruang nama atau majelis yang terpisah dan ditarik bersama menjadi satu Konteks sekali.
Juga, meskipun tidak mungkin, saya tidak berpikir itu aman untuk mengandalkan pemesanan definisi metode, jadi lebih baik menariknya dengan daftar Parameter. (.GetTypeMethods () adalah metode ekstensi yang saya bangun untuk bekerja dengan paradigma TypeInfo baru dan dapat meratakan hierarki kelas saat mencari metode).
Perhatikan bahwa OnModelCreating mendelegasikan metode ini:
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;
}
}
}
[Column(TypeName = "decimal(18,4)")]
atribut untuk properti desimal Anda