-1

I added a class with an actionfilter to let me know if the current logged user is and Admin (bool on the record). Because I need this info in many views and controllers, I put the action filter in global.asax

The problem is that if the user gets updated and the IsAdmin Checkbox is unchecked, the View does not grab the new updated information unless I either rebuilt the project or visual studio gets restarted.

Here is my code setup.

AppUser Entity

   public class AppUser
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Display(Name = "User Name")]
        [Required]
        public string Id { get; set; }

        [Display(Name = "Display Name")]
        [Required]
        public string Name { get; set; }
        public bool IsSuperUser { get; set; }
        public bool IsAdmin { get; set; }
        [Display(Name = "Default Location")]

        public int LocationID { get; set; }
        public virtual Location Location { get; set; }
        public virtual ICollection<Department> Departments { get; set; }
    }

ActionFilter:

  public class AppUserActionFilter : System.Web.Mvc.ActionFilterAttribute
    {
        private CrewLogContext db = new CrewLogContext();

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //TODO remove repeating code..////////////
            var currentAppUser = HttpContext.Current.User.Identity.Name.Split('\\')[1];
            var appUser = db.AppUsers.Where(i => i.Id == currentAppUser).Single();
            var currentAppUserLocation = appUser.LocationID;
            var departments = appUser.Departments.ToList();
            filterContext.Controller.ViewData.Add("AppUserDepartments", departments);
            filterContext.Controller.ViewData.Add("AppUserLoca", currentAppUserLocation);
            filterContext.Controller.ViewData.Add("appUser", appUser.Id);
            //TODO remove repeating code..////////////
        }

        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            //Remove domain\ from windows authenticated user. 
            var currentAppUser = HttpContext.Current.User.Identity.Name.Split('\\')[1];

            //Get user from db. 
            var appUser = db.AppUsers.Where(i => i.Id == currentAppUser).Single();
            var currentAppUserLocation = appUser.LocationID;
            //get IsAdmin flag. 
            //TODO not updating in VIEW
            bool currentAppUserIsAdmin = appUser.IsAdmin;

            //department related to user. 
            //TODO not updating in VIEW
            var departments = appUser.Departments.ToList();
            filterContext.Controller.ViewBag.AppUserDepartments = new SelectList(departments, "Id", "Name");

            //Flag tells me if current user is ADMIN

            filterContext.Controller.ViewBag.AppUserIsAdmin = currentAppUserIsAdmin;
            filterContext.Controller.ViewBag.AppUserLocation = currentAppUserLocation;

        }
    }

View: Toggle display link if User is Admin or not.

@{
    ViewBag.Title = "Index";
    var isAdmin = ViewBag.AppUserIsAdmin;
}


<label>@ViewBag.AppUserIsAdmin</label>
@if (isAdmin)
{
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
}

global asax.

namespace CrewLog
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            GlobalTrackingConfig.DisconnectedContext = true;
            AreaRegistration.RegisterAllAreas();
            //added actionfilter globally
            GlobalFilters.Filters.Add(new AppUserActionFilter(), 0);
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}

I can see the edit is working because I can verify the changes in the database.

Here the code that I'm using to update the AppUser.

[HttpPost]
        [ValidateAntiForgeryToken]
        //[Bind(Include = "Id,Name,IsSuperUser,IsAdmin,LocationID")]
        public ActionResult Edit(string id,string[] selectedDepartments)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            var appUserToUpdate = db.AppUsers
                .Include(i => i.Location)
                .Include(i => i.Departments)
                .Where(i => i.Id == id).Single();

            if(TryUpdateModel(appUserToUpdate,"",new string[] {"Name","IsAdmin","IsSuperUser","LocationID"}))
            {
                try
                {
                    UpdateAppUserDepartments(selectedDepartments, appUserToUpdate);

                    db.SaveChanges();

                    return RedirectToAction("Index");


                }
                catch (RetryLimitExceededException /* dex */)
                {
                    //Log the error (uncomment dex variable name and add a line here to write a log.
                    ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
                }
            }
            PopulateAssignedDepartmentData(appUserToUpdate);
            return View(appUserToUpdate);
        }

and just in case here is the method that updates the departments assigned to this AppUser

