Child object 验证,如何以及何时?

Child object validation, how and when?

我怀疑如何以及何时验证 child objects。 假设我有两个实体 Invoice 和 InvoiceDetails,其中 invoice 包含一个 InvoiceDetails 列表。

    public class Invoice
    {
       public int id {get; set;}
       public DateTime CreationDate {get; set;}
       public List<InvoiceDetails> Details { get; set; } = new List<InvoiceDetails>();
    }

    public class InvoiceDetails
    {
       public int id {get; set;}
       public string Description { get; set; }
       public decimal Amount { get; set; }
       public List<InvoiceDetails> Details { get; set; } = new List<InvoiceDetails>();
    }

现在假设在服务层我有一个 InvoiceService 和 InvoiceDetailsS​​ervice 负责保存,并在此之前进行适当的验证,例如:

public class InvoiceService
    {
       public Invoice Save(Invoice invoice, User user)
        {
            Validate();

            if (invoice.Id == 0)
            {
                context.Invoices.Add(invoice);
            }

            await context.SaveChangesAsync();
            return invoice;
        }

       public void Validate(Invoice invoice, User user)
        {
            if (!user.canSaveInvoices)
            {
               throw new Exception("User cannot save invoices!!")
            }

            await context.SaveChangesAsync();
            return invoice;
        }
    }

public class InvoiceDetailService
    {
       public InvoiceDetail Save(InvoiceDetail invoiceDetails)
        {
            Validate();

            if (invoiceDetails.Id == 0)
            {
                context.InvoiceDetails.Add(invoiceDetails);
            }

            await context.SaveChangesAsync();
            return invoiceDetails;
        }

       public void Validate(InvoiceDetail invoiceDetails)
        {
            if (invoiceDetails.Amount<0)
            {
               throw new Exception("Amount cannot be less than 0!!")
            }

            await context.SaveChangesAsync();
            return invoiceDetails;
        }
    }

最后在控制器中我会有类似的东西:

public async Task<IActionResult> CreateInvoice([FromBody] InvoiceRequest model)
{
   var invoiceDetails = new InvoiceDetails()
   {
     //fill properties
   };

   var invoice = new Invoice()
   {
     //fill properties
     InvoiceDetails = new List<InvoiceDetails>() { invoiceDetails };
   };

   invoiceService.Save(invoice, currentUser);
    
}

现在的重点是,如果我这样做,发票服务将验证发票,但由于我没有调用 invoiceDetailService.Save(),我将跳过对 child(发票详情)。

很明显,在这种简单的情况下,我可能会在 invoiceService.Validate() 中进行验证,因为 invoiceDetails 可能只会存在于此上下文中,但暂时让我们把它分开并假设 invoiceDetails 也可能存在在此上下文之外,因此我不能依赖“parent”验证他的 child 的事实(也因为在某些时候,Invoice 可能会变成 child 更大的 class, 在 parent.. ).

中重写相同的验证是没有意义的

一个想法可能是向下一层并在 dal 级别管理验证,比如覆盖 entity framework 的 beforeSave 但这对我来说听起来非常错误......它根本没有意义验证数据层中的业务规则...

另一个想法可能是在控制器中验证..但是...不!控制器不应该有这个责任,而且我应该永远记得在保存之前进行验证......对我来说不合适..

我确定有一个正确且干净的解决方案,但我还看不到...有人可以指出正确的方向吗?

谢谢大家!

ps。还有一件事困扰着我,事实上,由于服务的保存方法调用了 context.SaveChanges,这实际上意味着上下文中的每个更改都将被保存,而不仅仅是与“服务”相关的更改叫.. 显然我可以强迫自己尽快保存,但无论如何它为可能的“难以排除故障的行为”敞开了大门。如果有人对此也有意见那就太好了!

您不应该为 InvoiceDetails 提供单独的服务。 InvoiceService 应该处理验证并保存 Invoice InvoiceDetails。参见 What's an Aggregate Root?

since the save methods of the services call the context.SaveChanges, it actually means that every change in the context will be saved

确定您的服务范围以在聚合根上运行将对此有所帮助。您可以通过启动 Transaction.

来跨越对 SaveChanges() 的多个调用的 UnitOfWork 范围