What conforming standards is this code violating? - c#

What is wrong with having a private ViewModel object, so that all my controller actions have access to it?
I'm using EF4,MVC3,DBContext, DBsets.
public class MyController {
private MyViewModel _myViewModel;
public ActionResult Index(MyViewModel myViewModel){ <-- There is a model Binder making this work
_myViewModel = myViewModel;
return _myViewModel;
}
}

Because everytime you call a controller action you get a different instance of the controller. So anything that you might have stored in instance fields of this controller from a previous action would be lost on subsequent actions. That's the reason why in ASP.NET MVC you have notions such as Session, Application State, TempData, Cookies, Cache, ... you name it.

For one thing, by making the ViewModel a member of your controller, you really limit what you can do in a multi-threaded environment. If more than 1 of your Actions is invoked on the same controller, you may end up with a race condition to determine which of the passed-in view models is used for each view.
Edit: You get a different instance of the controller each time, so this will not happen. However, it is still something to keep in mind for other classes that use private members

Related

Controller Property is not consistent among different HttpGet methods in ASP.NET Core MVC

I have a Controller that has two action methods used by a page:
The standard OnGet, that is called, when the page is visited in browser and another method SortData that is called using an anchor tag.
Both of these methods interact with the same property of the controller, but there seem to be like two different object references to the property for each of the methods, because the data inside SortData is inconsistent with the date in OnGet.
Also, if I won't initialize that property inside the Controller's constructor but inside the OnGet method, it will be null inside SortData, even though OnGet has been called first.
My question is how do I get SortData to use the same Property that OnGet uses?
The code looks like this:
public class MyController : Controller
{
private MyClass Property {get; set;}
public SearchController()
{
Property = new MyClass()
}
[HttpGet("sortdata")]
public IActionResult SortData(string sortAttribute)
{
Property.SortData(sortAttribute);
return View("Index", Property);
}
public IActionResult OnGet([FromQuery]string requeststring)
{
ViewData["Message"] = requeststring;
Property.Datas = requeststring == null ?
searchService.GetAll() :
searchService.searchByRequestString(requeststring);
return View("Index", Property);
}
}
Of course, the service used in OnGet is being initialized in the Controller as well, but I removed it from the example to keep it rather simple.
So you can see that OnGet modifies Property and returns the Index page. At that page, there is an anchor that calls SortData which also modifies Property. But it is not the same Property as in OnGet, it is still at it's state of initialization. But I want the actual Property to be modified and then return the Index page with sorted data.
Yes, this is correct behavior. Each web request lives in it’s own context. you have two web requests, it instantiates a new controller for each request, so of course you do not have the same instance of the property.
Now that you know that this is the correct behavior, you will want ask a new question, which will be how do you share a property across multiple requests. That, however is the wrong question. The answer to that is “you don’t”. web requests should be stateless. instead, you should add your sort parameters to the query parameters.

Passing a complex object to the view and posting it back

I'm working on a MVC 5 application.
I've got an action taking a complex object as a parameter. This action generates a view, and this view calls another action, that needs this complex object to be passed to it.
What's the bast way to pass it along, avoiding storing it in the session (I know, that would be the simplest to do, but one of the project requirements is to avoid using session except for security things) ?
Example controller:
public class TestController {
public ActionResult Action1(ComplexObject object)
{
TestModel model = new TestModel
{
model.ComplexObject = complexObject
};
return View(model);
}
[HttpPost]
public ActionResult Action2(TestModel model)
{
DoSomeStuff(model.ComplexObject);
return View(model);
}
}
So the problem is to store this complex object somewhere (again, not in session) to be able to retrieve it in the Action2 the way it existed in Action1. I currently add it to the model, but it's not mandatory, as it is not used in the view.
I thought of creating hidden fields in the view, but storing everything from the object is a real pain in the ass, it's ugly, and would introduce errors if the object structure changes in the future.
Is there a good practice to do something like this ? Or is there a elegant way to somehow serialize and deserialize it ? Any idea welcome.
If your TestModel definition has a ComplexObject (which I imagine it does because you are assigning the ComplexObject value to it), when you pass that model to the view, as you are doing in Action1 your view will receive it as long as you have strongly typed your view as such:
#model = TestModel //simplified path since I do not know the full path of your model
Your Action2 method is an HttpPost which makes me conclude that you will have a user post the form on the view. When the form is posted, your strongly typed view will pass the model into your Action2 method. If you use any HTML helpers to modify any of the properties of TestModel, then those changes will be passed to your Action2 method as well.

Need to access a variable from all Views

