How to get controller name in another class? - asp.net-mvc

I am developing MVC application.
I want to pass controller to some other class for validation purpose.
After passing the controller, I am unable to get the controller name in that class.
[HttpPost]
public ActionResult Create(Location location)
{
if (ModelState.IsValid)
{
Validations v = new Validations();
boolean b;
//passing controller in another class's method
b = v.ValidProperty(location);
if (ValidProperties == true)
{
db.Locations.Add(location);
db.SaveChanges();
return RedirectToAction("Index");
}
}
}
Getting controller in below method
public void ValidProperty(object Controller)
{
//Gives an error in below line
string CtrName = (string)Controller.ToString;
}
How to get the controller Name ?

b = v.ValidProperty(ControllerContext);
you may be wondering where am I initializing ControllerContext variable.
well you don't have to
public void ValidProperty(ControllerContext ControllerContext)
{
// do your logic here.
}

You should call ControllerContext.RouteContext.GetRequiredString("controller")

To get the name of the controller, you can just use
RouteData.Values["controller"]

Related

Mvc OnException when Viewname and Action name is different

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.

MVC4 accessing method of one controller to the other

I have some inbuilt method in BootstrapBaseController.cs like
public void Success(string message)
{
TempData.Add(Alerts.SUCCESS, message);
}
I haven't used it yet but I guess it shows some success message that i pass to this method.
I want to use this method in my other controller called PortalController. In following method I want to show success message when a user is added to database.
Suggestions?
[HttpPost]
public ActionResult Register(RegisterModel user)
{
if (ModelState.IsValid && (user.Password == user.ConfirmPassword))
{
var regUser = _db.Users.Create();
regUser.UserName = user.UserName;
regUser.Password = user.Password;
_db.Users.Add(regUser);
_db.SaveChanges();
}
return View();
}
You'll need to make PortalController inherits from BootstrapBaseController.
public class PortalController : BootstrapBaseController
If this is not an option you may need to move that common method to a third class used by both controllers.
Make PortalController inherit from BootstrapBaseController
Before returning the View call Sucess and set the message
On your _layout.cshtml view (and I'm assuming all your views inherit use this layout) check for that temp variable and display your message
Updated Controller method:
[HttpPost]
public ActionResult Register(RegisterModel user)
{
if (ModelState.IsValid && (user.Password == user.ConfirmPassword))
{
var regUser = _db.Users.Create();
regUser.UserName = user.UserName;
regUser.Password = user.Password;
_db.Users.Add(regUser);
_db.SaveChanges();
}
Success("User Created successfully");
return View();
}
_Layout.cshtml
#if (TempData["VariableName"] != null)
{
<div>#TempData["VariableName"]</div>
}

Asp.net mvc what is the best practice of rebuilding ViewModel?

On POST , if validation failed and before sending back the ViewModel to the same View with Model State errors, do you rebuild ViewModel for all SelectLists, ReadOnly fields etc?
right now I have separate methods for Fill First Time(for GET Edit-Method) / Rebuild ViewModels from domain objects, what is the best practice so I can be DRY and also not have to change two methods any time I add a new readonly property to ViewModel?
My Solution: Followed this Pattern
Followed pattern suggested here: https://stackoverflow.com/a/2775656/57132
In IModelBuilder Implementation
Build(..)
{
var viewModel = new ViewModel();
// and Fill all Non-ReadOnly fields
...
...
call CompleteViewModel(viewModel)
}
CompleteViewModel(ViewModel viewModel)
{
//Fill all ReadOnly & SelectLists
...
}
The reason I went with this solution is because I don't want to store stuff on server to retrieve across the HTTP Requests
I don't rebuild it, because I don't stay at POST. I follow POST-REDIRECT-GET pattern, so if I post to /User/Edit/1 using POST HTTP method, I get redirected to /User/Edit/1 uasing GET.
ModelState is transferred to TempData to follow Post-Redirect-Get and be availabe at GET call. View model is built in one place, at GET call. Example:
[HttpPost]
[ExportModelStateToTempData]
public ActionResult Edit(int id, SomeVM postedModel)
{
if (ModelState.IsValid) {
//do something with postedModel and then go back to list
return RedirectToAction(ControllerActions.List);
}
//return back to edit, because there was an error
return RedirectToAction(ControllerActions.Edit, new { id });
}
[ImportModelStateFromTempData]
public ActionResult Edit(int id)
{
var model = //create model here
return View(ControllerActions.Edit, model);
}
This is code for attributes importing/exporting ModelState:
public abstract class ModelStateTempDataTransferAttribute : ActionFilterAttribute
{
protected static readonly string Key = typeof(ModelStateTempDataTransferAttribute).FullName;
}
public class ExportModelStateToTempDataAttribute : ModelStateTempDataTransferAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Only export when ModelState is not valid
if (!filterContext.Controller.ViewData.ModelState.IsValid)
{
//Export if we are redirecting
if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult))
{
filterContext.Controller.TempData[Key] = filterContext.Controller.ViewData.ModelState;
}
}
base.OnActionExecuted(filterContext);
}
}
public class ImportModelStateFromTempDataAttribute : ModelStateTempDataTransferAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ModelStateDictionary modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary;
if (modelState != null)
{
//Only Import if we are viewing
if (filterContext.Result is ViewResult)
{
filterContext.Controller.ViewData.ModelState.Merge(modelState);
}
else
{
//Otherwise remove it.
filterContext.Controller.TempData.Remove(Key);
}
}
base.OnActionExecuted(filterContext);
}
}
The simplest solution would be to pass in you viewModel to the method and account for null
private MyViewModel BuildViewModel(MyViewModel model = null)
{
model = model ?? new MyViewModel();
model.ReadOnlyList = new .....
.
.
return model;
}
for Create:
var model = BuildViewModel();
for rebuild:
model = buildViewModel(model);
I like #LukLed's answer above - it looks very interesting. If you want another option, here's what I currently do.
In my service layer, I have a method to build my view model. I call that on GET and return the the view model to the view. On POST, I build the model from the incoming ID and then TryUpdateModel(model). From there, you can do whatever you like (save, check model state, etc.). With this method, you only have 1 build method and only have to update it once if your model changes (i.e. add/remove properties in the future, etc.).
[HttpGet]
public ActionResult AssessFocuses(int apaID)
{
var model = this.apaService.BuildAssessFocusesViewModel(apaID);
return this.View(model);
}
[HttpPost]
public ActionResult AssessFocuses(int apaID, string button)
{
var model = this.apaService.BuildAssessFocusesViewModel(apaID);
this.TryUpdateModel(model);
switch (button)
{
case ButtonSubmitValues.Back:
case ButtonSubmitValues.Next:
case ButtonSubmitValues.Save:
case ButtonSubmitValues.SaveAndClose:
{
try
{
this.apaService.SaveFocusResults(model);
}
catch (ModelStateException<AssessFocusesViewModel> mse)
{
mse.ApplyTo(this.ModelState);
}
if (!this.ModelState.IsValid)
{
this.ShowErrorMessage(Resources.ErrorMsg_WEB_ValidationSummaryTitle);
return this.View(model);
}
break;
}
default:
throw new InvalidOperationException(string.Format(Resources.ErrorMsg_WEB_InvalidButton, button));
}
switch (button)
{
case ButtonSubmitValues.Back:
return this.RedirectToActionFor<APAController>(c => c.EnterRecommendationsPartner(model.ApaID));
case ButtonSubmitValues.Next:
return this.RedirectToActionFor<APAController>(c => c.AssessCompetenciesPartner(model.ApaID));
case ButtonSubmitValues.Save:
this.ShowSuccessMessage(Resources.Msg_WEB_NotifyBarSuccessGeneral);
return this.RedirectToActionFor<APAController>(c => c.AssessFocuses(model.ApaID));
case ButtonSubmitValues.SaveAndClose:
default:
return this.RedirectToActionFor<UtilityController>(c => c.CloseWindow());
}
}

