2011-08-25 66 views
5

我有一个强类型的视图,它有一个DropDownListFor属性。Guid的验证

下拉列表中的每个项目都由一个GUID表示。

我所追求的是一种验证用户是否从下拉列表中选择项目的方法。目前我还没有看到使用数据注释来做这件事。

有没有办法实现这个使用数据注释,因此客户端和服务器端验证将工作。

我猜我需要做一个自定义的方法来做到这一点,但想知道是否有任何东西已经存在。

回答

8

编辑答案

,在重读你的问题,这听起来像你只是想如果选择的值就知道了。如果是这样的话那么就应用RequiredAttributeGuid财产,并使其可为空的模型

public class GuidModel 
{ 
    [Required] 
    public Guid? Guid { get; set; } 

    public IEnumerable<Guid> Guids { get; set; } 
} 

然后在强类型的视图(@model GuidModel

@Html.ValidationMessageFor(m => m.Guid) 
@Html.DropDownListFor(
    m => m.Guid, 
    Model.Guids.Select(g => new SelectListItem {Text = g.ToString(), Value = g.ToString()}), 
    "-- Select Guid --") 

添加客户端验证的JavaScript脚本客户端验证参考。

控制器看起来像

public class GuidsController : Controller 
{ 
    public GuidRepository GuidRepo { get; private set; } 

    public GuidsController(GuidRepository guidRepo) 
    { 
     GuidRepo = guidRepo; 
    } 

    [HttpGet] 
    public ActionResult Edit(int id) 
    { 
     var guid = GuidRepo.GetForId(id); 
     var guids - GuidRepo.All(); 

     return View(new GuidModel { Guid = guid, Guids = guids }); 
    } 

    [HttpPost] 
    public ActionResult Edit(GuidModel model) 
    { 
     if (!ModelState.IsValid) 
     { 
      model.Guids = GuidRepo.All(); 
      return View(model); 
     } 

     /* update db */ 

     return RedirectToAction("Edit"); 
    } 
} 

这将确保Guid属性是必需的模型绑定GuidModel

原来的答案

我不相信有一个现成的数据注释验证属性,它能够这样做的。我写了a blog post about one way to achieve this;这篇文章使用的是一个IoC容器,但如果你想让某些东西能够工作,你可以采用硬编码的依赖关系。

喜欢的东西

public class ValidGuidAttribute : ValidationAttribute 
{ 
    private const string DefaultErrorMessage = "'{0}' does not contain a valid guid"; 

    public ValidGuidAttribute() : base(DefaultErrorMessage) 
    { 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     var input = Convert.ToString(value, CultureInfo.CurrentCulture); 

     // let the Required attribute take care of this validation 
     if (string.IsNullOrWhiteSpace(input)) 
     { 
      return null; 
     } 

     // get all of your guids (assume a repo is being used) 
     var guids = new GuidRepository().AllGuids(); 

     Guid guid; 
     if (!Guid.TryParse(input, out guid)) 
     { 
      // not a validstring representation of a guid 
      return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); 
     } 

     // is the passed guid one we know about? 
     return guids.Any(g => g == guid) ? 
      new ValidationResult(FormatErrorMessage(validationContext.DisplayName)) : null; 
    } 
} 

,然后发送到控制器动作

public class GuidModel 
{ 
    [ValidGuid] 
    public Guid guid { get; set; } 
} 

模型这给你的服务器端验证。你也可以编写客户端验证来做到这一点,也许使用RemoteAttribute,但在这种情况下我没有看到很多的价值,因为唯一会看到这个客户端验证的人是那些在DOM;这对您的普通用户没有任何好处。

+1

实际上,除非您将Guid属性标记为可空,请参阅我的答案。 –

+0

@NickAlbrecht谢谢,更正 –

15

其实,你不能使用Required属性用的GUID(没有我在下面提及的方法),因为他们从struct继承,因此它们的默认值实际上是Guid.Empty的实例,这将满足的要求Required属性。现在说,有可能得到你想要的东西,你只需要让你的财产可以为空,拿这个例子...

public class Person 
{ 
    [Required] //Only works because the Guid is nullable 
    public Guid? PersonId { get; set;} 
    public string FirstName { get; set;} 
    public string LastName { get; set;} 
} 

通过标记为空的GUID(使用?,或可为空的,如果你喜欢的很长的路),你让它对浏览器发送的内容结合,保持为空。在你的情况下,只要确保下拉列表的默认选项的值使用空字符串作为它的值。

编辑:唯一要注意这种方法是你最终不得不使用像Person.GetValueOfDefault()无处不在,并可能测试为Guid.Empty。我厌倦了这样做,最终创建了自己的验证属性来帮助简化验证Guids(以及其他任何具有默认值的类型,我想将它们视为无效,如int,DateTime等)。但是,我还没有客户端验证,所以验证只发生在服务器上。但是,如果您可以使用可为空的类型,则可以将它与[Required](旨在不重复[Required]的功能)结合使用。那么至少你会得到[Required]的客户端验证。这意味着您仍然需要使用GetValueOrDefault(),但至少您不必再为Guid.Empty进行测试。 Gist链接有一些带有例子的XMLDocs,为简洁起见,我在这里将它们留在这里。我目前在ASP.NET Core中使用它。

要点:RequireNonDefaultAttribute

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)] 
public class RequireNonDefaultAttribute : ValidationAttribute 
{ 
    public RequireNonDefaultAttribute() 
     : base("The {0} field requires a non-default value.") 
    { 
    } 

    public override bool IsValid(object value) 
    { 
     return value != null && !Equals(value, Activator.CreateInstance(value.GetType())); 
    } 
} 
+0

我试着在模型中使用类似下面的属性的代码,如果我提供空值,模型会失败?根据摘要中的例子,在这种情况下,我认为null是好的,但不是0。请确认 [必须是非默认] public int?测试{get;组; } – Krishna

+0

这是因为你的类型是int ?.任何Nullable <>对象的默认值都是null,所以null无效,但是0。不过,你是正确的,因为我的例子不然。我会解决这个问题。为了得到你要描述的工作(允许null,但不是0),最好使用像这样的'[Range(1,int.MaxValue)] int?我{get; set;} –

+0

@Albercht谢谢您的确认。 – Krishna

3

我知道这是一个老问题了,但如果任何人有兴趣我设法解决这个问题通过创建[IsNotEmpty]注释(制作的GUID为空的不在我的情况下一个选项)。

这使用反射来确定在属性上是否存在Empty的实现,并且如果这样比较它。

public class IsNotEmptyAttribute : ValidationAttribute 
{ 

    public override bool IsValid(object value) 
    { 

     if (value == null) return false; 

     var valueType = value.GetType(); 
     var emptyField = valueType.GetField("Empty"); 

     if (emptyField == null) return true; 

     var emptyValue = emptyField.GetValue(null); 

     return !value.Equals(emptyValue); 

    } 
}