ASP.NET MVC 3 - 如何在调用UpdateModel或TryUpdateModel时有条件地验证(IValidatableObject)?

时间:2011-01-28 22:07:20

标签: c# asp.net-mvc asp.net-mvc-3 entity-framework

我正在使用EFCodeFirst CTP5(虽然这应该没什么区别)和ASP.NET MVC 3.我创建了以下实体:

public class Company
{
    public int CompanyID { get; set; }    
    public int SubdomainID { get; set; }
    public virtual Subdomain Subdomain { get; set; }    
}

public class Subdomain : IValidatableObject
{
    public int SubdomainID { get; set; }    
    public virtual ICollection<SubdomainActivity> Activities { get; set; }    
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        int activitySum = Activities.Sum(x => x.Value);
        if (activitySum != 100)
        {
            yield return new ValidationResult(string.Format("The Activity percentages must add up to 100. Current sum is {0}", activitySum), new[] { "Activities" });
        }
    }    
}
public class SubdomainActivity
{        
    public int ActivityID { get; set; }    
    public int Value { get; set; }
}

请注意,CompanySubdomainSubdomainICollection<SubdomainActivity>

我有CompanySubdomain的控制器。在SubdomainController我在UpdateModel()的实例上调用TryUpdateModel()(或Subdomain)时,我得到了我想要的结果。 Subdomain活动经过验证,值总计为100.

在我CompanyController的{​​{1}}实例UpdateModel()上,Company确认Subdomain.Activities加起来也是100,我不想要。用户甚至无法编辑Subdomain.Activites编辑视图中的Company,因此告诉他们Subdomain.Activities出错是没有意义的。

我希望能够在validationContext.Items IDictionary中添加一个项目,以便我可以根据上下文或类似的东西进行有条件的验证,但是如果不写我的话,我就无法弄清楚如何做到这一点拥有自己的IModelBinder(我想我可以做,但看起来有点过分)。最后,我在CompanyController中提出了这种解决方法,但我确信有一种正确的方法来解决这个问题(这包含在try catch

var d = this.companyRepository.GetById(id);
bool good = TryUpdateModel(d);
if (!good)
{
    if (ModelState.ContainsKey("Subdomain.Activities"))       
        ModelState.Remove("Subdomain.Activities");        
    if (!ModelState.IsValid)
        throw new InvalidOperationException(string.Format("The model of type '{0}' could not be updated.", typeof(Company).FullName));
}
this.companyRepository.Save();

作为一个有趣的附注,如果我不检查模型是否有效并抛出错误,那么companyRepository.Save()工作正常。换句话说,EFCodeFirst似乎不会在子域上调用Validate,但MVC UpdateModel会这样做。

任何人都可以用更好/正确/推荐的方式帮助我解决这个问题吗?

2 个答案:

答案 0 :(得分:0)

我找到了一种更优雅地处理这个问题的方法。我没有在IValidatableObject上实现Subdomain,而是在Subdomain的Activities属性上使用[CustomValidation]属性。在我的问题中验证Company实例时没有调用此方法,我仍然可以对活动进行专门的验证。希望有一天这会帮助某个人。

public class Subdomain
{
    public int SubdomainID { get; set; }

    [CustomValidation(typeof(Subdomain), "ValidateActivities")]
    public virtual ICollection<SubdomainActivity> Activities { get; set; }
    public static ValidationResult ValidateActivities(ICollection<SubdomainActivity> activities)
    {
        if (activities.Count > 0)
        {
            int activitySum = activities.Sum(x => x.Value);
            if (activitySum != 100)
            {
                return new ValidationResult(string.Format("The Activity percentages must add up to 100. Current sum is {0}", activitySum), new[] { "Activities" });
            }
        }
        return ValidationResult.Success;
    }
}

答案 1 :(得分:0)

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    int activitySum = Activities.Sum(x => x.Value);
    if (activitySum != 100)
    {
        yield return new ValidationResult(string.Format("The Activity percentages must add up to 100. Current sum is {0}", activitySum), new[] { "Activities" });
    }
}  

如下修改Validate()方法呢?

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
    int activitySum = Activities.Sum(x => x.Value);
    if (Activities.Count != 0 && activitySum != 100)
    {
        yield return new ValidationResult(string.Format("The Activity percentages must add up to 100. Current sum is {0}", activitySum), new[] { "Activities" });
    }
}