【问题标题】:SQL Server to .Net Decimals with EF6 dDatabase first issueSQL Server to .Net Decimals with EF6 database first issue
【发布时间】:2019-09-27 15:55:43
【问题描述】:

我目前正在从 SQL Server 存储过程中读取数据,它返回为 Decimal(38, 20)

我知道 .Net 小数点是 (28, 10),但 EF 为此生成的模型类显示 Decimal?。当我查询表时,代码会引发转换溢出错误,因为它试图将 SQL Server 小数点放在 .Net 小数点字段中。

有没有一种简单的方法可以通过数据上下文将其转换为 .Net 十进制?基本上我无权修改 SQL Server 数据类型。

SQL Server 数据类型:

 Amount Decimal(38, 20)

实体

public class EntityClass
{
     public decimal? Amount { get; set; }
}

当我打电话时

 var eee =  _context.EntityClass
                    .Select(x => x.Amount)
                    .FirstOrDefaultAsync();

我明白了

转换溢出错误

【问题讨论】:

  • 什么版本的 EF?您需要保留多少个有效数字?数据库中的什么值导致溢出?
  • 这个肯定是个问题 (-4210862852.86000000000000000000)。
  • 是的。我可以重现。什么版本的 EF?
  • 为什么你不在课堂上使用 double ?公双?金额{得到;放; }
  • @YairI double 不是just,它会让事情变得更糟。它的范围比 Decimal 并且保证会导致舍入错误。它也不会影响这个错误 - 字段的范围太长了

标签: c# sql-server entity-framework entity-framework-6


【解决方案1】:

这是 .NET 的 SqlClient 的错误或限制。这是一个再现:

    using (var con = new SqlConnection("server=.;database=tempdb;Integrated Security=true"))
    {
        con.Open();
        var cmd = new SqlCommand("select cast(4210862852.86 as decimal(38,20))  val", con);
        using (SqlDataReader rdr = cmd.ExecuteReader())
        {
            rdr.Read();

            var val = rdr.GetDecimal(0);
            Console.WriteLine(val);
        }
    }

问题是这个数字

select cast(cast(4210862852.86 as decimal(38,20)) as varbinary(20))

被存储,并通过网络传输

0x261400010000D877FB4DEE8B51699A5005000000

而且 SqlClient 将拒绝转换最后 4 个字节中任何非零的小数:

internal decimal Decimal
{
    get
    {
        ThrowIfNull();

        if (StorageType.Decimal == _type)
        {
            if (_value._numericInfo.data4 != 0 || _value._numericInfo.scale > 28)
            {
                throw new OverflowException(SQLResource.ConversionOverflowMessage);
            }
            return new decimal(_value._numericInfo.data1, _value._numericInfo.data2, _value._numericInfo.data3, !_value._numericInfo.positive, _value._numericInfo.scale);
        }
        if (StorageType.Money == _type)
        {
            long l = _value._int64;
            bool isNegative = false;
            if (l < 0)
            {
                isNegative = true;
                l = -l;
            }
            return new decimal((int)(l & 0xffffffff), (int)(l >> 32), 0, isNegative, 4);
        }
        return (decimal)this.Value; // anything else we haven't thought of goes through boxing.
    }
}

https://github.com/dotnet/corefx/blob/master/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlBuffer.cs

我在这里打开了一个针对 .NET Core 的问题:https://github.com/dotnet/corefx/issues/37592

【讨论】:

    【解决方案2】:

    您应该在 ApplicationDbContext 中添加

      protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            builder.Entity<EntityClass>()
                .Property(p => p.Amount )
                .HasColumnType("decimal(28,20)");
    

    【讨论】:

    • “我无权修改 SQL Server 数据类型”
    • OP的问题是数据库字段的精度38Decimal 最多可以处理 28 个。HasColumnType("decimal(28,20)") 也不会更改现有字段的精度,因此不会影响可以在那里存储的值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-14
    • 2020-02-06
    • 2011-04-01
    • 1970-01-01
    • 2022-12-02
    • 2017-03-01
    • 1970-01-01
    相关资源
    最近更新 更多