Search code examples
c#asp.netasp.net-mvcasp.net-identity

How to add a List<string> to other strings that filters results in ASP.NET MVC C#?


I have been stuck on this for over a day now and it's driving me nuts. I don't know what I'm doing wrong or what I'm missing. Anyways, I have a Users List page that is using ASP Identity framework. I have some filters at the top of the page that will filter the users by: UserName(txtbox), CompanyName(dropdown), and by Role(dropdown). My current code is filtering it, but it won't filter it with the other criteria's if they use multiple filters (like CompanyName and Role). I had to query a List string to populate my model to display in the view. So my problem I'm having trouble figuring out is how to add the "Roles" (which is the List string) to my controller code that incorporates 3 strings from the filters together. But since Roles is already a string, if I put .ToString() after it, the filter will not show any records when triggered. If I take the .ToString() off of Roles, it works only if I were to filter the Roles by itself, but it will not work with the other filters. Any suggestions on what I can do would be greatly appreciated. Here is my model:

public class UserList
{
    public UserList() { }

    public ApplicationUser User { set; get; }
    public List<string> Roles { get; set; }

}

Here is my view:

@using (Html.BeginForm("ListUsers", "Administrator", FormMethod.Get))
{
    <div class="form-inline title">
        <h4>Users</h4>
        <button class="create-btn"><span class="fa-solid fa-plus"></span>&nbsp;@Html.ActionLink("Create New User", "Register", "Account", new { @class = "CreateLink" })</button>
    </div>
    <p class="filter-section">
        <label class="filter-lbl1">By Company:</label><label class="filter-ddl">@Html.DropDownList("strComp", Extensions.GetCompanyList(), "All", new { @class = "comp-ctl" })</label>
        <label class="filter-lbl2">By Role:</label><label class="filter-ddl">@Html.DropDownList("strRole", Extensions.GetRolesList(), "All", new { @class = "role-ctn" })</label><br />
        <label class="filter-lbl3">By User Name:</label><label class="filter-txtbox">@Html.TextBox("strName", "", new { @class = "name-ctl" })</label>
        <button type="button" class="red" onclick="location.href='@Url.Action("ListUsers", new { })'"><i class="fa solid fa-trash-o"></i>&nbsp;&nbsp;Reset</button>
        <button type="submit" class="filter-btn"><i class="fa solid fa-filter"></i>&nbsp;&nbsp;Filter</button>
    </p>
}

<table class="table">
    <tr>
        <th>
            Email
        </th>
        <th>
            Company
        </th>
        <th>
            Role
        </th>
        <th>
            Last Login
        </th>
        <th></th>
    </tr>
    @foreach (var user in Model)
    {
        var appUser = user.User;
        var roles = user.Roles;
        <tr>
            <td class="email">
                @Html.HiddenFor(modelitem => user.User.Id)
                @Html.DisplayFor(modelitem => user.User.Email)
            </td>
            <td>
                @Html.DisplayFor(modelitem => user.User.CompanyName)
            </td>
            <td>
                @Html.DisplayFor(modelitem => user.Roles, string.Join(",", roles))
            </td>
            <td>
            </td>
            <td>
                @Html.ActionLink("Edit", "EditUser", new { id = appUser.Id }, new { @class = "edit-link" }) |
                @Html.ActionLink("Delete", "DeleteUser", new { id = appUser.Id }, new { @class = "delete-link" })
            </td>
        </tr>

    }
</table>

<link href="~/Content/PagedList.css" rel="stylesheet" />
<div></div>
Page @(Model.PageCount < Model.PageNumber ? 0: Model.PageNumber)/@Model.PageCount

@Html.PagedListPager(Model, page => Url.Action("ListUsers", new { page, strComp = ViewData["strFilter1"], strRole = ViewData["strFilter2"], strName = ViewData["strFilter3"] }))

And here is my code for my controller part:

