4

I have a model and an actionMethod in MVC;

public class employee
{
    [Key]
    public int id { get; set; }

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

    [Required]
    [Remote("doesCnicExist", "employee", AdditionalFields = "employeeID", HttpMethod = "POST", ErrorMessage = "A user with this cnic already exists. Please enter a different cnic.")]
    public string cnic { get; set; }
}

[HttpPost]
    public JsonResult doesCnicExist(string employeeID, string cnic)
    {
        var empList = hc.employee.ToList();
        bool flag = false;
        foreach (employee e in empList)
        {
            if ((employeeID == e.employeeID) && (cnic == e.cnic))
            {
                flag = true;
            }
        }
        return Json(flag == false);
    }

On Create() action, it works great. But on Edit() action, program sees cnic already exist. And I cannot update employee with the same cnic. I cannot figure out how I can use additional employeeID field to achieve uniquness of employee object while editing?

Jogi
  • 1
  • 2
  • 14
  • 29
  • What is the difference between `int id` and `string employeeID`? And you need to show the `doesCnicExist()` method you have tried. –  Mar 21 '16 at 02:06
  • @StephenMuecke `int id` is autogenrated by the database for the table and `Employee ID` is assigned by the organization. I am sorry I have been totally unable to think about a possible logic for this situation - for `doesCnicExist()`. That's why I asked this question. – Jogi Mar 21 '16 at 02:11
  • But you said it works for your `Create()` action. So show it and then it can be corrected to work for the `Edit()` method. (Bu as far as the `id` and `employeeID ` fields go, what is the point of having 2 identifier fields? - you should be using the `id` field in `AdditionalFields` since that's the key) –  Mar 21 '16 at 02:14
  • @StephenMuecke I got your point. But what difference it makes, since id will also always exist while editing. I have uploaded `doesCnicExist`. – Jogi Mar 21 '16 at 02:16

1 Answers1

6

Since id is your unique identifier, you need to pass that to the doesCnicExist() method and then you can modify the logic to ignore exiting rows where the id already exists. Change the model to

public class employee
{
    [Key]
    public int id { get; set; }
    ....
    [Required]
    [Remote("doesCnicExist", "employee", AdditionalFields = "id", HttpMethod = "POST", ErrorMessage = "A user with this cnic already exists. Please enter a different cnic.")]
    public string cnic { get; set; }
}

and the controller method to

[HttpPost]
public JsonResult doesCnicExist(string cnic, int id)
{
    return Json(IsUnique(cnic, id));
}

private bool IsUnique(string cnic, int id)
{
  if (id == 0) // its a new object
  {
    return !hc.employee.Any(x => x.cnic == cnic);
  }
  else // its an existing object so exclude existing objects with the id
  {
    return !hc.employee.Any(x => x.cnic == cnic && x.id != id);
  }
}

Note that I have separated the logic into a separate method because RemoteAttribute is client side validation only and validation should always be performed on the server (client side validation should be considered a nice bonus, but a malicious user can easily bypass it). The separate method also allows you to validate it in the Create() and Edit() POST methods to prevent a possible exception being thrown when you save to the database.

But because RemoteAttribute is a client side (UI) attribute, it should not be applied to a data model anyway, and you should be following best practice and using a view model where the attribute is applied to your view model property and not the data model. I also recommend you use normal naming conventions for your classes and property names (i.e. PascalCase).

Community
  • 1
  • 1
  • Side note: Refer [this DotNetFiddle](https://dotnetfiddle.net/jVHK7p) for an example of how it works –  Mar 21 '16 at 02:37
  • After your suggested solution, I am sorry but `[HttpPost] Create()` is not firing. When I press `Create` button, program just jams there. – Jogi Mar 21 '16 at 03:09
  • What do you mean - nothing in my code has anything to do with your `Create()` method - you have not even shown it in your question –  Mar 21 '16 at 03:15
  • 1
    The only thing I can think of is that you have not included a hidden input for the `id` property in your create view so therefore the value of `id` is `null` which would result in an exception. If that is the case, make the parameter nullable - `doesCnicExist(string cnic, int? id)` –  Mar 21 '16 at 03:18
  • And ditto for `IsUnique(string cnic, int? id)`. Then change the code in the `IsUnique()` method to `if (!d.HasValue)` and `return !hc.employee.Any(x => x.cnic == cnic && x.id != id.Value);` –  Mar 21 '16 at 03:20
  • Yes indeed, hidden input property for `id` was not included. So when I changed `IsUnique(string cnic, int id)` to `IsUnique(string cnic, int? id)`, it started working like a charm. Thank You. :) – Jogi Mar 21 '16 at 03:29
  • Just as a side note. Not sure if you know linq very well but your current `foreach` loop is awful in terms of performance. Imagine you had 100,000 employees. Your code first gets all of them from the database and adds then to an in-memory collection. Then you loop through them and assuming you find a match after the 10th iteration you still continue to loop through all the remaining 99,990 items. Compare that with using `.Any()` which does it without reading anything into memory and it stops searching as soon as it finds a match. –  Mar 21 '16 at 03:35