我在MVC 5应用程序上使用Code First方法使用Entity Framework 6.x.在这种特殊情况下,我的模型(以及其他内容)包含两个名为Latitude和Longitude的属性:
[Required, Range(-90, +90)]
public decimal Latitude { get; set; }
[Required, Range(-180, +180)]
public decimal Longitude { get; set; }
当我执行迁移时,我得到了类似的东西
CreateTable("ResProperty"), c => new {
:
Latitude = c.Decimal(nullable: false, precision: 10, scale: 8),
Longitude = c.Decimal(nullable: false, precision: 11, scale: 8),
:
})
... other stuff
所以纬度和经度都有8个十进制数字。前者有2个整数(最多90个),后者有3个整数(最多180个)。
执行Update-Database命令后,我的表的列显示为:
Latitude decimal(10,8)
Longitude decimal(11,8)
这对我来说似乎很好。现在在我看来,我有一个地图和Javascript代码,允许用户重新定位标记。这也很好。重新定位标记时,纬度和经度字段将填充更新的值,其中(Javascript)具有超过12个十进制数字。这与AFAIK无关,因为我的比例是8位小数。
按下提交按钮并调用Create或Edit POST方法后,我检查模型实例,并确认模型中传递给控制器的实际值是正确的,它们有足够的十进制数字(那些那个Javascript代码的地方)。所以价值是正确的。
现在......问题是在执行db.SaveChanges()之后数据库得到更新 - 我已经确认已经发生了实际的写入/更新 - 但不知何故内部EF忽略了我的实际值并写入截断纬度/经度四舍五入到只有两个十进制数字,所以我的纬度在数据库中显示为09.500000000所有其他十进制数字都归零,因为似乎已经进行了舍入。
// Prior to SaveChanges()
Latitude = 9.08521879
Longitude = -79.51658792
// After SaveChanges()
Latitude = 9.08000000
Longitude = -79.51000000
如果我给出了正确的比例和精度并且色谱柱具有正确的比例和精度,为什么它会四舍五入呢?为什么SaveChanges会改变我的价值观?
我发现这篇文章(http://weiding331.blogspot.com/2014/01/entity-framework-decimal-value.html)是同一个问题,但我不知道如何解决这个问题(如果确实如此),因为我已经在相关表格之后执行了多次迁移和数据添加被“迁移”了。
综述
答案 0 :(得分:12)
EF具有 SqlProviderServices 的特殊属性(SQL Server的SqlClient提供程序的实现) - TruncateDecimalsToScale 。 默认值为true,因此您可以将其更改为false值。例如:
public class DbContextConfiguration : DbConfiguration
{
public DbContextConfiguration()
{
var now = SqlProviderServices.Instance;
SqlProviderServices.TruncateDecimalsToScale = false;
this.SetProviderServices(SqlProviderServices.ProviderInvariantName, SqlProviderServices.Instance);
}
}
[DbConfigurationType(typeof(DbContextConfiguration))]
public class MyContext : DbContext
{ ... }
答案 1 :(得分:0)
存储空间数据,我建议为该类型的数据创建DbGeography
类。
正如@AdrianTarnowski指出的那样,可以使用SqlProviderServices.TruncateDecimalsToScale = false;
解决截断问题。但是,我想说明为什么会这样,为什么Entity Framework 6.X会截断十进制值而不是默认舍入。
要测试我使用的是这样的基本程序:
class Program
{
static void Main(string[] args)
{
var dbContext = new ApplicationDbContext();
dbContext.TestValues.Add(new TestValue()
{
Value = 0.0005m
});
dbContext.TestValues.Add(new TestValue()
{
Value = 0.0001m
});
dbContext.TestValues.Add(new TestValue()
{
Value = 0.0007m
});
dbContext.SaveChanges();
}
}
public class TestValue
{
public int Id { get; set; }
public decimal Value { get; set; }
}
public class DbContextConfiguration : DbConfiguration
{
public DbContextConfiguration()
{
var providerInstance = SqlProviderServices.Instance;
SqlProviderServices.TruncateDecimalsToScale = true;
this.SetProviderServices(SqlProviderServices.ProviderInvariantName, SqlProviderServices.Instance);
}
}
[DbConfigurationType(typeof(DbContextConfiguration))]
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext() : base("ApplicationContext")
{
Database.Log = s => Debug.WriteLine(s);
}
public DbSet<TestValue> TestValues { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<TestValue>().Property(x => x.Value).HasPrecision(18, 3);
base.OnModelCreating(modelBuilder);
}
}
默认看起来像这样:SqlProviderServices.TruncateDecimalsToScale = true;
。这是为了防止破坏依赖此行为的现有应用程序。
当TruncateDecimalsToScale正常(TruncateDecimalsToScale = true;
)时,来自实体框架的插入在Database.Log
中的DbContext
中看起来像这样:
INSERT [dbo].[TestValues]([Value])
VALUES (@0)
SELECT [Id]
FROM [dbo].[TestValues]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: '0,0005' (Type = Decimal, Precision = 18, Scale = 3)
不过,对于SQL Server Profiler
来说,上面的每个值发送的实际数据都是 0 。
exec sp_executesql N'INSERT [dbo].[TestValues]([Value])
VALUES (@0)
SELECT [Id]
FROM [dbo].[TestValues]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()',N'@0 decimal(18,3)',@0=0
从SqlProviderServices.TruncateDecimalsToScale = false;
到Database.Log
的{{1}}改为:
DbContext
现在INSERT [dbo].[TestValues]([Value])
VALUES (@0)
SELECT [Id]
FROM [dbo].[TestValues]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: '0,0005' (Type = Decimal)
看起来更好并且具有正确的值:
SQL Server Profiler