I have a controller Edit
which does an insert and update at the same action. In there I do a check against Active Directory
. The applictaion should show a validation error if insert fails due to a mismatch of the username entered by the user and checked agaisnt Active Directory
. Since multiple entries happen in the action then Insert should be stopped for that particular user even if the later usernames are valid.
Problem: My code currently does use the ModelState check but it doesn't work for validation message to be passed into VIEW, because model did not actually change it's state in this case.
Question: How can the server side check be done before DB insert for a particular Model property (username in this case)?
Assumption: Should the username check method return the username itself and that can then be passed to the view to display error message?
Limitations: Couldn't use the Annotation
on the model IsRequired
because it is not a mandatory field. USer should be able to edit the current list of user and save even if username text box is empty. Only when a username is entered in the text box and insert happens then the validation needed. Can't do a client side check against Active Directory
since it would be heavy. This is not a filed validation and rather check that bad data doesn't get through to the DB.
Model:
public class User
{
[Key]
public string username { get; set; }
[Key]
public string role { get; set; }
}
View:
@model List<Project.ViewModels.UserViewModel>
@using (@Html.BeginForm())
{
<table class="table">
<tr>
<th>User</th>
@for (int i = 0; i < Model[0].Roles.Count; i++)
{
<th>
@Model[0].Roles[i].RoleName
</th>
}
</tr>
@for (int i = 0; i < Model.Count; i++)
{
<tr>
<td>
@if (Model[i].UserName == null)
{
@Html.EditorFor(m=> m[i].UserName)
@Html.ValidationMessageFor(m=> m[i].UserName)
if (!ViewData.ModelState.IsValid)
{
<span class="field-validation-error">
@ViewData.ModelState["UserName"].Errors[0].ErrorMessage
</span>
}
}
else
{
@Html.HiddenFor(m => m[i].UserName)
@Model[i].UserName
}
</td>
@for (int j = 0; j < Model[i].Roles.Count; j++)
{
<td>
@Html.HiddenFor(m => m[i].Roles[j].RoleName)
@Html.CheckBoxFor(m => m[i].Roles[j].IsSelected)
</td>
}
</tr>
}
</table>
<div class="form-actions">
<button id="SubmitUserRoles" type="submit" class="btn btn-success submit" value="Save">Save</button>
</div>
}
<script>
$("#SubmitUserRoles").click(function () {
$.ajax({
url: '@Url.Action("Edit", "Users")',
type: 'POST',
cache: false,
data: JSON.stringify($('form').serialize()),
success: function (data) {
window.location.href = data
}, error: function (xhr, ajaxOptions, error) {
console.log(xhr.status);
console.log("Error: " + xhr.responseText);
}
});
});
});
Controller:
[HttpPost]
public ActionResult Edit(List<UserViewModel> model)
{
var level = model[0].Level;
var location = model[0].Location;
for (int i = 0; i < model.Count; i++)
{
if (model[i].UserName != null)
{
var uName = model[i].UserName;
for (int j = 0; j < model[i].Roles.Count; j++)
{
var uRole = model[i].Roles[j].RoleName;
var uRoleSelected = model[i].Roles[j].IsSelected;
var userWithSpecificRole = db.Users.FirstOrDefault(m => m.username == uName && m.role == uRole);
if (uRoleSelected && userWithSpecificRole == null)
{
if (DoesUserExist(uName))
{
if (ModelState.IsValid)
{
db.Users.Add(new User
{
username = uName,
role = uRole,
});
db.SaveChanges();
}
}
else
ModelState.AddModelError("UserName", "Username does not exist!");
}
}
}
}
return Json(Url.Action("Index", "Users", new {level,location}));
}
The method that checks the username against Active Directory
is as follows:
private bool DoesUserExist(string username)
{
PrincipalContext domain = new PrincipalContext(ContextType.Domain, "CompanyDomain", "DC=CompanyDomain,dc=com");
UserPrincipal foundUser = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, username);
return foundUser != null;
}