2

I have user created page and obviously I don't want my users to have same usernames so I created my validation for that, however I have problem editing users now. When I try to edit user it won't allow me to do it since it says username already taken. But I don't even want to edit the username, just password or something.

Edit: I did made mistake by calling ValidateModel(), now I fixed that but still run into problems.

Validator

public class UniqueUsername : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        if (value == null) return true; // In Edit view you can't edit username so it's null

        FinanceDataContext _db = new FinanceDataContext();
        var user = _db.Users.ToList().Where(x => x.Username.ToLower() == value.ToString().ToLower()).SingleOrDefault();

        if (user == null) return true;
        return false;
    }
}

Model

public class User
{
    public int ID { get; set; }

    // When I remove UniqueUsername and Remote validators everything works just fine
    [Required]
    [UniqueUsername(ErrorMessage = "Username is already taken")]
    [Remote("CheckUsername", "User", null, ErrorMessage = "Username is allready taken", HttpMethod = "POST")]
    public string Username { get; set; }

    [Required]
    public string Password { get; set;}
}

Action

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(User u)
{
    // Get user we want to edit
    var user = _db.Users.Where(x => x.ID == u.ID).SingleOrDefault();
    if (user == null) return HttpNotFound();

    // Set new fields manually
    if (!string.IsNullOrEmpty(u.Password)) user.Password = Infrastructure.Encryption.SHA256(u.Password);

    ModelState.Remove("Username"); // this doesen't seem to do much...

    _db.SaveChanges(); // validation error here
    return Content(Infrastructure.Helper.SerializeObject(u));
}

Error

Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.

I know why it throws me error. Because he checks if Username is already taken, and of course it's taken but I want to validate if username is taken only on CREATE, not on EDIT (since you can't change your username)

4

3 に答える 3

3

Your edit method loads a User entity that has a username. After that you're calling ValidateModel that checks if there is a User in the database with this username and if so it returns false (not valid). Well, of course there is one because you just loaded it! The error says that your IsValid method returned false.

The solution to your problem is to check if the username already exists but where the id is different. Change your validation to this code:

public class UniqueUsername : ValidationAttribute
{
    public override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null) return ValidationResult.Success; // In Edit view you can't edit username so it's null
        var currentUser = validationContext.ObjectInstance as User;
        FinanceDataContext _db = new FinanceDataContext();
        var user = _db.Users.ToList().Where(x => x.Username.ToLower() == value.ToString().ToLower() && x.ID != currentUser.ID).SingleOrDefault();

        if (user == null) return ValidationResult.Success;
        return new ValidationResult("Username exists");
    }
}
于 2012-11-01T15:46:50.283 に答える
0

I'm not sure what you're doing here, since you're validating the model that you just pulled out of the database, should that not have been validated already?

The user model that's posted (u) is validated when posted, you can verify its validation with:

if(ModelState.IsValid)

If that doesn't validate then you can remove the specific property validation error using *username in this case) with:

var username = ModelState["Username"];
if (username != null)
{
   username.Errors.Clear();
}

Unless you allow username changes this should be ok; I'm assuming that username will remain unchanged from the validated DB version.

于 2012-11-01T15:46:40.133 に答える
0

The exception your getting is due to EF context being created with the property "ProxyCreationEnabled" set to true. Try this instead "context.ContextOptions.ProxyCreationEnabled = false;"

However, in terms of your validation I would move away from data annotations and instead use fluent validation which is much easier to use and write tests for.

http://fluentvalidation.codeplex.com/wikipage?title=mvc&referringTitle=Documentation

于 2012-11-01T15:51:30.317 に答える