ASP.NET MVC 验证唯一性

14

Rails提供了非常方便的唯一性验证。

但ASP.NET MVC没有提供。

我需要确保用户输入的电子邮件地址还没有被其他人注册。

我只能想到一种方法来进行此类验证:在UniqueAttribute类中创建一个新的数据上下文对象。

但我担心为了一个验证而浪费内存创建一个新的数据上下文对象是危险的。

我错了吗?有更好的方法吗?

更新

这是我目前的进展。

public class UniqueEmailAttribute : ValidationAttribute {
    public override bool IsValid(object value) {
        DataContext db = new DataContext();
        var userWithTheSameEmail = db.Users.SingleOrDefault(
            u => u.Email == (string)value);
        return userWithTheSameEmail == null;
    }
}

// Usage
[UniqueEmail(ErrorMessage="This e-mail is already registered")]
public string Email { get; set; }

有两个问题。

  1. 最好只有一个UniqueAttribute类,而不是针对电子邮件、用户名等单独的类。我该如何做到这一点?

  2. 每次需要验证单个属性时都要创建一个新的数据上下文。

解决方案

因此,最终我在表上创建了唯一约束,现在我只需拦截Users存储库中的SqlException即可。效果很好,可能比在整个表中搜索相同节点更有效率。谢谢!


2
Rails和ASP.NET MVC的比较就像是苹果和橙子的比较,它们并不相同。ASP.NET MVC没有正式的数据持久层,你必须从众多选择中选择一个,并与之奋斗。 - TFD
@TFD,说得好。我同意,没有数据层,所以DataContext必须存储在某个地方,这就是我的问题所在。 - Alex
我认为,在 Web 项目中,在插入/更新之前每次验证单个属性是最好的选择,以避免并发问题。 - swapneel
你应该将当前的DataContext实例存储在HttpRequest.Item集合中,而不是创建一个新的实例。 - Dmitry S.
@Dmitry,我从未听说过将DataContext存储在HttpRequest.Item中。我找到了这个问题:https://dev59.com/lE3Sa4cB1Zd3GeqPuFK0 我创建了相同的类,它可以正常工作。希望它不会占用太多内存... - Alex
显示剩余2条评论
4个回答

8
Mvc 3版本中的新版本校验属性RemoteValidation,你可以在客户端(Jquery)上注册一个方法进行校验。以下是一个示例:
RemoteAttribute 新的RemoteAttribute校验属性利用jQuery Validation插件的远程校验器,使得客户端校验能够调用服务器上执行实际校验逻辑的方法。
在下面的示例中,UserName属性应用了RemoteAttribute。编辑此属性时,在Edit视图中,客户端校验将调用UsersController类中的UserNameAvailable动作来对此字段进行校验。
public class User {  
    [Remote("UserNameAvailable", "Users")]  
    public string UserName { get; set; }  
}  

以下是对应的控制器示例。
public class UsersController {  
        public bool UserNameAvailable(string username) {  
            return !MyRepository.UserNameExists(username);  

       }  
   }

Mvc 3

UPDATE

    public bool UserNameAvailable(string Propertyname)  
    {  
        if (Request.QueryString[0]= "UserName")  
        {   
            //validate username  
        }  
        elseif (Request.QueryString[0]= "Email")  
        {  
            //Validate Email  
        }  

    }   

请注意,这仅适用于客户端验证。您需要扩展RemoteAttribute以添加服务器端验证。请参见: http://www.codeproject.com/Articles/682434/Custom-Remote-Attribute-for-Client-Server-Validati - Kurren

2

2
ASP.Net具有一个功能,可以在用户注册时自动检查用户电子邮件地址的唯一性。这就是ASP.Net Membership服务,即使您不使用其所有功能,也可以使用它来实现您想要的功能。
如果您的MVC应用程序中没有使用完整的Membership功能,则只需使用
Membership.FindUsersByEmail(emailYouAreLookingFor);
如果返回任何值,则知道该地址不是唯一的。如果您正在使用Membership服务创建用户,则Membership服务将自动检查并向您返回代码,以指示用户的电子邮件地址不是唯一的。
Membership服务位于System.Web.Security区域中,因此您需要在控制器中引用 using System.Web.Security;
这是一个例子。
            MembershipCreateStatus createStatus = MembershipService.CreateUser(UserName, Password, Email);

            if (createStatus == MembershipCreateStatus.DuplicateEmail)
            {

                   //do something here
            }
            else
            {
                   //do something here

            }
我希望这能对您有所帮助!

1
一个可靠的方法是创建一个验证属性,该属性将查询数据库以获取电子邮件地址。这肯定会增加延迟。
另一种选择是在表上创建唯一约束,并拦截SqlException。

14
你的“万无一失”的方法并不可靠。它会产生竞争条件。两个独立的服务器进程可能同时查询同一个电子邮件地址,并且都被告知它不存在于数据库中。然后它们都会尝试保存它。只有一个能够成功。确保这种唯一性的唯一可靠方法是在数据库中设置唯一约束。 - mlibby
我已经更新了我的帖子,并附上了可用的类,但它仍然不是我想要的。从性能角度来看,唯一约束会更好吗? - Alex
如果您对每个HTTP请求使用相同的工作单元/连接,这通常是推荐的做法,那么mcl不应该是问题。但这肯定是需要注意的事情。 - Dmitry S.
1
@Alex 我知道这是老问题,但我刚刚谷歌了一下。在你捕获异常后,你会怎么处理它?它将如何自动地反映到ModelState中?我认为你的第一个方法可以解决这个问题。这个解决方案像你的第一种方法那样自动化吗? - dvjanm
1
@Alex 还有一件事:如果您想对模型进行编辑,第一种类型的验证似乎不太好。在这种情况下,您必须验证用户本身,而不仅仅是电子邮件地址的唯一性。 - dvjanm
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接