In my database table I have a column in which the values are manipulated before saving into the database. The logic for the manipulation was added at a later stage of development, after a lot of values were inserted to the table. Now I want to edit the contents of the table to manipulate the values of the existing contents.
My approach
To call the edit function for all the items in the table as the manipulation logic is added in the EDIT action method as well.
When I call the edit function while looping through the contents in the database, I get a null reference exception which is not there when I use the edit function from the UI.
EDIT Action method
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
{
//Many lines removed for simplicity. all the variables used in the code are assigned.
if (ModelState.IsValid)
{
// Valuestring from another function
setValue.Value = valuestring;
var original = db.SetValue.Find(setValue.SetValueID);
bool modified = original.Value != setValue.Value;
if (modified)
{
var rev = new RevisionHistory();
rev.CreatedOn = original.ModifiedOn;
rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
db.Entry(original).CurrentValues.SetValues(setValue);
db.RevisionHistory.Add(rev);
}
original.ModifiedOn = DateTime.Now;
original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
db.Entry(original).State = EntityState.Modified;
db.SaveChanges();
}
}
The call to this function was made from the HomeController. I commented all the return statements in the EDIT method while calling it from the HomeController.
Exception
Object reference not set to an instance of an object.
Question
Why does the edit work while called from the UI without any Exception , but not from HomeController?
Why is the user null even when I call the function from a different controller? Using windows authentication. How do I get the user name if the user has already been authenticated?
EDIT - Added how the Edit function is called
//Home controller (But I have also tried calling the function from a different controller where the call to the method is from the UI
public ActionResult Index()
{
test();
return View();
}
public void test()
{
foreach(var item in db.SetValue.ToList())
{
var setvalcon = new SetValuesController();
setvalcon.Edit(item);
}
}
Update - General Overview of the problem
The question can be generalized so as to find an answer how the User property is defined when using Windows Authentication.
Is it that the controller gets access to the Windows user property only when it is called from the UI?
Is it not possible to access the User Property in a function that is not called via UI?
And the best thing as far as I know about the issue is that, the User is defined in the Controller where the testmethod is called but not in the Edit in another controller.
You are partially correct when you are saying you can only access the User property only accessing from the UI. The correct answer is -
When accessing the Edit method through the UI, it is actually going through the ASP.Net controller initializer pipeline and that environment takes care of the current browser session and assigns the User variable. HttpContext is available at this time.
But when you are creating the controller variable like this -
var setvalcon = new SetValuesController();
setvalcon.Edit(item);
You are bypassing all those initialization codes. No HttpContext and this is just another normal object and thus it does not have the User property populated.
ANSWER TO QUESTIONS:
Is it that the controller gets access to the Windows user property only when it is called from the UI?
=> Yes, absolutely right, because you are going through the ASP.Net pipeline. But it is not only for Windows user, it is all those things that re in a HttpContext.
Is it not possible to access the User Property in a function that is not called via UI?
=> Only if, you manually assign it otherwise NO.
MORE INSIGHT:
Basically, what you are trying to achieve is a very poor design. Nowhere, remember nowhere, you are supposed to call a controller from inside a controller unless it is a subclass to base method call. The only way you can call another controller from inside another controller is redirecting the execution by using "Redirect" methods. No one will stop you from calling controller like this, but this shows poor design principle..
The best way to solve your situation is like this -
public class ModelService {
public void Edit(IPrincipal user, SetValue setValue){
setValue.Value = valuestring;
var original = db.SetValue.Find(setValue.SetValueID);
bool modified = original.Value != setValue.Value;
if (modified)
{
var rev = new RevisionHistory();
rev.CreatedOn = original.ModifiedOn;
rev.ModifiedBy = User.Identity.Name; //If modified exception on this line
db.Entry(original).CurrentValues.SetValues(setValue);
db.RevisionHistory.Add(rev);
}
original.ModifiedOn = DateTime.Now;
original.ModifiedBy = User.Identity.Name; //if not modified exception on this line
db.Entry(original).State = EntityState.Modified;
db.SaveChanges();
}
}
Then in both the controllers constructors -
public class ControllerOne : Controller {
private readonly ModelService _modelService
//constructor
public ControllerOne(){
_modelService= new ModelService ();
}
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue)
{
//Many lines removed for simplicity. all the variables used in the code are assigned.
if (ModelState.IsValid)
{
_modelService.Edit(User, Setvalue);
}
}
//controller 2
public class HomeController: Controller {
private readonly ModelService _modelService
//constructor
public ControllerOne(){
_modelService= new ModelService ();
}
public ActionResult Index()
{
foreach(var item in db.SetValue.ToList())
{
_modelService.Edit(User, item);
}
}
}
You can take help from IoC Container for dependency injection, that is even better approach.
Invoking a Controller from within another Controller is probably getting some data (as the User) to be missing. I wouldn't be making much effort understanding why (unless curious), since doing it this way might be considered as bad design. (I bet some other info would be missing as well, maybe cookies and such). the better thing you can do is to separate the logic from your Controllers into some service layer and call the methods with the IPrincipal User as parameter. If you are forced to do it the way you described, then send the user as a parameter to the other controller.
public ActionResult Index()
{
test(User);
return View();
}
public void test(IPrincipal user)
{
foreach(var item in db.SetValue.ToList())
{
var setvalcon = new SetValuesController();
setvalcon.Edit(item, user);
}
}
And the oter controller
public ActionResult Edit([Bind(Include = "SetValueID,Value,Status,TcSetID,OptionValueID,CreatedOn,CreatedBy,ModifiedOn,ModifiedBy")] SetValue setValue, IPrincipal user = null)
{
var currentUser = User == null? user : User;//or somthing like that
}
Didn't check this code. but it should give you something to work with.
Related
We are trying to implement security in with our predefined set of permissions, which will serves the purpose whether to execute action method, show the view OR not, hide specific control(Like button,textbox etc) etc. So, while user getting logged in into the application we have the data of users role and there permissions.
So, my question is whether we should go for ActionFilter OR Authorize Filter? Initially we have tried with ActionFilter, but my action filter is getting called though the particular action is NOT executed/called.
Action Filter
using Microsoft.AspNetCore.Mvc.Filters;
namespace LMS.Web.Core.Classes
{
public class SecurityFilter : ActionFilterAttribute
{
private string permissionName;
private Permissions permissions;
public SecurityFilter(string m_permissionName)
{
permissionName = m_permissionName;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
}
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
}
}
}
and this action filter I have referred on one action method
[Route("Course/Info")]
[SecurityFilter("some permission name")]
public ActionResult CourseDetails()
{
return View();
}
So, while logging into the application the action filter is getting called. why this is so ?
We want to use the filter on view and controller side. So, basically we are looking like this
[Route("Course/Info")]
[SecurityFilter(PermissionName = "some permission")]
public ActionResult CourseDetails()
{
return View();
}
public class SecurityFilter : ActionFilterAttribute
{
public string PermissionName { get; set; }
public SecurityFilter(SessionServices _session)
{
session = _session;
}
public SecurityFilter()
{
//Unable able to remove the default constructor
// because of compilation error while using the
// attribute in my controller
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (session.GetSession<List<OrganizationUserRolePermission>>("OrganizationUserRolePermission") != null)
{
List<OrganizationUserRolePermission> permissionList = session.GetSession<List<OrganizationUserRolePermission>>("OrganizationUserRolePermission");
checkPermission = permissionList.Any(m => m.PermissionName == PermissionName);
if(!checkPermission)
{
// Redirect to unauthorized access page/error page
}
}
base.OnActionExecuting(context);
}
}
and whatever the permission we passed to the filter will check whether user has the permission OR not. Also, we are trying to inject session service into filter, but getting session null.
I'm not sure about your use case to pass the SessionServices
instance to filter attribute constructor but this is not possible as any
argument to Attribute invocation should be a compile-time constant
value.
Reference
Attribute parameters are restricted to constant values of the following types:
- Simple types (bool, byte, char, short, int, long, float, and double)
- string
- System.Type
- enums
- object (The argument to an attribute parameter of type object must be
a constant value of one of the above types.)
- One-dimensional arrays of any of the above types
Rather you could retrieve the stored session data inside the OnActionExecuting method directly to check the needed permissions.
Ideally Authorize attribute would be more appropriate in your case to check the user permissions to allow access to any view. I believe ActionFilter might be more suitable in case of any logging before/after the action execution.
Regarding the below
So, while logging into the application the action filter is getting called. why this is so ?
Please check the Filter Registration in your application code. Ideally if the filter is applied to any specific action (e.g. CourseDetails in your case) then it will be called only on that particular action execution.
Alternatively please include the Login action in your question so that we could check for the problem if any.
I hope this would help find a solution in your case!
Hello :) I am building an MVC5/EF6 system that has stores information about students with a number of user types . When the user logs in certain information about the user is stored in Session; UserID, UserTypeID etc. Different users have different privileges, and I often need to get the user information from Session within my ActionResult methods in each controller:
private Student GetCurrentStudentInfo()
{
var currentuser = (SessionModel)Session["LoggedInUser"];
var student = _db.Student.Find(currentuser.UserID);
return student;
}
I have this method at the bottom of my controllers, which I call from each method depending on when I need this information. It gets the userID from the current logged in user and returns the profile information. I would like to be able to either:
Make this method available to all my controllers
Or create a class variable that I can use at the top of my controller, which would return this info:
public class RegistrationWizardController : Controller
{
private readonly DefaultContext _db = new DefaultContext();
private UserInfo _userInfo = new UserInfo();
}
I am very new to MVC and coding in general, so any help/opinions/other suggestions are welcome. Thanks!
You have a couple of options.
The first (and easier) of the two is to make all of your controllers inherit from a common base controller. To do this, make a base controller that extends from the default controller:
public class BaseController : Controller
{
protected Student GetCurrentStudentInfo() //protected so we can access this method from derived classes
{
var currentuser = (SessionModel)Session["LoggedInUser"];
var student = _db.Student.Find(currentuser.UserID);
return student;
}
}
Now, change your controllers to inherit the base controller you just created:
public class RegistrationWizardController : BaseController
{
public ActionResult AnAction()
{
var student = this.GetCurrentStudentInfo(); //calls the method inherited from BaseController
}
}
The other option you have is to use Dependency Injection. This is a bit more complicated, and much more abstract than the previous method. There are a bunch of Dependency Injection frameworks, my favorite is Ninject (http://www.ninject.org/). This would probably be closer to the "Industry Standard" of doing something like this, and I would encourage you to at least look into it, but I think a sample would be a little out of scope for this question (do some side reading first). If you do decide to implement it and get stuck, post another question.
I have a controller method which returns a list of resources like this:
[HttpGet, Route("events/{id:int}/rosters")]
public RosterExternalList List(int id, int page = 1, int pageSize = 50) {
return Repository.GetRosters(id).ToExternal(page, pageSize);
}
And the repository method is:
public IQueryable<EventRoster> GetRosters(int id) {
return EventDBContext.EventRosters.Where(x => x.eventID == id).OrderBy(x => x.EventRosterID).AsQueryable();
}
This is the same pattern for multiple list methods. The problem is that when no items are retrieved from the db, the api sends a 200 with an empty response body even if the id passed in is invalid.
What I want is if id = invalid, send appropriate response (404?). Otherwise, if the id is valid, and there are no records, send the 200 with empty body.
My question is - is this the right way to handle this? Can this be done via an Action Filter so it will be implemented across all the methods like this? How?
It is possible with an action filter like this:
public class EventsFilter : ActionFilterAttribute
{
public EventDBContext EventDBContext { get; set; }
public override void OnActionExecuting(HttpActionContext actionContext)
{
bool exists = false;
var routeData = actionContext.Request.GetRouteData();
object value;
if (routeData.Values.TryGetValue("id", out value))
{
int id;
if (int.TryParse(value, out id))
{
exists = EventDBContext.EventRosters.Where(x => x.eventID == id).Any();
}
}
if (exists == false)
{
var response = actionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, "Event not found");
throw new HttpResponseException(response);
}
}
}
You would also need to configure a dependency resolver to set EventDBContext property.
Is it the right way?
It depends on your solution design:
If your business layer is integrated and depends on the Web API (as it appears from your example), then yes it is the way to go.
If Web API (service layer) is not the only one who uses your business layer, then you would want to avoid duplication of event checking in other places and move it to a more generic class than Web API action filter and call this class from your business layer instead of Web API to make sure that regardless of the caller, you would always check if event exists. In that class you would throw something like BusinessLogicNotFoundException with information about event. And on the Web API, you would need to create an ExceptionFilterAttribute that handles BusinessLogicNotFoundException and creates an appropriate 404 response.
Alright, so I have spend LITERALLY five or six hours working on this to no avail. I'm working in C# with ASP.NET MVC 4 and EF 5.0, I believe. Here's my problem: I have a method in my controller takes a custom class as a parameter (these names have been candy-themed obfuscated :P). The ViewError class looks like so:
public class ViewError<T> where T : class
{
public T ModelObject { get; set; }
public string Message { get; set; }
public ViewError(string message, T modelObject)
{
Message = message;
ModelObject = modelObject;
}
public ViewError()
{
Message = null;
ModelObject = default(T);
}
}
(It's a generic so I can figure out what type the ModelObject is from within the code. It's irrelevant, though; I've already tested it taking a generic 'object' and the same problem happens.)
The controller method looks like this. All it does is ask who is eating the candy, and if there's a ViewError, it displays the message. (The view has a paragraph that displays the ViewBag.Message).
public ActionResult EatCandy(ViewError<EatCandyViewModel> error)
{
ViewBag.BrandList = new SelectList(db.Brands, "ID", "Name");
// If there's a notification, pass it to the view, along with the model
if(error.Message != null)
{
ViewBag.Message = error.Message;
return View(error.ModelObject);
}
return View();
}
On the post:
[HttpPost]
public ActionResult EatCandy(EatCandyViewModel viewModel)
{
if(ModelState.IsValid)
{
CandyEater eater = (CandyEater)viewModel;
db.CandyEaters.Add(eater);
db.SaveDatabase(); // Custom convenience wrapper for SaveChanges()
return RedirectToAction("ChooseCandyToEat", eater);
}
return View(viewModel);
}
Pretty standard stuff. Now, in the ChooseCandyToEat method, it brings up a list of candy available to eat of a specific brand. If that brand doesn't have any available candy, I want it to send an error back to the EatCandy method (via ViewError object) telling that eater that they have no candy to eat, and send the model back so that the eater doesn't have to type in their info again, merely select a different brand of candy.
public ActionResult ChooseCandyToEat(CandyEater eater)
{
// Get the list of candy associated with the brand.
IEnumerable<Candy> candyList = db.Candies.Where(b => b.Brand == eater.DesiredBrand)
.Where(e => !e.Eaten);
// If the brand has no candy, return an error message to the view
if(candyList.Count() == 0)
{
EatCandyViewModel viewModel = (EatCandyViewModel)eater;
ViewError<EatCandyViewModel> viewError = new ViewError<EatCandyViewModel>("Oh noes! That brand has no candy to eat!", viewModel.Clone()); // This is a deep clone, but even if it wasn't, it would still be weird. Keep reading.
return RedirectToAction("EatCandy", viewError);
}
return View(candyList);
}
According to my understanding, this should all work. Now here's the weird part - I can confirm, through debug messages and the Watch window (using Visual Studio) that the ViewError is created properly and holds a deep clone of the viewModel in its ModelObject (I have to convert it back because the EatCandy method expects an EatCandyViewModel as a parameter). However, when I step forward and the ViewError is passed to EatCandy, the ModelObject within it is null! I originally thought this was because it only passed a reference of viewModel into the object and it was getting garbage collected, which is why I added the Clone() method. The string, which is also a reference type, is getting through okay, though. Why isn't the object? Does anyone know?
If you need more info, just ask. And disregard the ridiculousness of this database - I obfuscated it intentionally, not just to be silly.
This won't work. RedirectToAction results in a 302 to the browser which just is not capable of carrying complicated models.
There are some alternatives, you could use the TempData to store the model, do the redirect and retrieve the data. You could probably even call the EatCandy action directly instead of redirecting, however the auto matching the view won't work and you'd have to force the "EatCandy" view name at the end of the EatCandy action.
I want to improve current implementation of the ASP.NET MVC Framework.
Current code:
routes.MapRoute(null, "I-want-to-fly", new { controller = "Airport", action = "Fly" });
public class AirportModel
{
public List<Plane> Planes { get; private set; }
public List<Pilot> Pilots { get; private set; }
public void AddFly(Plane plane, Pilot pilot, Passenger passenger)
{
// . . .
}
}
public class AirportController
{
private AirportModel model;
[HttpGet]
public ViewResult Fly(string from, string to)
{
var planes = return (from p in model.Planes
where p.CityFrom == from && p.CityTo == to
select p).ToList();
return View(planes);
}
[HttpPost]
public ActionResult Fly(Plane plane, Passenger passenger, DateTime time)
{
if (!(ModelState.IsValid && plane.TimeOfDeparture == time))
return View();
var pilot = (from p in model.Pilots
where p.Free && p.CanAviate(plane.Id)
select p).First();
model.AddFly(plane, pilot, passenger);
return RedirectToAction("Succeed");
}
}
My proposal:
routes.MapRoute(null, "I-want-to-fly", new { model = "Airport", action = "Fly" });
public class AirportModel
{
private List<Plane> planes;
private List<Pilot> pilots;
private void AddFly(Plane plane, Pilot pilot, Passenger passenger)
{
// . . .
}
[HttpGet]
public ViewResult Fly(string from, string to)
{
var planes = return (from p in model.Planes
where p.CityFrom == from && p.CityTo == to
select p).ToList();
return View(suitablePlanes);
}
[HttpPost]
public ActionResult Fly(Plane plane, Passenger passenger, DateTime time)
{
if (!(ModelState.IsValid && new PlaneController().CanFly(plane, time)))
return View();
var pilot = (from p in pilots
where p.Free && p.CanAviate(plane.Id)
select p).First();
AddFly(plane, pilot, passenger);
return RedirectToAction("Succeed");
}
}
public static class PlaneController
{
public static bool CanFly(Plane plane, DateTime time)
{
return plane.TimeOfDeparture == time; // it will be more complex
}
}
You see, in such way we don't need excessive count of controllers and their methods. Model would create controller only by perforce: mostly to verify user input (not input validation, business validation).
What do you think, can this idea have a continuation? Or, what is wrong with it?
Thanks for your replies!
UPDATE: I noticed, that we need to replace implementations of controller and view as a result of changing the model's state (mostly). So, if model causes to change the implementation, why model cannot do it?
UPDATE 2: It seems to me I explained incorrectly. I don't want model to do all work, of course no! I try to say, that not controller should decide what to do with model and what view is the most suitable for this user request.
Doesn't it strange, that model doesn't know how to visualize itself, but some controller knows?
Doesn't it strange, than we need controller for the GET request, where there is nothing to control?
I try to remove those strangenesses.
UPDATE 3: I understand that it cannot be applied anywhere. The main question is: can it improve some part of current implementations of MVC ? Mostly I'm interested in ASP.NET MVC -- can we
remove redundant controllers or some its methods
work directly with models
using this idea? Is it possible and what are the problems of this idea?
Found problems:
More strong connection between model and view/controller -- but currently I don't think it's a problem. Actually it shows that views and controllers were created in help for the major element -- model.
UPDATE 4: I changed the code, showing "before/after". Maybe this example will be better.
Doesn't this just violate the whole idea of MVC? Your model is separated from your controller and your view. In this way (the way you propose) you would not be able to replace your model by another implementation, or your controller for that matter.
updated I:
You could of course let your model do the part of the controller as well, but from that moment on you're not talking about the MVC design pattern anymore. For MVC, the model does and should not now about the view. That's the controllers job.
The controller receives user input and initiates a response by making calls on model objects. A controller accepts input from the user and instructs the model and viewport to perform actions based on that input.
In the MVC pattern, the Model isn't just fixed to your database model, it could be a combination of your database model and a repository pattern as well, where you implement your business logic.
The biggest problem I see with your proposal is that it makes code non-reusable. I get a model that is tightly coupled with it's views which I really don't want if I want to reuse the model in whatever way I might want to.
Update II
I think your are being mislead by the actual word Controller, I had that thought for a while and your latest comment sort of confirms this for me
Controllers are some objects, that check correspondence of user input to business-logic.
Controllers act upon user input, they might check the user input but their responsibility for checking validity stops there. Business logic goes in the Model (again, the Model as defined by the MVC pattern, not the model as in datamodel). Their main purpose is deciding what View to display.
Also from one of your latest comments:
How do you think, if [asp.net mvc] would be developed in my way, would it solve problem of redundant controllers?
Asp.Net MVC follows the MVC design pattern. Your proposal does not. It seem more like a ModelControlled View pattern, just to coin a name. Also, there are no redundant controllers, the controllers are no problem, they are an integral part of the solution.
And an effort to simplistically clarify what I mean with a code example:
namespace DataProject.Model
{
public class AirportModel
{
public List<Plane> Planes { get; set; }
public List<Pilot> Pilots { get; set; }
public List<Passenger> Passengers { get; set; }
public List<Flight> Flights { get; set; }
}
}
namespace SomeProject.Repository
{
public class AirportRepository
{
private DataProject.Model.AirportModel model;
//constructor sets the model somehow
public bool AddFlight(Plane plane, List<Passenger> passengers, DateTime time)
{
//Business logic
if (plane.TimeOfDeparture != time) return false;
var pilot = (from p in model.Pilots
where p.Free &&
p.CanAviate(plane.Id)
select p).FirstOrDefault();
//More Business logic
if (pilot == null) return false;
//Add plane, pilot and passenger to database
model.Flights.add(new Flight{Pilot = pilot, Plane = plane, Passengers = passengers});
//Even here you could decide to do some error handling, since you could get errors from database restrictions
model.Save();
return true;
}
public List<Planes> GetPlanes(string from, string to)
{
return (from p in model.Planes
where p.CityFrom == from && p.CityTo == to
select p).ToList();
}
}
}
namespace MVCApp.Controllers
{
public class AirportController
{
private SomeProject.Repository.AirportRepository repository;
[HttpGet]
public ViewResult Fly(string from, string to)
{
var viewModel = repository.GetPlanes(from, to);
return View(viewModel);
}
[HttpPost]
public ActionResult Fly(Plane plane, List<Passenger> passengers, DateTime time)
{
if (!ModelState.IsValid) return View();
if (!repository.AddFlight(plane, pilot, passenger)) return View();
return RedirectToAction("Succeed");
}
}
}
No offense intended but how exactly is this an improvement?
So you've made a class called a PersonModel that isn't really doing "model things" at all - it is doing the work that Controllers do - you've got it handling gets and posts and calling out for the display of Views and then you've got a static "Controller" that really controlling nothing and is concerning itself with business logic. Honestly, I don't get how this is an improvement.
A concrete example of is you've got a controller checking whether Age >= 18, which is a very "business rules" thing for a controller to be doing. That's not the purpose of a controller. That's the job of a model object - to concern itself with things like business logic. Controllers, as one person put it, are more of an electronic curator. In your example, you've relegated it to something far less than a curator.
There are distinct roles that objects play in an MVC application. Views show us stuff and provide us with ways to interact with the application. Controllers handle the input coming from the View and serve up views that are needed. Models provides a place to put data and the logic and business rules that the model encompasses. Services handle things like persisting data to some store, like a DB.
You can do everything in one class without controllers at all but you want "separte of concerns"
So the controller is responsible for the http request and validation and the model is responsible only for the data.
You are the programmer then you could agree or disagree with MVC pattern.
But the pattern which you described doesn't support the separation of concern and it breaks out the whole idea about MVC
This has nothing to do with MVC.
This is 'MVNothing'
:)
Just kidding *_^