在哪里进行验证以更换对象的子项?

时间:2010-11-15 14:43:24

标签: asp.net-mvc nhibernate validation

我正在使用带有nhibernate的ASP.NET MVC 2。我有这个 Sales 类。销售有很多付款。一旦销售'状态变为确认,就不应修改销售付款。我需要有关如何强制执行此操作的建议。

在尝试进行此验证时,我对几件事情感到困惑:

  • 添加和删除付款:
    • 我可以在Sales中创建 AddPayment DeletePayment 方法,但每个人都必须记住使用这些方法而不是直接添加和删除付款集合
    • 我不想隐藏付款集合,因为nhibernate需要它,此集合也用于软件的其他部分
  • 修改现有付款:
    • 我认为我不应该在Payments设置器中进行验证,因为nhibernate需要访问setter。
  • 我应该抛出异常吗?有一个discussion关于抛出异常以防止对象进入无效状态的缺点。在我的情况下,修改后的对象状态可能仍然有效,但我想阻止修改。在这种情况下抛出异常是合理的吗?有什么选择?

我应该在控制器操作中强制执行此操作吗?

3 个答案:

答案 0 :(得分:1)

您可以通过使其受到保护甚至隐私来隐藏该集合。 NHibernate仍然会找到它。或者,当销售状态为限制值时,您可以让集合getter返回不可变集合。

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"/>

编辑其他问题...

在保存之前,我不倾向于抛出异常。像在属性设置器中一样向前抛出它们可能会让UI开发人员烦恼,他们不得不一次只返回一个错误。我也主要使用MVC应用程序,所以我最近一直在使用System.ComponentModel.DataAnnotations验证属性。虽然在某些方面受到限制,但它们在MVC上可以很好地用于浏览器和控制器中的模型检查。但是,如果您使用它们,我建议您创建一个自定义拦截器,以便在保存之前检查它们。如果出现任何问题,拦截器就会抛出异常。

答案 1 :(得分:0)

有几种选择:

  • 一个是在数据库中有一个触发器,那么你将100%确定
  • 您对AddPayment和DeletePayment的建议,加上一些代码审查可能是最好的方法。

答案 2 :(得分:0)

我将在您的解决方案中介绍两个新项目(图层):

  • 模型层
  • 服务(或业务)层

您的模型层应包含接口和支持类型:

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
    }
}

由于隐藏了类SalesPayment,因此您的控制器需要使用服务类:

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 containerautofacNinject