Can we call the Method of a controller from another controller in asp.net MVC?

Can we call the Method of a controller from another controller in asp.net MVC?
You could also simply redirect straight to the method like so:
public class ThisController
{
public ActionResult Index()
{
return RedirectToAction("OtherMethod", "OtherController");
}
}
Technically, yes. You can call a static method of a controller or initialize an instance of a controller to call its instance methods.
This, however, makes little sense. The methods of a controller are meant to be invoked by routing engine indirectly. If you feel the need to directly call an action method of another controller, it is a sign you need some redesign to do.
Well, there are number of ways to actually call an instance method on another controller or call a static method off that controller type:
public class ThisController {
public ActionResult Index() {
var other = new OtherController();
other.OtherMethod();
//OR
OtherController.OtherStaticMethod();
}
}
You could also redirect to to another controller, which makes more sense.
public class ThisController {
public ActionResult Index() {
return RedirectToRoute(new {controller = "Other", action = "OtherMethod"});
}
}
Or you could just refactor the common code into its own class, which makes even more sense.
public class OtherClass {
public void OtherMethod() {
//functionality
}
}
public class ThisController {
public ActionResult Index() {
var other = new OtherClass();
other.OtherMethod();
}
}
Try This.
var ctrl= new MyController();
ctrl.ControllerContext = ControllerContext;
//call action
return ctrl.Action();
As controllers are just classes: Yes, we can do it. We can do it by some of the following ways:
By directly redirecting- return RedirectToAction("MethodName", "ControllerName");
By creating object - ControllerName objController=new ControllerName();
objController.methodName(parameters)
Yes, you can call a method of another controller.
public ActionResult Index()
{
AccountController accountController = new AccountController {ControllerContext = ControllerContext};
return accountController.Index();
}
The controller is also a simple class. Only things are that its inheriting Controller Class. You can create an object of the controller, but it will not work for Routing if you want to redirect to another page.

