发现此问题是因为我有一个对象,其字段是根据ID计算的,其中包含ID作为其一部分的ID,带有前缀和校验和数字。要求这些计算值是唯一的,但它们也不能是随机的,所以这似乎是最好的方法。
有问题的代码如下:
entity = new Entity() { /* values */ };
context.SaveChanges(); //generate the ID field
entity.CALCULATED_FIELD = CalculateField(prefix, entity.ID);
这在99%的情况下都能正常工作,但偶尔我们会在数据库中获得一个看起来像这样的值:
ID: 1234
CALCULATED_FIELD : prefix000{1233}8
EXPECTED: prefix000{1234}3
括号中的部分是从ID列计算的。
计算字段不正确这一事实已经足够糟糕,但其含义是,在执行保存更改时,无法保证返回到Entity Framework的行是最初处理的行!我正在研究在插入时使用存储过程以修复生成的字段问题,但是从长远来看,如果我们继续处理错误的行,我们将会有大量不良数据。
当我告诉实体框架将表映射到存储过程时,它生成了以下样板代码:
INSERT [dbo].[tableName](fields...)
VALUES(values...)
DECLARE @ID int
SELECT @ID = [ID]
FROM [dbo].[tableName]
WHERE @@ROWCOUNT > 0 AND [ID] = scope_identity()
SELECT t0.[ID]
FROM [dbo].[tableName] as t0
WHERE @@ROWCOUNT > 0 AND t0.[ID] = @ID
我能想到的最好的想法是在调用scope_identity()之前可能会发生额外的插入。我们正在使用存储过程来迁移这个系统,而我们使用了@@ IDENTITY,那可能会有区别吗?
编辑:CalculateField:
public static string CalculateField(string prefix, int ID)
{
var calculated = prefix.PadRight(17 - ID.ToString().Length)
.Replace(" ", "0") + ID.ToString();
var multiplier = 3;
var sum = 0;
foreach (char c in calculated.ToCharArray().Reverse())
{
sum += multiplier * int.Parse(c.ToString());
multiplier = 4 - multiplier;
}
if (sum % 10 == 0) { return calculated + "0"; }
return calculated + (10 - (sum % 10)).ToString();
}
更新:将被调用的方法从静态更改为实例方法,并且仅在创建后进行其他更改后才运行它而不是在创建后直接运行它似乎解决了问题,原因我无法理解。我现在暂时搁置这个问题,因为我还没有足够大的样本来完全确定问题是否得到解决,还因为我没有解释真正改变的问题。