On the site I'm working on, there are users with different permissions on the site.
Given the schedule ID and employee ID that we're currently looking at, we can get their role-specific permissions.
Right now, our BaseModel has a property that properly accesses the DB and grabs this info.
For all views that pass a model to the view, everything runs fine.
The problem lies in Controller Methods where no model is passed. In a few views, all they're supplied is a few ViewBag entries, and work fine.
However, I /need/ the CurrentPermissions property in those pages nonetheless, for the layout. Whether or not the permissions have one boolean value set true/false, something may/may not be displayed/populated.
So, my option seem to be:
Somehow throw my CurrentPermissions into a ViewBag entry for all views, and access them through that instead of the base model.
I'm not sure how to do this. I've seen people using OnActionExecuting, but that fails since my connection to TransactionManager is not yet set up at that point.
Somehow throw just the BaseModel into those views that don't currently pass a model. I'm refraining from this as much as possible. I'm not sure how I would go about doing such, but it seems like that would over-complicate the situation.
How can I go about pushing this CurrentPermissions object (generated from a call to my TransactionManager) to every view (specifically, the Layouts!)
Your approach is what we use in out projects... and we use this approach to systematically remove the use of ViewBag changing it to ViewModels.
Other approach we have used (for UserPreferences in my case) is adding an ActionFilter that ends including the preference in the ViewBag. You decorate the actions needing it with [IncludePreferences] in my case (that is the name of my filter attribute.
EDIT ActionFilter:
public class IncludePreferencesAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller as BaseController;
// IController is not necessarily a Controller
if (controller != null)
{
//I have my preferences in the BaseController
//and cached but here you can query the DB
controller.ViewBag.MyPreferences = controller.TenantPreferences;
}
}
}
In you action you decorate it using [IncludePreferences]
As a temporary solution, I'm doing the following at the top of my Layout:
#{ OurModel.SupervisorRestriction CurrentSupervisorRestrictions = ViewBag.CurrentSupervisorRestrictions ?? Model.CurrentSupervisorRestrictions; }
This way, if we're passing in an object then it works just fine. Otherwise, I'll directly pass in a ViewBag.CurrentSupervisorRestrictions from the controller. There are only a few cases, so it's not that bad.
Better suggestions would be great, though.

ASP.NET MVC common model for different controller methods

I am having a hard time understanding the way MVC behaves. In my Controller class I created a model and initialized it the main ActionMethod. After that I call another controller method and it turns out that the model is null. Why is that?
Now it seems I can only use them once for passing database info to the views. Is it necessary to always modify/query the database? I know in most cases it makes sense to do it, but I would like to keep these parts separate.
Edit:
Here's some code:
public class TestController : Controller {
TestModel model;
public ActionResult Index() {
model = new TestModel();
return View(model);
}
public ActionResult OtherMethod {
// Here I would like to access/modify the previously created model, but it is null
return View();
}
}
You should use Html helpers to bind properties of your model in view
or simply use #Html.EditorForModel() helper. It will create UI and binding for entire model
the default lifetime of controller is "per request" this means that a new instance of your TestController is created on every HttpRequest.
you could change this behavior by creating a custom controller factory but i would not recommend that, i suggest u use the TempData collection instead.
see http://msdn.microsoft.com/en-us/library/dd394711(v=vs.100).aspx

How does ViewBag in ASP.NET MVC work behind the scenes?

I am reading a book on ASP.NET MVC and I'm wondering how the following example works:
Example #1
Controller
public class MyController : Controller
{
public ActionResult Index()
{
ViewBag.MyProperty = 5;
return View();
}
}
View
<h1>#ViewBag.MyProperty</h1>
Now I understand that ViewBag is a dynamic object, so that's how you can set the property (though I don't know much about dynamic objects, never worked with them.) But how does the view get the specific instance of the ViewBag from the controller, even though we don't pass anything directly?
I thought that the ViewBag could be a public static object, but then any change to it would be global and it wouldn't be specific to a view instance.
Could you elaborate as to how this works behind the scenes?
Example #2
Controller
public class MyController : Controller
{
public ActionResult Index()
{
ViewBag.MyProperty = 5;
return View();
}
public ActionResult Index2()
{
ViewBag.MyProperty = 6;
return View();
}
}
Now let's say the Index method is called first, and then the Index2. In the end the value of ViewBag.MyProperty will end up as 6 (the value from Index2). I feel that it is not a good thing to do, but at the same time I feel that I'm thinking in desktop development terms. Maybe it doesn't matter when used with ASP.NET MVC, as the web is stateless. Is this the case?
ViewBag is a property of ControllerBase, which all controllers must inherit from. It's a dynamic object, that's why you can add new properties to it without getting compile time errors.
It's not static, it's a member of the object. During the request lifetime, the controller instance is created and disposed, so you won't have "concurrency" problems, like overwriting the value.
The View (and its variants) method is not static as well, and this is how the view receives the ViewBag values: during the process of rendering the view, the controller instance has its ViewBag instance as well.
If you would analyse ControllerBase class you would see that ViewBag property is a "proxy" to ViewData property just to make your source look nicer. (I even remember Scott Hanselman taking interview from Phil Haack where Phil introduced ViewBag property as a shortcut to ViewData and eliminating the need of repeated square brackets and quotes). Even though ViewBag property is exposed as dynamic object it implements a DynamicViewDataDictionary class which works directly with ViewData.
Looking at source code of Controller class you can find this method:
protected internal virtual ViewResult View(string viewName, string masterName, object model)
So basically when you call return View(); from your controller it creates a new instance of ActionResult class passing ViewData from controller to it's constructor. Instance of ActionResult is then passed to a particular view engine (ASPX, Razor) so it could be used to render a view in question.
Making ViewBag/ViewData public static could be harmful. Each web request to your MVC application creates a new instance of controller. If you'd have ViewData/ViewBag as public static then two concurrent users would share same data from ViewBag/ViewData.
Here is a video. Discussion on ViewBag (formder ViewModel) starts at 04:05
ViewBag is a property of ControllerBase. It is defined as follows:
public Object ViewBag { get; }
Note that this signature is actually incorrect. Here's what the source code actually looks like:
public dynamic ViewBag {
get {
if (_dynamicViewDataDictionary == null) {
_dynamicViewDataDictionary = new DynamicViewDataDictionary(() => ViewData);
}
return _dynamicViewDataDictionary;
}
}
_dynamicViewDataDictionary is an ExpandoObject; you can add properties to it at runtime. Its lifetime is the same as that of the controller, which is the lifetime of the HTTP request.

Categories

Resources