In a aspnetcore mvc execute context .
我有这个简单的实体。
public class Foo
{
public int Id { get; private set; }
public string Name{ get; private set; }
public string Code { get; private set; }
private Foo() { }
public Foo(string Name, string Code)
{
GuardClauses.IsNullOrWhiteSpace(Name,nameof(Name), "cannot be null or empty");
GuardClauses.IsNullOrWhiteSpace(Code, nameof(Code), "cannot be null or empty");
this.Nom = Nom;
this.Code = Code;
}
}
在我的 DbContext 中,我有这个代码字段/约束,它确保代码从持久性的角度来看是唯一的。
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Foo>()
.HasIndex(u => u.Code)
.IsUnique();
}
我希望我的服务类中的 addNewFoo 方法在添加之前确保对于应用程序中的所有 Foos 的属性代码都是唯一的。
我尽我所能尊重坚持无知原则,但我并不像我希望的那样熟练。
对于初学者来说,确定"代码"字段是否为"唯一"是生成器的角色吗?
其次,我知道在我的验证层中,我可以确定是否存在与我当前尝试添加的实际 foo 相同的代码的现有 foo。但是这个approchah不是线程安全的或事务性的。
事实是,我不想等待我添加foo的那一刻,也有一个SqlException,只是知道它无法完成。
确保我的应用程序中的单一性的最佳方法是什么 牢记快速失败原则。
因为没有系统的具体示例或描述,我将进行一些概括。如果您提供一个具体的例子,我可以添加其他信息。每个解决方案都有一个最适合的上下文,当然总会有权衡
让我们问几个关于本准则的性质及其代表的内容的问题
。谁负责代码生成:系统用户还是系统本身?
代码可以完全随机吗(例如UUID)?
是由某种特殊算法生成的代码(SSN或可能由具有特殊含义的不同部分组成的CarPartNumber)
还有一个非常重要的问题:
- 我们预计这些独特的违规行为发生的频率如何?
如果问题2的答案是"是",那么您没有问题。你可以有一个重复的UUID,但几率非常低。您可以向数据库添加一个唯一配置以防万一,并将此违规视为您不关心的正常错误,因为它每百万年就会发生一次。
如果问题3的答案是肯定的,那么我们有不同的位置。在多用户系统中,您无法避免并发。有几种方法可以处理坐姿:
选项 1:乐观离线锁定
选项 2:悲观离线锁定
选项 3:如果系统正在生成代码,请具有特殊服务和队列代码生成请求。
如果选择使用锁,则可以锁定整个资源Foo或仅锁定代码生成。
选项 1:
您将必须处理 SQLException。这个在当今的大多数应用程序中使用,因为它通过不导致应用程序长时间停止而导致应用程序因某人锁定资源而导致长时间停止来确保流畅的用户体验。
您可以使用抽象,例如存储库。定义您自己的应用程序级异常 UniqueCodeViolationException 将由存储库引发。存储库将尝试{}catch{} SQLException,处理它并将其包装在UniqueCodeViolationException中,当比较错误代码时。这不会为您节省检查,但至少会隐藏具体错误,并且您只能在一个地方进行处理。
选项 2:
有时您确实需要确保没有并发性,因此使用此选项。在这种情况下,您将不得不仅为一个用户锁定创建Foo的过程,并且如果存在锁定,则不允许其他用户甚至能够打开用于创建Foo的对话框/表单/页面。
这确保了一致性,并通过创建一个基本上无法用于针对同一Foo的多个用户的系统来避免问题。您正在构建的应用程序很可能只有一个人负责Foo创建,或者并发性可能非常低,因此这可能是一个很好的解决方案。
我有朋友在保险应用程序中使用此锁。通常在他们的申请中,一个人去一个办公室购买保险。因此,为同一个人创建保险的并发可能性非常低,但为同一个人提供多个保险的成本非常高。
选项 3:
另一方面,如果您的代码是由系统生成的,则可以拥有一个处理代码生成并确保生成唯一代码的代码生成服务。您可以对这些请求进行队列。在服务中的每个生成操作中,您可以检查此代码是否存在并返回错误(或引发异常)。
现在进入问题4。如果您不希望经常发生冲突,只需在数据库中添加唯一约束并将其视为一般意外错误即可。添加检查代码是否已存在,如果存在,则显示错误。
您仍然可以在此处并发,因此将有一个微小的变化,一个用户将添加一个Foo,另一个用户将收到错误"哎呀...什么时候不对劲..请重试"。因为这每 100 年就会发生一次,所以没关系。
最后一个解决方案将通过忽略在极少数情况下可能发生的特殊情况来使您的系统变得更加简单。