如何在商务逻辑层中管理统一性?

时间:2019-04-11 09:57:24

标签: c# design-patterns .net-core architecture ef-core-2.2

在aspnetcore mvc执行上下文中。

我有这个简单的实体。

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属性代码都是唯一的。

我会尽我所能遵守持久性无知原则,但是我并不像我希望的那样熟练。

对于初学者来说,确定代码字段是否唯一的是Builder的角色吗?

其次,我知道在验证层中,我可以确定是否已经存在一个具有与我当前尝试添加的实际foo相同的代码的foo。但是这种方法不是线程安全的或事务性的。

事实是我不想等到我添加foo时也有SqlException,只是想知道它无法完成。

确保我的应用程序与 牢记快速失败原理。

1 个答案:

答案 0 :(得分:1)

因为没有具体的示例或系统描述,所以我将对其进行概括。如果您提供具体示例,则可以添加其他信息。每个解决方案都有一个上下文,它最适用于此,当然总会有一个权衡

让我们问几个有关此代码的性质及其代表什么的疑问

  1. 谁负责代码的生成:系统的用户系统本身?

    < / li>
  2. 代码可以完全随机(例如UUID)吗?

  3. 是由某些特殊算法(SSN还是由具有特殊含义的不同部分组成的CarPartNumber)生成的代码

还有一个非常重要的问题:

  1. 我们期望这些独特的违例发生的频率是多少?

如果问题 2 的答案是“是”,那么您就没有问题。您可以有一个重复的UUID,但是机会很少。您可以在数据库中添加 Unique Constrant 以防万一,并将此违规视为您不关心的普通错误,因为它在一百万年后会发生一次。

如果问题 3 的答案是“是”,那么我们的情况就不同了。在多用户系统中,您无法避免并发。有几种方法可以处理这种情况:

如果选择使用锁定,则可以锁定整个资源 Foo ,也可以仅锁定 Code 生成。

选项1

您将必须处理SQLException。如今,该应用程序已在大多数应用程序中使用,因为它通过避免由于有人锁定资源而导致应用程序长时间停顿来确保流畅的用户体验

您可以使用抽象,例如存储库。定义您自己的应用程序级别的异常UniqueCodeViolationException,该异常将由存储库引发。当比较错误代码时,存储库将尝试{} catch {} SQLException,对其进行处理并将其包装在UniqueCodeViolationException中。这将不会为您节省检查,但至少会掩盖具体错误,并且只在一个地方进行处理。

选项2

有时您确实需要确保没有并发,因此您使用此选项。在这种情况下,您将只能锁定一个用户的 Foo 创建过程,并且不允许其他用户打开创建的对话框/表单/页面> Foo (如果有锁)。

这可以确保一致性,并通过创建一个基本上对于以同一 Foo 为目标的多个用户不可用的系统来避免该问题。您正在构建的应用程序很可能只有一个人负责 Foo 的创建,或者并发性很低,因此这可能是一个很好的解决方案。

我有朋友在保险应用程序中使用此锁。通常,在他们的申请中,一个人要去一个办公室进行保险。因此,为同一个人创建保险的并发可能性非常低,但是为同一个人购买多个保险的成本却很高。

选项3:

另一方面,如果您的代码是由系统生成的,则可以拥有一个 CodeGenerationService 来处理代码生成并确保生成唯一的代码。您可以将这些请求放在队列中。在Service的每个生成操作中,您可以检查此代码是否存在并返回错误(或引发异常)。

现在要提问 4 。如果您不希望经常发生冲突,只需在数据库中添加 Unique Constraint (唯一约束)并将其视为一般的意外错误即可。添加检查代码是否已经存在,并显示错误(

)。

您仍然可以在此处进行并发操作,因此将有一个微小的变化,一个用户将添加一个 Foo ,而另一个用户将收到错误消息“糟糕……有些错误。.请重试”。因为这将在100年内发生一次,所以可以。

最后一种解决方案将忽略罕见情况下可能发生的特殊情况,从而使您的系统更加简单。