Где поставить валидацию для изменения дочерних объектов? - PullRequest
0 голосов
/ 15 ноября 2010

Я использую ASP.NET MVC 2 с nhibernate. У меня есть Продажа класс. В продаже много платежей . Платежи от продаж не должны изменяться, когда статус продаж становится подтвержденным . Мне нужны предложения о том, как обеспечить это.

Я нахожусь в тупике при попытке поставить следующие проверки:

  • Для добавления и удаления платежей:
    • Я могу создать методы AddPayment и DeletePayment в Sales, но все должны помнить об их использовании вместо непосредственного добавления и удаления коллекции платежей
    • Я не хочу скрывать сбор платежей, потому что он нужен nhibernate, этот сборник также используется в других частях программного обеспечения
  • Для изменения существующих платежей:
    • Не думаю, что мне следует помещать проверку в установщики платежей, потому что nhibernate необходим доступ к установщикам.
  • Должен ли я выбросить исключение? Существует обсуждение о недостатках создания исключения для предотвращения входа объекта в недопустимое состояние. В моем случае состояние объекта после модификации все еще может быть действительным, но я хочу заблокировать изменение. Разумно ли в этом случае выбрасывать исключение? Какие есть альтернативы?

Должен ли я применять это в действиях контроллера?

Ответы [ 3 ]

1 голос
/ 15 ноября 2010

Вы можете скрыть коллекцию, сделав ее защищенной или даже закрытой.NHibernate все равно найдет его.В качестве альтернативы вы можете заставить получателя сбора вернуть неизменную коллекцию, когда статус продаж является ограниченным значением.

private ISet<Payment> _payments;

public virtual ISet<Payment> Payments
{
    get
    {
        if (Status == SalesStatus.Confirmed)
            return new ImmutableSet<Payment>(_payments);

        return _payments;
    }
    private set { _payments = value; }
}

Размещение правил проверки в установщиках также хорошо.Вы можете указать NHibernate для прямого доступа к полю поддержки (вам может понадобиться добавить поле поддержки, если вы в настоящее время используете авто свойства).

<property name="_name" access="field"/>
<property name="Description" access="nosetter.camelcase-underscore"/>

Изменить для дополнительного вопроса ...

Я не склонен генерировать исключения, пока не произойдет сохранение.Бросать их раньше, как в установщике свойств, может раздражать разработчики пользовательского интерфейса, которые вынуждены возвращаться к пользователю только с одной ошибкой за раз.Я также в основном работаю с приложениями MVC, поэтому в последнее время я использую атрибуты проверки System.ComponentModel.DataAnnotations.Хотя в некоторых отношениях они ограничены, они хорошо работают с MVC для проверки моделей в браузере и в контроллере.Однако, если вы используете их, я рекомендую создать собственный перехватчик, чтобы проверить их перед сохранением.Перехватчик - это то место, где я выброшу исключение, если что-то не так.

0 голосов
/ 15 ноября 2010

Я бы представил два новых проекта (слоя) в вашем решении:

  • модельный слой
  • сервисный (или бизнес) уровень

Уровень вашей модели должен состоять из интерфейсов и вспомогательных типов:

public interface ISales
{
    IEnumerable<IPayment> GetPayments();
}

public enum PaymentStatus
{
    Unknown,
    Confirmed
}

public interface IPayment
{
    // your public properties
    PaymentStatus Status { get; set; }
}

Вы должны переместить свои классы NHibernate в слой сервиса и скрыть их с помощью internal. Ваши классы NHibernate реализуют интерфейсы модели:

internal class Sales : ISales
{
    public IEnumerable<IPayment> GetPayments()
    {
        // your implementation
    }
}

internal class Payment : IPayment
{
    // your public properties
    public PaymentStatus Status { get; set; }
}

public class SalesService
{
    public ISales FindByKey(int key)
    {
        // your implementation
    }

    public void AddPayment(ISales sales, IPayment payment)
    {
        // throw exception if validation fails
    }

    public void DeletePayment(ISales sales, IPayment payment)
    {
        // throw exception if validation fails
    }
}

public class PaymentService
{
    public IPayment FindByKey(int key)
    {
        // your implementation
    }
}

Поскольку классы Sales и Payment скрыты, ваши контроллеры вынуждены использовать классы обслуживания:

public class SalesController : Controller
{
    private readonly SalesService salesService;
    private readonly PaymentService paymentService;

    public SalesController()
    {
        salesService = new SalesService();
        paymentService = new PaymentService();
    }

    public ActionResult AddPayment(int salesId, int paymentId)
    {
        var sales = salesService.FindByKey(salesId);
        var payment = paymentService.FindByKey(paymentId);

        salesService.AddPayment(sales, payment);

        return RedirectToAction("Index");
    }
}

Вам также следует рассмотреть возможность использования контейнера IoC , например autofac или Ninject .

0 голосов
/ 15 ноября 2010

Есть несколько вариантов:

  • Один из них - иметь триггер в базе данных, тогда вы будете на 100% уверены
  • Ваше предложение AddPayment и DeletePayment в сочетании с некоторымиКодовый обзор, вероятно, лучший путь.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...