When I access ViewData inside a method in the controller, I am able to assign the value in the form of dictionary ie.
ViewData["message"]="this is a custom message";
but I got into a scenario where I was trying to handle the Exception in MVC, here is my code:
public void OnException(ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled && (filterContext.Exception is ArgumentOutOfRangeException))
{
filterContext.Result = new ViewResult { ViewName = "Error", ViewData = };
filterContext.ExceptionHandled = true;
}
}
Now when handling exception i would like to pass a message to the error page, so tried to access the Result property of the ExceptionContext.
Now
my question is why am I not able to assign a value to the ViewData in
a dictionary-like a format here
filterContext.Result = new ViewResult { ViewName = "Error", ViewData = };
This is also a property returning a ViewDataDictionary object, when am I able to assign a value in the Controller method like this ViewData["message"] = "argument exception error"; the why am I not able to do the same inside the ViewResult object.
I tried it myself and got an understanding on the inner workings of the MVC frameWork, please correct me if I am wrong and please provide an explanation for it, which would make to learn more about the framework.
When I access ViewData inside a method in the controller, I am able to
assign the value in the form of dictionary
This is because when we call the controller and the method, MVC takes responsibilty to assign objects to all the properties, thats the reason we could assign value for the ViewData inside the method.
filterContext.Result = new ViewResult { ViewName = "Error", ViewData =
};
When we are dealing with the ViewData property of the ViewResult class, we cannot assign the value, in this way ViewData["key"]="some value" because it requires ViewDataDictionary object. however we can do this to assign the value like this
var d = new ViewDataDictionary();
d["key"] = "some value";
filterContext.Result = new ViewResult { ViewName = "Error",ViewData=d };
Related
I'm trying to write unit tests for a MVC application. im trying to test if my controller returns the correct view name.
This is the controller action im testing:
public IActionResult Index(string reportcode)
{
if(string.IsNullOrEmpty(reportcode))
ReportCode = reportcode;
ViewBag.GridSource = GetReportData(reportcode);
return View("Index");
}
This is my Unittest:
[Test]
public void Index_ReturnCorrectView()
{
var controller = new HomeController();
var result = controller.Index("COMD") as ViewResult;
Assert.AreEqual("Index", result.ViewName);
}
The error i get from the unit test is expected "Index" but got null.
I did a lot of search and most answers say the ViewName property should be set after you declare it when returning the view. I tried the same but it wont work still.
Thank you
The documentation for Controller.View() states:
This method overload of the View class returns a ViewResult object
that has an empty ViewName property. If you are writing unit tests for
controller actions, take into account the empty ViewName property for
unit tests that do not take a string view name.
At run time, if the ViewName property is empty, the current action
name is used in place of the ViewName property.
So when expecting a view with the same name as the current action we can just test that it's an empty string.
Alternatively, the Controller.View(ViewName, Model) method will set the ViewName.
My Controller Method
public ActionResult Index()
{
return View("Index");
}
Test Method
[TestMethod]
public void Index()
{
// Arrange
HomeController controller = new HomeController();
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsTrue(result.ViewName == "Index");
}
I know that there are some types of handling exception on MVC. I choosed creating a base controller and overriding the OnException method. Everything is fine but in one of my controllers i have an post action name different from the view name like below
[HttpPost]
public ActionResult Kaydet(PersonelModel model)
{
var personel = new Personel();
SimpleMapper.PropertyMap(model,personel);
_personelService.Ekle(personel);
model.Id = personel.Id;
model.UyariBilgisi.BildirimTipi=BildirimTipi.Bilgi;
model.UyariBilgisi.UyariMetni = "Kayıt başarıyla eklendi.";
return View("PersonelDetay", model);
}
the view name is PersonelDetay.
Here is my OnException Method
protected override void OnException(ExceptionContext filterContext)
{
Exception exception = filterContext.Exception;
if (exception is NotificationException)
{
ViewModelBase viewModelBase = new ViewModelBase()
{
UyariBilgisi = new UyariBilgisi() { UyariMetni = exception.Message, BildirimTipi = BildirimTipi.Uyari }
};
filterContext.Result = View(viewModelBase);
filterContext.ExceptionHandled = true;
}
}
When the Kaydet method gets an notification exception OnException method works and tries to return "Kaydet" view but there is no view named "Kaydet"
To solve this problem i nedd the view name in OnException event. How can i get viewname? or do i thinking wrong? is there a better way a best practice ?
You could try this...
In your controller add the member variable Dictionary<string, string> _viewNames and then within your constructor initialize the collection using an action name as the key and the name of the view as the value:
_viewNames = new Dictionary<string, string>
{
{ "Kaydet", "PersonelDetay" }
}
Then create a method that will return a view name for the current action:
private string GetViewName()
{
return _viewNames[this.ControllerContext.RouteData.Values["action"].ToString()];
}
Change the return statement in the action to:
return View(GetViewName(), model)
and in OnException replace filterContext.Result with:
filterContext.Result = View(GetViewName(), viewModelBase);
Now this isn't ideal for a number of reasons, e.g. actions returning different views, action name changes have to be reflected when initializing the collection, etc, etc, but it may be useful for your current situation.
If there was a way to register different view names with MVC rather than pass them into the View method, then you could have OnException simply call View(viewModelBase) and have it use your custom named view, from any method, based on the action currently being executed.
I need your help.
I tried to pass object form the view to the controller using ViewData.Model
this is the index method in the controller
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
dynamic stronglytyped = new { Amount = 10, Size = 20 };
List<dynamic> ListOfAnynomous = new List<object> { new { amount = 10 } };
ViewData.Model = ListOfAnynomous[0];
return View();
}
and this is the view part
<div>
#Model.amount
</div>
this is the erro
'object' does not contain a definition for 'amount'
please could anyone help me.
The exception is thrown because you passing an anonymous object. Anonymous types are internal, so their properties can't be seen outside their defining assembly. This article gives a good explanation.
While you can use the html helpers to render the properties, for example
#Html.DisplayFor("amount")
you will also lose intellisense and you app will be difficult to debug.
Instead use a view model to represent what you want to display/edit and pass the model to the view.
Your code is wrong.
If you want to use the model object you have pass it to the view:
return View(ListOfAnynomous[0]);
After you will be able to use the "Model" property.
ViewData is another container not related to the model property.
In the end your method will look like this:
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
dynamic stronglytyped = new { Amount = 10, Size = 20 };
List<dynamic> ListOfAnynomous = new List<object> { new { amount = 10 } };
// ViewData.Model = ListOfAnynomous[0];
return View(ListOfAnynomous[0]);
}
I have some filters set up on an MVC C# framework. From here I am try to render an error page. The error page renders correctly, but I want to pass data from the HandleUnautorizedRequest (that depends on which filter you fail) so far I have this. Is there any way to do something like this, but pass data over to the error page I have in shared. I have already unsuccessfully tried to use ViewData in the object constructor, but I might have just been doing it wrong.
The way our code base is structured I can't initialize any of my controllers from here either.
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new ViewResult
{
ViewName = "Error"
};
}
Not sure how you tried to use ViewData, but you could give this a shot:
filterContext.Result = new ViewResult()
{
ViewName = "test",
ViewData = new ViewDataDictionary()
{
{ "key", "value"}
}
};
I've created an OnActionExecuted filter to populate some viewmodel attributes with data from db (I'm not using ViewData["somekey"], preferring many ViewModels descending from a common ancestor).
public class BaseController : Controller
{
protected DataClassesDataContext context = new DataClassesDataContext();
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
ViewModel model = (ViewModel) ViewData.Model;
model.IsUserAuthenticated = filterContext.HttpContext.User.Identity.IsAuthenticated;
if (model.IsUserAuthenticated)
{
model.UserName = filterContext.HttpContext.User.Identity.Name;
}
model.CommonAttribute = from c in context.Something select new SomethingElse() {...};
}
}
The problem is that when an action results in a redirect or a 404 error, OnActionExecuted tries to access ViewModel, which has not been initialized. Also, it's completely useless to fill those values, as they will not be used, since another action is going to be called.
How can I avoid filling viewodel on redirect?
A trivial solution would be to not fill in the model when it doesn't exist:
ViewModel model = ViewData.Model as ViewModel;
if (model != null)
{
model.IsUserAuthenticated = filterContext.HttpContext.User.Identity.IsAuthenticated;
if (model.IsUserAuthenticated)
{
model.UserName = filterContext.HttpContext.User.Identity.Name;
}
model.CommonAttribute = from c in context.Something select new SomethingElse() {...};
}