Does a view exist in ASP.NET MVC?

Is it possible to determine if a specific view name exists from within a controller before rendering the view?
I have a requirement to dynamically determine the name of the view to render. If a view exists with that name then I need to render that view. If there is no view by the custom name then I need to render a default view.
I'd like to do something similar to the following code within my controller:
public ActionResult Index()
{
var name = SomeMethodToGetViewName();
// The 'ViewExists' method is what I've been unable to find.
if (ViewExists(name))
{
retun View(name);
}
else
{
return View();
}
}
private bool ViewExists(string name)
{
ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, name, null);
return (result.View != null);
}
For those looking for a copy/paste extension method:
public static class ControllerExtensions
{
public static bool ViewExists(this Controller controller, string name)
{
ViewEngineResult result = ViewEngines.Engines.FindView(controller.ControllerContext, name, null);
return (result.View != null);
}
}
What about trying something like the following assuming you are using only one view engine:
bool viewExists = ViewEngines.Engines[0].FindView(ControllerContext, "ViewName", "MasterName", false) != null;
Here's another [not necessarily recommended] way of doing it
try
{
#Html.Partial("Category/SearchPanel/" + Model.CategoryKey)
}
catch (InvalidOperationException) { }
In asp.net core 2.x and aspnet6 the ViewEngines property no longer exists so we have to use the ICompositeViewEngine service. This a variant of the accepted answer using dependency injection:
public class DemoController : Controller
{
private readonly IViewEngine _viewEngine;
public DemoController(ICompositeViewEngine viewEngine)
{
_viewEngine = viewEngine;
}
private bool ViewExists(string name)
{
ViewEngineResult viewEngineResult = _viewEngine.FindView(ControllerContext, name, true);
return viewEngineResult?.View != null;
}
public ActionResult Index() ...
}
For the curious: The base interface IViewEngine is not registered as a service so we must inject ICompositeViewEngine instead. The FindView() method however is provided by IViewEngine so the member variable may use the base interface.
If you want to re-use this across multiple controller actions, building on the solution given by Dave, you can define a custom view result as follows:
public class CustomViewResult : ViewResult
{
protected override ViewEngineResult FindView(ControllerContext context)
{
string name = SomeMethodToGetViewName();
ViewEngineResult result = ViewEngines.Engines.FindView(context, name, null);
if (result.View != null)
{
return result;
}
return base.FindView(context);
}
...
}
Then in your action simply return an instance of your custom view:
public ActionResult Index()
{
return new CustomViewResult();
}
ViewEngines.Engines.FindView(ViewContext.Controller.ControllerContext, "View Name").View != null
My 2 cents.
Here's how to do it in Razor for Core 2.2 etc. Note that the call is "GetView", not "Find View)
#using Microsoft.AspNetCore.Mvc.ViewEngines
#inject ICompositeViewEngine Engine
...
#if (Engine.GetView(scriptName, scriptName, isMainPage: false).Success)
{
#await Html.PartialAsync(scriptName)
}

Resources