В настоящее время у меня есть сервисный уровень, основанный на статье Проверка с сервисным уровнем с сайта ASP.NET.
Согласно этому ответу, это плохой подход, потому что логика обслуживания смешана с логикой валидации, которая нарушает принцип единой ответственности.
Мне действительно нравится альтернатива, которая предоставляется, но во время перефакторинга моего кода я столкнулся с проблемой, которую не могу решить.
Рассмотрим следующий интерфейс сервиса:
interface IPurchaseOrderService
{
    void CreatePurchaseOrder(string partNumber, string supplierName);
}
со следующей конкретной реализацией, основанной на связанном ответе:
public class PurchaseOrderService : IPurchaseOrderService
{
    public void CreatePurchaseOrder(string partNumber, string supplierName)
    {
        var po = new PurchaseOrder
        {
            Part = PartsRepository.FirstOrDefault(p => p.Number == partNumber),
            Supplier = SupplierRepository.FirstOrDefault(p => p.Name == supplierName),
            // Other properties omitted for brevity...
        };
        validationProvider.Validate(po);
        purchaseOrderRepository.Add(po);
        unitOfWork.Savechanges();
    }
}
 Для объекта PurchaseOrder который передается в валидатор, также требуются две другие сущности, Part и Supplier (для этого примера предположим, что PO имеет только одну деталь).
 Объекты Part и Supplier могут быть нулевыми, если предоставленные пользователем подробности не соответствуют объектам в базе данных, которые требуют, чтобы валидатор выдал исключение.
У меня проблема в том, что на этом этапе валидатор потерял контекстную информацию (номер детали и имя поставщика), поэтому не может сообщить точную ошибку пользователю. Лучшая ошибка, которую я могу предоставить, - это "заказ на поставку должен иметь связанную деталь", который не имеет смысла для пользователя, потому что он предоставил номер детали (он просто не существует в базе данных).
Используя класс обслуживания из статьи ASP.NET, я делаю что-то вроде этого:
public void CreatePurchaseOrder(string partNumber, string supplierName)
{
    var part = PartsRepository.FirstOrDefault(p => p.Number == partNumber);
    if (part == null)
    {
        validationDictionary.AddError("", 
            string.Format("Part number {0} does not exist.", partNumber);
    }
    var supplier = SupplierRepository.FirstOrDefault(p => p.Name == supplierName);
    if (supplier == null)
    {
        validationDictionary.AddError("",
            string.Format("Supplier named {0} does not exist.", supplierName);
    }
    var po = new PurchaseOrder
    {
        Part = part,
        Supplier = supplier,
    };
    purchaseOrderRepository.Add(po);
    unitOfWork.Savechanges();
}
Это позволяет мне предоставлять пользователю намного лучшую информацию о проверке, но означает, что логика проверки содержится непосредственно в классе обслуживания, что нарушает принцип единой ответственности (код также дублируется между классами обслуживания).
Есть ли способ получить лучшее из обоих миров? Могу ли я отделить уровень обслуживания от уровня проверки, в то же время предоставляя информацию об ошибках того же уровня?