public ActionResult ListUsers(string strFilter1, string strFilter2, string strFilter3, string strComp, string strName, string strRole, int? page)
    {
        if (strComp != null && strRole != null && strName != null)
        {
            strFilter1 = strComp;
            strFilter2 = strRole;
            strFilter3 = strName;
        }
        ViewBag.strFilter1 = strComp;
        ViewBag.strFilter2 = strRole;
        ViewBag.strFilter3 = strName;

        var user = (from u in context.Users
                    let query = from ur in context.Set<IdentityUserRole>()
                                where ur.UserId.Equals(u.Id)
                                join r in context.Roles on ur.RoleId equals r.Id
                                select r.Name
                    select new UserList() { User = u, Roles = query.ToList<string>() })
        .ToList();

        if (!String.IsNullOrEmpty(strComp))
        {
            //Filter results based on company selected.
            var pageNumber = page ?? 1;
            var pageSize = 25;
            if (strRole == null && strName == null)
            {
                var comp = user.Where(u => u.User.CompanyName.ToString().Contains(strComp));
                return View(comp.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
            }
            else
            {
                var compuser = user.Where(u => u.User.CompanyName.ToString().Contains(strComp) &&
                                                   u.Roles.ToString().Contains(strRole) &&
                                                   u.User.UserName.ToString().Contains(strName));
                return View(compuser.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
            }
        }
        if (!String.IsNullOrEmpty(strRole))
        {
            //Filter results based on role selected.
            var pageNumber = page ?? 1;
            var pageSize = 25;
            if (strComp == null && strName == null)
            {
                var roll = user.Where(u => u.Roles.Contains(strRole));
                return View(roll.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
            }
            else
            {
                var rolluser = user.Where(u => u.Roles.Contains(strRole) &&
                                                  u.User.CompanyName.ToString().Contains(strComp) &&
                                                  u.User.UserName.ToString().Contains(strName));
                return View(rolluser.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
            }
        }
        if (!String.IsNullOrEmpty(strName))
        {
            //Filter results based on the username typed in.
            var pageNumber = page ?? 1;
            var pageSize = 25;
            if (strComp == null && strRole == null)
            {
                var uname = user.Where(u => u.User.UserName.ToString().Contains(strName));
                return View(uname.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
            }
            else
            {
                var nameuser = user.Where(u => u.User.UserName.ToString().Contains(strName) &&
                                                 u.User.CompanyName.ToString().Contains(strComp) &&
                                                 u.Roles.ToString().Contains(strRole));
                return View(nameuser.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
            }
        }
        //var userList = user.OrderBy(u => u.User.Id).ToList();
        {
            var pageNumber = page ?? 1;
            var pageSize = 25;
            var userList = user.OrderBy(u => u.User.CompanyName).ToList();
            return View(userList.ToPagedList(pageNumber, pageSize));
        }
    }

The controller is where I believe the issue is. If I leave the .ToString() off of the Roles, its works perfect if I am just filtering by the role. But if I try to filter with anything else, it filters it to empty results. I'm thinking since the Roles is a list string, for some reason it's not able to properly add to the other strings. What's weird is with my current code, it will filter records by UserName and by Role at the same time, but the CompanyName and Role do not work in sync with each other.


Solution

  • I think the bit of logic you want is Enumerable.Any.

    e.g. your Where clause for roles can be:

    u.Roles.Any(role => role.Contains(strRole)))

    this will return true if any of the u.Roles contain the specified strRole string.


    Edit: Sidenote, if I've read this correctly, all of your code after the user declaration can be simplified to just:

    public ActionResult ListUsers(/*...*/)
    {
    
        var user = /*...*/;
    
        var pageNumber = page ?? 1;
        var pageSize = 25;
    
        IEnumerable<ApplicationUser> filteredUsers = user;
        if (!String.IsNullOrEmpty(strComp)) {
            filteredUsers = filteredUsers.Where(u => u.User.CompanyName.ToString().Contains(strComp));
        }
    
        if (!String.IsNullOrEmpty(strName)) {
            filteredUsers = filteredUsers.Where(u => u.User.UserName.ToString().Contains(strName));
        }
            
        if (!String.IsNullOrEmpty(strRole)) {
            filteredUsers = filteredUsers.Where(u => u.Roles.Any(role => role.Contains(strRole)));
        }
    
    
        return View(filteredUsers.OrderBy(u => u.User.Id).ToPagedList(pageNumber, pageSize));
    }