private void UpdateAppUserDepartments(string[] selectedDepartments, AppUser appUserToUpdate)
        {
            if (selectedDepartments == null)
            {
                appUserToUpdate.Departments = new List<Department>();
                return;
            }

            var selectedDepartmentsHS = new HashSet<string>(selectedDepartments);
            var appUserDepartments = new HashSet<int>
                (appUserToUpdate.Departments.Select(c => c.Id));
            foreach (var department in db.Departments)
            {
                if (selectedDepartmentsHS.Contains(department.Id.ToString()))
                {
                    if (!appUserDepartments.Contains(department.Id))
                    {
                        appUserToUpdate.Departments.Add(department);
                    }
                }
                else
                {
                    if (appUserDepartments.Contains(department.Id))
                    {
                        appUserToUpdate.Departments.Remove(department);
                    }
                }
            }
        }

I thought it was Chrome but if I open the app with other browser such as FireFox, the data still persisting. Is this because it has been added to the global.asax?

and just in case I'm verifying that my razor view is correct I added

`<label>@ViewBag.AppUserIsAdmin</label>` to verify. 

The problem was the the db Context was not getting disposed. I modified my action filter. However, I'm sure there is a cleaner way to do this.

public class AppUserActionFilter : System.Web.Mvc.ActionFilterAttribute
    {
        //private CrewLogContext db = new CrewLogContext();

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //TODO remove repeating code..////////////
            using (CrewLogContext db1 = new CrewLogContext())
            {
                var currentAppUser = HttpContext.Current.User.Identity.Name.Split('\\')[1];
                var appUser = db1.AppUsers.Where(i => i.Id == currentAppUser).Single();
                var currentAppUserLocation = appUser.LocationID;
                var departments = appUser.Departments.ToList();
                filterContext.Controller.ViewData.Add("AppUserDepartments", departments);
                filterContext.Controller.ViewData.Add("AppUserLoca", currentAppUserLocation);
                filterContext.Controller.ViewData.Add("appUser", appUser.Id);
            }

            //TODO remove repeating code..////////////
        }

        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            //Remove domain\ from windows authenticated user. 
            using (CrewLogContext db2 = new CrewLogContext())
            {
                var currentAppUser = HttpContext.Current.User.Identity.Name.Split('\\')[1];

                //Get user from db. 
                var appUser = db2.AppUsers.Where(i => i.Id == currentAppUser).Single();
                var currentAppUserLocation = appUser.LocationID;
                //get IsAdmin flag. 
                //TODO not updating in VIEW
                bool currentAppUserIsAdmin = appUser.IsAdmin;

                //department related to user. 
                //TODO not updating in VIEW
                var departments = appUser.Departments.ToList();
                filterContext.Controller.ViewBag.AppUserDepartments = new SelectList(departments, "Id", "Name");

                //Flag tells me if current user is ADMIN

                filterContext.Controller.ViewBag.AppUserIsAdmin = currentAppUserIsAdmin;
                filterContext.Controller.ViewBag.AppUserLocation = currentAppUserLocation;
            }
        }



    }
causita
  • 1,607
  • 1
  • 20
  • 32
  • Not sure if this is related to the issue you are seeing, but since the `CrewLogContext` is created at the class level, and the class is being registered as a singleton in the `GlobalFilters.Filters` collection, the `CrewLogContext` instance is never disposed. You should either put it in a using block or use DI to inject it into your filters. You can use a [custom FilterProvider](https://stackoverflow.com/a/38493666/) to allow for disposable dependencies. – NightOwl888 Oct 20 '17 at 00:20
  • @NightOwl888 thank you! I disposed by using Using. I will still look for a cleaner way to do this but this will do for now. – causita Oct 20 '17 at 00:40

1 Answers1

0

Just in case I'm leaving this as an answer. Like @NightOwl888 suggested, I have to dispose of the context. in the action filter

public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        //Remove domain\ from windows authenticated user. 
        using (CrewLogContext db2 = new CrewLogContext())
        {
            var currentAppUser = HttpContext.Current.User.Identity.Name.Split('\\')[1];

            //Get user from db. 
            var appUser = db2.AppUsers.Where(i => i.Id == currentAppUser).Single();
            var currentAppUserLocation = appUser.LocationID;
            //get IsAdmin flag. 
            //TODO not updating in VIEW
            bool currentAppUserIsAdmin = appUser.IsAdmin;

            //department related to user. 
            //TODO not updating in VIEW
            var departments = appUser.Departments.ToList();
            filterContext.Controller.ViewBag.AppUserDepartments = new SelectList(departments, "Id", "Name");

            //Flag tells me if current user is ADMIN

            filterContext.Controller.ViewBag.AppUserIsAdmin = currentAppUserIsAdmin;
            filterContext.Controller.ViewBag.AppUserLocation = currentAppUserLocation;
        }
    }

I will find a cleaner way to do but at least it working as it should.

causita
  • 1,607
  • 1
  • 20
  • 32