![](/img/trans.png)
[英]How to validate that a given amount is not greater than some amount using Data annotation validations?
[英]How to selectively validate some data annotation attribute?
我的視圖模型中有一些屬性在保存時是可選的,但在提交時是必需的。 總之,我們允許部分保存,但提交整個表單,我們確實希望確保所有必填字段都有值。
我現在能想到的唯一方法是:
視圖模型具有所有[Required]
屬性。 如果請求是部分保存,則在輸入控制器操作時, ModelState.IsValid
將變為false
。 然后我遍歷所有ModelState
(這是一個ICollection<KeyValuePair<string, ModelState>>
)錯誤並刪除[Required]
屬性引發的所有錯誤。
但是如果請求是提交整個表單,我將不會干擾ModelState
並且[Required]
屬性生效。
這個更難看。 一個視圖模型將包含操作方法用於提交的所有[Required]
屬性。 但是對於部分保存,我將表單數據發布到使用相同視圖模型的不同操作,而不使用所有[Required]
屬性。
顯然,我最終會遇到很多重復的代碼/視圖模型。
我一直在考慮是否可以為這些必需的屬性創建自定義數據注釋屬性[SubmitRequired]
。 並且以某種方式使得驗證在部分保存時忽略它,但在提交時則忽略。
仍然沒有一個明確的線索。 有人可以幫忙嗎? 謝謝。
這是我在項目中使用的一種方法。
創建一個包含業務邏輯的ValidationService<T>
,該業務邏輯將檢查您的模型是否處於使用IsValidForSubmission
方法提交的有效狀態。
將IsSubmitting
屬性添加到您在調用IsValidForSubmission
方法之前檢查的視圖模型。
僅使用內置驗證屬性來檢查無效數據,即字段長度等。
在某個場景中創建一些自定義屬性,這些屬性將在某些場景中驗證,即[RequiredIfSubmitting]
,然后在您的服務中使用反射來迭代每個屬性上的屬性並手動調用它們的IsValid
方法(跳過不在您的命名空間內的任何方法)。
這將填充並返回Dictionary<string, string>
,它可用於將ModelState
填充回UI:
var validationErrors = _validationService.IsValidForSubmission(model);
if (validationErrors.Count > 0)
{
foreach (var error in validationErrors)
{
ModelState.AddModelError(error.Key, error.Value);
}
}
我認為你的問題有更精確的解決方案。 假設您提交了一種方法,我的意思是說您使用相同的方法進行部分和完全提交。 然后你應該這樣做:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult YourMethod(ModelName model)
{
if(partialSave) // Check here whether it's a partial or full submit
{
ModelState.Remove("PropertyName");
ModelState.Remove("PropertyName2");
ModelState.Remove("PropertyName3");
}
if (ModelState.IsValid)
{
}
}
這應該可以解決您的問題。 如果您遇到任何麻煩,請告訴我。
編輯:
由於@SBirthare評論說在模型更新時添加或刪除屬性是不可行的,我在下面找到了適用於[Required]
屬性的解決方案。
ModelState.Where(x => x.Value.Errors.Count > 0).Select(d => d.Key).ToList().ForEach(g => ModelState.Remove(g));
上面的代碼將獲得所有會出錯的鍵並將其從模型狀態中刪除。 如果條件確實以部分形式提交,則需要將此行放在內部。 我還檢查了只有[Required]
屬性的錯誤(不知何故,模型綁定器給予該屬性高優先級,即使你將它放在任何其他屬性之后/之下)。 因此您不必再擔心模型更新了。
我的方法是添加條件檢查注釋屬性,這是從萬無一失的學習。
使SaveMode
成為視圖模型的一部分。
將屬性標記為可為空,以便在SaveMode
不是Finalize
時其值是可選的。
但是添加自定義注釋屬性[FinalizeRequired]
:
[FinalizeRequired]
public int? SomeProperty { get; set; }
[FinalizeRequiredCollection]
public List<Item> Items { get; set; }
以下是屬性的代碼:
[AttributeUsage(AttributeTargets.Property)]
public abstract class FinalizeValidationAttribute : ValidationAttribute
{
public const string DependentProperty = "SaveMode";
protected abstract bool IsNotNull(object value);
protected static SaveModeEnum GetSaveMode(ValidationContext validationContext)
{
var saveModeProperty = validationContext.ObjectType.GetProperty(DependentProperty);
if (saveModeProperty == null) return SaveModeEnum.Save;
return (SaveModeEnum) saveModeProperty.GetValue(validationContext.ObjectInstance);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var saveMode = GetSaveMode(validationContext);
if (saveMode != SaveModeEnum.SaveFinalize) return ValidationResult.Success;
return (IsNotNull(value))
? ValidationResult.Success
: new ValidationResult(string.Format("{0} is required when finalizing", validationContext.DisplayName));
}
}
對於原始數據類型,請檢查value!=null
:
[AttributeUsage(AttributeTargets.Property)]
public class FinalizeRequiredAttribute : FinalizeValidationAttribute
{
protected override bool IsNotNull(object value)
{
return value != null;
}
}
對於IEnumerable
集合,
[AttributeUsage(AttributeTargets.Property)]
public class FinalizeRequiredCollectionAttribute : FinalizeValidationAttribute
{
protected override bool IsNotNull(object value)
{
var enumerable = value as IEnumerable;
return (enumerable != null && enumerable.GetEnumerator().MoveNext());
}
}
這種方法通過從控制器中刪除驗證邏輯,最好地實現了關注點的分離。 Data Annotation屬性應該處理那種工作,哪個控制器只需要檢查!ModelState.IsValid
。 這在我的應用程序中特別有用,因為如果每個控制器中的ModelState
檢查不同,我將無法重構為基本控制器。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.