You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Entity Framework添加实体时截断Decimal并存入SQL Server?

问题描述

我尝试将从API获取的Decimal值截断后直接存入SQL Server的Recipes表,表结构和Entity Framework添加代码如下:

数据库表结构

CREATE TABLE [dbo].[Recipes] 
(
    [Id]          INT PRIMARY KEY NOT NULL,
    [Uri]         NVARCHAR(MAX) NULL,
    [Label]       NVARCHAR(MAX) NULL,
    [Image]       NVARCHAR(MAX) NULL,
    [Source]      NVARCHAR(MAX) NULL,
    [Url]         NVARCHAR(MAX) NULL,
    [ShareAs]     NVARCHAR(MAX) NULL,
    [Yield]       DECIMAL(38)   NULL,
    [Calories]    DECIMAL(38)   NULL,
    [TotalWeight] DECIMAL(38)   NULL,
    [TotalTime]   DECIMAL(38)   NULL,
);

EF添加实体代码

myContext.Recipes.Add(new()
{
    Id = IdForRecipesManually,
    Calories = hit.recipe.calories,
    ShareAs = hit.recipe.shareAs,
    Uri = hit.recipe.uri,
    Url = hit.recipe.url,
    Image = hit.recipe.image,
    Label = hit.recipe.label,
    Yield = hit.recipe.yield,
    Source = hit.recipe.source,
    TotalTime = hit.recipe.totalTime,
    TotalWeight = Decimal.Truncate(hit.recipe.totalWeight)
});

遇到两个问题:

  1. 直接在实体初始化中调用Decimal.Truncate截断TotalWeight不生效,但把截断逻辑放到外部临时变量(var temp = Decimal.Truncate(hit.recipe.totalWeight);)再赋值就正常,想问能不能在EF的Add操作中直接实现截断?
  2. 最初截断Decimal是因为遇到「argument exception parameter value is out of range decimal」错误,把SQL Server和EF中的Decimal类型改为decimal(38,30)后,部分对象正常存入,但又出现「Conversion overflow」错误。

解决方案

一、在EF Add操作中实现Decimal截断的方法

1. 临时变量中转(最简单直接)

既然外部变量截断有效,可先将截断后的值存入临时变量,再赋值给实体属性,避免EF表达式树解析可能带来的问题:

var truncatedTotalWeight = Decimal.Truncate(hit.recipe.totalWeight);
myContext.Recipes.Add(new()
{
    // 其他属性赋值...
    TotalWeight = truncatedTotalWeight
});

2. 实体属性Setter自动截断

在Recipe实体类中重写TotalWeight的Setter,让所有赋值操作自动触发截断逻辑,一劳永逸:

public class Recipe
{
    // 其他属性...
    private decimal? _totalWeight;
    public decimal? TotalWeight
    {
        get => _totalWeight;
        set => _totalWeight = value.HasValue ? Decimal.Truncate(value.Value) : null;
    }
}

之后不管是在Add还是其他操作中赋值TotalWeight,都会自动截断。

3. EF ValueConverter全局配置

在DbContext的OnModelCreating方法中配置值转换器,让EF在映射数据库时自动处理截断:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Recipe>()
        .Property(r => r.TotalWeight)
        .HasConversion(
            // 写入数据库时截断
            v => v.HasValue ? Decimal.Truncate(v.Value) : v,
            // 读取数据库时直接返回
            v => v
        );
}

这种方式不需要修改实体类,也不用每次手动处理截断逻辑。

二、解决Decimal范围/溢出错误

1. 理解SQL Server Decimal精度规则

SQL Server中decimal(p,s)的p是总位数(整数+小数),s是小数位数。比如你改成的decimal(38,30),意味着整数部分最多只能有38-30=8位。如果API返回的Decimal值整数部分超过8位,就会触发「Conversion overflow」错误。

2. 调整数据库Decimal精度

根据业务场景调整精度,比如总重量一般不会有超大整数位,可改为decimal(18,2)(总位数18,小数位2,整数位最多16位),完全满足日常需求:

ALTER TABLE [dbo].[Recipes] ALTER COLUMN [TotalWeight] DECIMAL(18,2) NULL;

同时在EF实体类中确保属性类型匹配,或者通过Fluent API配置:

modelBuilder.Entity<Recipe>()
    .Property(r => r.TotalWeight)
    .HasPrecision(18, 2);

3. 截断时额外校验范围

如果必须保留decimal(38,30)的精度,可在截断后添加范围校验,避免溢出:

var rawWeight = hit.recipe.totalWeight;
var truncatedWeight = Decimal.Truncate(rawWeight);
// 检查整数部分是否超过8位(38-30)
if (truncatedWeight >= Math.Pow(10, 8) || truncatedWeight <= -Math.Pow(10, 8))
{
    // 处理超出范围的情况,比如设为null、抛出业务异常或取最大值
    truncatedWeight = null;
}
myContext.Recipes.Add(new() { TotalWeight = truncatedWeight });

内容的提问来源于stack exchange,提问作者dev-113

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 04:55:17