C# 带有日期时间字段的实体框架并发性

C# 带有日期时间字段的实体框架并发性,c#,concurrency,entity-framework-core,C#,Concurrency,Entity Framework Core,更新模型的实体时出现问题: var intervenant = this.IntervenantRepository.GetAll().FirstOrDefault(intervenant => intervenant.Id == intervenantId); if (IsInscrire) { intervenant.MotifdesinscId = null; intervenant.IsInscrit = true; } this.Intervenan

更新模型的实体时出现问题:

var intervenant = this.IntervenantRepository.GetAll().FirstOrDefault(intervenant => intervenant.Id == intervenantId);

 if (IsInscrire)
 {
     intervenant.MotifdesinscId = null;
     intervenant.IsInscrit = true;
 }

this.IntervenantRepository.Update(intervenant);
我在中间实体模型中处理与属性
LastModificationTime
的一致性:

[ConcurrencyCheck] 
public DateTime LastModificationTime { get; set; }
当我用上面的源代码更新实体时,我得到了一个
DbUpdateConcurrencyException
,我认为问题在于我在属性LastModificationTime中得到的数据是2020-03-09T10:02:37,但在数据库中,列包含数据2020-03-09 10:02:37.4570000


是否有任何解决方案可以让我处理这个问题,因为我在其他实体中也得到了这个问题?

我的建议是完全忽略
LastModificationTime
属性,并使用
TimestampAttribute
。这实际上是@marc_s在评论中的建议

public class Intervenant
{
    // Your properties here

    [Timestamp]
    public byte[] RowVersion { get; set; }
}

它使用一个表示时间戳的字节数组,以避免您遇到的问题

在实体类中,只能将其应用于字节数组类型属性一次它在SQL Server数据库中创建一个时间戳数据类型为的列。实体框架API在数据库中UPDATE语句的并发检查中自动使用此时间戳列

如果您不想使用多个值并让框架自己处理,请使用此选项。它将稍微扩展表,但它将解决任何此类问题


并发检查属性

解决方案是修改我的实体配置,并删除此行:

.HasColumnType("datetime")
通过这种配置,我不再有问题:

 builder.Property(e => e.LastModificationTime)
            .HasColumnName("INTERVENANT_MODIFQUAND");

在尝试使用EF和DateTime字段作为并发检查字段时,我遇到了同样的挑战。EF并发代码似乎不符合元数据(edmx)的精度设置,即Type=“DateTime”precision=“3”。 数据库日期时间字段将在字段中存储毫秒分量(即2020-10-18 15:49:02.123)。即使将实体的原始值设置为包含毫秒组件的日期时间,SQL EF生成的结果如下:

UPDATE [dbo].[People]
SET [dateUpdated] = @0
WHERE (([PeopleID] = @1) AND ([dateUpdated] = @2))
-- @0: '10/19/2020 1:07:00 AM' (Type = DateTime2)
-- @1: '3182' (Type = Int32)
-- @2: '10/19/2020 1:06:10 AM' (Type = DateTime2)
正如您所看到的,@2是一个没有毫秒分量的字符串表示形式。这将导致更新失败

因此,如果要使用DateTime字段作为并发键,则在检索记录时必须从数据库字段中去掉毫秒/刻度,并且只传递/更新具有类似剥离DateTime的字段

    //strip milliseconds due to EF concurrency handling
    PeopleModel p = db.people.Where(x => x.PeopleID = id);
    if (p.dateUpdated.Millisecond > 0)
    {
        DateTime d = new DateTime(p.dateUpdated.Ticks / 10000000 * 10000000);
        object[] b = {p.PeopleID, d};
        int upd = db.Database.ExecuteSqlCommand("Update People set dateUpdated=@p1 where peopleId=@p0", b);
        if (upd == 1)
            p.dateUpdated = d;
        else
            return InternalServerError(new Exception("Unable to update dateUpdated"));
    }
return Ok(p);
当用新值更新字段时,也要去掉毫秒

(param)int id, PeopleModel person;
People tbl = db.People.Where(x => x.PeopleID == id).FirstOrDefault();
db.Entry(tbl).OriginalValues["dateUpdated"] = person.dateUpdated;
//strip milliseconds from dateUpdated since EF doesn't preserve them
tbl.dateUpdated = new DateTime(DateTime.Now.Ticks / 10000000 * 10000000);

日期只是以不同的格式显示。不确定是否存在类型问题,即两个日期中的秒数相差14秒。是的,问题是当我获取中间实体时,当我尝试进行导致问题的更新时,因为数据库中的数据是2020-03-09 10:02:37,所以我获取了其属性LastModificationTime,其值为2020-03-09T10:02:37,但没有.4570000。PC上的时间精确到100nsec。因此,您需要找出为什么时间在代码中被截断为秒。
DATETIME
在SQL Server中对于并发控制来说是一个错误的选择-它只精确到3.33毫秒-因此您可能会得到“错误”的副本。我强烈建议您使用一个适当的、单独的
ROWVERSION
列-这就是它的全部内容,也是处理乐观并发的最可靠的方法。这是一个解决方案,但我无法做到,我们有很多实体,我们选择在项目中使用lastModificationTime。我找到的解决方案是在我的实体的coffiguration中删除这一行。HasColumnType(“datetime”)。这是不错的,但它忽略了边缘情况和已经存在的机制,这是更有效的方法。