How to pass Viewdata value to masterpage in Razor MVC4 - c#

I use Directoryservices for login in My page. I need to pass the username to my masterpage to display the username in all the pages.
I got the username and stored it in a ViewData. How to pass the viewdata value in masterpage.
My code :
[HttpPost]
public ActionResult Index(LoginModels model)
{
if (ModelState.IsValid)
{
string DisplayUserName = string.Empty;
string LoginUser = model.Userid;
string LoginPassword = model.Password;
string name = model.UserName
if (ValidateActiveDirectoryLogin(LoginUser, LoginPassword, out DisplayUserName) == true)
{
model.UserName = DisplayUserName;
ViewData["UserName"] = "Welcome" + DisplayUserName;
return RedirectToAction("Index", "MPP", new { UserID = LoginUser });
}
else
{
ModelState.AddModelError("","Invalid Username or Password");
}
}
return View();
}
In Layout page :
#{ #ViewData["UserName"] }
I tried the following way to display the Username. But it throws nullexception.
EDIT :
#foreach (var m in IEnumerable<SampleECommerce.Models.LoginModels>)ViewData["UserName"])
{
#m.UserName
}

There are some misunderstandings, like if you set ViewData["UserName"] to a string value you get a IEnumerable<SampleECommerce.Models.LoginModels>. Here is another solution:
Put this to layout page:
<span>#{Html.RenderAction("actionname", "controllername");}</span>
And in related action:
public ActionResult actionname() {
string result = getusername();
return Content(result);
}
[NoneAction]
private string getusername(){
return (Membership.GetUser()!= null) ? Membership.GetUser().UserName : "Guest";
}

Try it without the extra #, i.e.
#{ ViewData["UserName"] }

Firs you need to change your syntax to:
#(ViewData["UserName"])
That's probably the best (of a bad bunch). Realistically you should be looking to push your user into the User property of your pages via the User property of your controllers (typically in an authorization attribute where, perhaps, you read a cookie) - that way you don't rely on type-unsafe ViewData and magic strings for something that you're going to be using on every page.
But anyway... if the view is rendering because of the last return View(); line then what you're trying to do will work if you change your syntax as I've shown.
If not, and it's when you do return RedirectToAction("Index", "MPP", new { UserID = LoginUser }); then you need to push the UserName into TempData and then read it back at the start of the Index action on your MPP controller:
So:
TempData["UserName"] = "Welcome " + DisplayUserName;
return RedirectToAction("Index", "MPP", new { UserID = LoginUser });
And then at the start of your Index method you need to pull the value back out of TempData:
public class MPPController {
public ActionResult Index(){
ViewData["UserName"] = TempData["UserName"];
}
}
Why do you have to do this? Because RedirectToAction doesn't render a page - it tells the client to make a different request to a new Url - thus any ViewData or model or whatever is thrown away as far as the server is concerned. TempData is there to provide temporary storage between two successive requests only - thus it works well for the RedirectToAction scenario.
Like I say though - this really is a poor way to persist your user information from controller to view and you should seriously rethink it as a matter of urgency.

in layout page:
<span>#{Html.RenderAction("actionname", "controllername");}</span>
in controller store a session variable
[HttpPost]
public ActionResult Index(LoginModels model)
{
Session["username"] = model.UserName;
//remaining code
}
add one more function
public ActionResult actionname() {
return Content(Session["username"]);
}
so here we dont need additional functions.

Related

ASP.NET MVC - Add Query String Value to RedirectToAction in Controller

I have an ASP.NET MVC app. My app is using T4MVC. In my controller, I need to redirect from one action to another. When I do this, I want to append a query string value. I can successfully redirect without the query string value, but I've been unsuccessful applying a query string value. My actions look like this:
[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult Action1()
{
return RedirectToAction(MVC.MyController.Action2().AddRouteValue("id", "5"));
}
[Route("action-2")]
public virtual ActionResult Action2(string input)
{
ViewBag.Input = input;
return View(viewModel);
}
The Action2 works fine when I visit ./action-2. I can also successfully POST to Action1. But, when the redirect doesn't work. I notice in the address bar the following:
/MyController/id
Why? How do I fix this? I just want to redirect back to Action2 but this time with a query string parameter added. What am I missing?
You need to specify the parameter by the name in the action (in this case "input") in order for it to work, see below:
return redirectToAction(MVC.MyController.Action2().AddRouteValue("input", "5"));
or alternatively:
return RedirectToAction("Action2", "MyController", new { input = "myInput"});
I try in following way and it works fine for me.
return RedirectToAction("Index", "CustomBuilder", new { usern = "admin" });

MVC 5 - set session variable at login

I am building an MVC 5 application and have come to the following problem: I want to show a menu item to the user, after the user has logged in, if the user has an Agreement with me.
I want to set a session variable at the moment the user logs in like:
Session["HasAgreement"] = Agreement.HasAgreement(userId);
and then in my _Layout.cshtml file where I build my menu do something like:
#if (Session["HasAgreement"] == "True")
{
<li>#Html.ActionLink("Agreement", "Agreement", "Home")</li>
}
My problem arises in the AccountController where I have added the logic to the standard Login Action:
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
var userId = User.Identity.GetUserId();
Session["HasAgreement"] = Agreement.HasAgreement(userId);
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
This is the standard MVC 5 login - except that I have added the two lines right after the "case SignInStatus.Success:" where I try to get the userId and then set the Session variable.
My problem is that at this point in thime the User is not authenticated(I thought that happened in the SignInManager above).
How do I set the session variable right after the user logs in?
The new session isn't set until you hit the next action. This is a common issue with trying to use the result for anything other than redirection. The best course of action would be to use the result to redirect to another action, in which you will then be able to access the session.
Assuming when your user logs in they go to a "dashboard", it might look something like:
SignInStatus.Success case:
case SignInStatus.Success:
return RedirectToAction("Dashboard");
If you require the ability to return to numerous actions, you can return the action name instead of a url and simply do RedirectToAction(returnAction). Obviously if you need to specify a controller as well, you'll need to post a returnController too.
Dashboard action:
[Authorize]
public ActionResult Dashboard() {
var userId = User.Identity.GetUserId();
Session["HasAgreement"] = Agreement.HasAgreement(userId);
return View();
}
I'm not sure where your Agreement object is coming from but you have access to the User property in the View so you could potentially do something like this:
_Layout.cshtml
#if (Agreement.HasAgreement(User.Identity.GetUserId()))
{
<li>#Html.ActionLink("Agreement", "Agreement", "Home")</li>
}
this also assumes that HasAgreement returns a bool which if it doesn't, it really should.
Your problem is not when you update the session variable, but what version of the session your layout page has got.
session isn't the best option for passing data between views. try ViewBag
(although you should always try to use a ViewModel where possible!)
(and you can use the session AS WELL, for the next page load)
This might not work in all cases, but I found the easiest way to set a session variable at Login was to move the logic to the controller action returned by RedirectToLocal(). In my case I made a new post-login entry point to the application called "Main", that has it's own view/controller.
On login the user is always redirected here first, so it guarantees my Session data gets set.
First I changed RedirectToLocal() in the AccountController:
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
//This is the template default
//return RedirectToAction("Index", "Home");
//This is my new entry point.
return RedirectToAction("Index", "Main"); }
Inside my MainController I can access the User object and set my data:
// GET: Main
public ActionResult Index()
{
ApplicationUser sessionuser = db.Users.Find(User.Identity.GetUserId());
Session.Add("UserName", sessionuser.UserName);
return View();
}
And for good measure I'm wiping the session data on logoff:
public ActionResult LogOff()
{
Session.RemoveAll(); //Clear all session variables
//...
}
As an option:
Add new partial view - call it Agreement.
#model bool
#if (Model)
{
<li>#Html.ActionLink("Agreement", "Agreement", "Home")</li>
}
Add new action to your, say, Account controller, call it Agreement
public PartialViewResult Agreement(){
var userId = User.Identity.GetUserId();
bool hasAgreement = Agreement.HasAgreement(userId); // This will be your model
return PartialView("Agreement", hasAgreement);
}
And in your layout do:
#Html.RenderAction("Agreement", "Account")
I have the exact same issue and appear to be using the same version of Identity as you (the OP). I tried what you did (prior to finding this) as well as followed Sippy's advice and put it in RedirectToLocal ActionResult in the AccountController (which didn't work):
In the meantime, I put it in the Global.Asax.cs under:
public void Profile_OnMigrateAnonymous(object sender, ProfileMigrateEventArgs args)
{
...//other code (or not) on migrate anonymous user to authenticated
Session["HasAgreement"] = Agreement.HasAgreement(userId);
}
I also created this void in my Global.asax.cs (catches expired sessions but still logged in):
void Session_Start(object sender, EventArgs e)
{
Session["HasAgreement"] = Agreement.HasAgreement(userId);
}
It works and also updates if the session expires. However, one of the downsides of this is that the session variable will not be updated when a user Logs Off (via Identity). However, I also tried putting my client extension in the LogOff ActionResult after AuthenticationManager.SignOut(); and it doesn't work.
I need my session variable to update after Log Off too, so this won't be my final solution, but it may be good enough for you?
I'll come back and update this if I find a better way, but right now, it's good enough and going on my TODO list.
UPDATE:
To catch the user's logoff event, I used Sippy's idea and changed my LogOff ActionResult in the AccountController to this:
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
return RedirectToAction("SetSessionVariables", "Account");
}
[AllowAnonymous]
public ActionResult SetSessionVariables()
{
Session["HasAgreement"] = Agreement.HasAgreement(userId);
return RedirectToAction("Index", "Home");
}
I think this encompasses all logon/logoff/session scenarios. I might see if I can incorporate what I did for the LogOff into the Logon successful redirect (more so to see when the user is actually "authenticated" than anything).
You can use user Claims to extend Identity data.
Implementation for instance here: How to extend available properties of User.Identity

Why isn't viewbag value passing back to the view?

straight forward question , can't seem to get my viewBag value to display in a view that the user is directed to after completing a form.
Please advise..thanks
My Index ActionResult simple returns model data..
public ActionResult Index()
{
var source = _repository.GetByUserID(_applicationUser.ID);
var model = new RefModel
{
test1 = source.test1,
};
return View(model);
}
My Get Edit" ActionResult , simply uses the same model data as Index.
My Post "Edit" ActionResult, assigns the new values if any to the model and redirects to the Index page, but Index page does not display ViewBag value ??
[HttpPost]
public ActionResult Edit(RefModell model)
{
if (ModelState.IsValid)
{
var source = _repository.GetByUserID(_applicationUser.ID);
if (source == null) return View(model);
source.test1 = model.test1;
_uow.SaveChanges();
#ViewBag.Message = "Profile Updated Successfully";
return RedirectToAction("Index");
}
return View(model);
}
And in my Index view...
#if(#ViewBag.Message != null)
{
<div>
<button type="button">#ViewBag.Message</button>
</div>
}
ViewBag only lives for the current request. In your case you are redirecting, so everything you might have stored in the ViewBag will die along wit the current request. Use ViewBag, only if you render a view, not if you intend to redirect.
Use TempData instead:
TempData["Message"] = "Profile Updated Successfully";
return RedirectToAction("Index");
and then in your view:
#if (TempData["Message"] != null)
{
<div>
<button type="button">#TempData["Message"]</button>
</div>
}
Behind the scenes, TempData will use Session but it will automatically evict the record once you read from it. So it's basically used for short-living, one-redirect persistence storage.
Alternatively you could pass it as query string parameter if you don't want to rely on sessions (which is probably what I would do).
RedirectToAction causes an HTTP 302 response, which makes the client make another call to the server and request a new page.
You should be returning a view instead of redirecting.
The RedirectToAction(msdn) instructs your browser to make a new request.
So your server will be called again but it will be a new request with a blank viewbag and all
You could do a sort of internal redirect by just calling the index method, this way the viewbag will still have its data.
Edit : you'll also have to modify your index method or your View(model) line will try to render the edit. Full code below
public ActionResult Index()
{
var source = _repository.GetByUserID(_applicationUser.ID);
var model = new RefModel
{
test1 = source.test1,
};
return View("Index",model);
}
[HttpPost]
public ActionResult Edit(RefModell model)
{
if (ModelState.IsValid)
{
var source = _repository.GetByUserID(_applicationUser.ID);
if (source == null) return View(model);
source.test1 = model.test1;
_uow.SaveChanges();
#ViewBag.Message = "Profile Updated Successfully";
return Index();
}
return View(model);
}
You can try this way also
Controller
public ActionResult Test()
{
ViewBag.controllerValue= "testvalue";
..................
}
View -
define top of razor page
#{string testvalue= (string)ViewBag.controllerValue;}
$(function () {
var val= '#testvalue';
});

Set Viewbag before Redirect

Is it possible to set the ViewBag before I call a redirection?
I want something like:
#ViewBag.Message="MyMessage";
RedirectToAction("MyAction");
When you use redirection, you shall not use ViewBag, but TempData
public ActionResult Action1 () {
TempData["shortMessage"] = "MyMessage";
return RedirectToAction("Action2");
}
public ActionResult Action2 () {
//now I can populate my ViewBag (if I want to) with the TempData["shortMessage"] content
ViewBag.Message = TempData["shortMessage"].ToString();
return View();
}
You can use the TempData in this situation.
Here is some explanation for the ViewBag, ViewData and TempData.
I did like this..and its working for me...
here I'm changing password and on success I want to set success message to viewbag to display on view..
public ActionResult ChangePass()
{
ChangePassword CP = new ChangePassword();
if (TempData["status"] != null)
{
ViewBag.Status = "Success";
TempData.Remove("status");
}
return View(CP);
}
[HttpPost]
public ActionResult ChangePass(ChangePassword obj)
{
if (ModelState.IsValid)
{
int pid = Session.GetDataFromSession<int>("ssnPersonnelID");
PersonnelMaster PM = db.PersonnelMasters.SingleOrDefault(x => x.PersonnelID == pid);
PM.Password = obj.NewPassword;
PM.Mdate = DateTime.Now;
db.SaveChanges();
TempData["status"] = "Success";
return RedirectToAction("ChangePass");
}
return View(obj);
}
Taken from here
Summary
The ViewData and ViewBag objects give you ways to access those extra pieces of data that go alongside your model, however for more complex data, you can move up to the ViewModel. TempData, on the other hand, is geared specifically for working with data on HTTP redirects, so remember to be cautious when using TempData.
Or you can use Session for alternative:
Session["message"] = "MyMessage";
RedirectToAction("MyAction");
and then call it whenever you need.
UPDATE
Also, as what #James said in his comment, it would be safe to nullify or clear the value of that specific session after you use it in order to avoid unwanted junk data or outdated value.

Asp.Net MVC - Blank model not returning blank data

I have a form which a user can fill in x times with the data they want too. The form is posted to the following Action.
[HttpPost]
public ActionResult Manage(ProductOptionModel DataToAdd)
{
if (!ModelState.IsValid)
{
return View(DataToAdd);
}
var ProdServ = new ProductService();
if (DataToAdd.ID != 0)
{
//Edit Mode.
DataToAdd = ProdServ.EditProductOption(DataToAdd);
ViewData["Message"] = "Option Changes Made";
}else
{
//Add
DataToAdd = ProdServ.AddProductOption(DataToAdd);
ViewData["Message"] = "New Option Added";
}
var RetModel = new ProductOptionModel() {ProductID = DataToAdd.ProductID};
return View(RetModel);
}
So at the bottom I blank the model (Leaving just the required field) and then return to the view. However the view holds the data from the previously submitted form.
Any ideas why? I have debugged the code and checked that the RetModel variable is empty.
Html helpers work this way when a view is returned on HTTP POSTs. They prefer post data over model values.
Use Post/Redirect/Get
That's why I suggest you use the Post/Redirect/Get pattern that's very well supported in Asp.net MVC. Your controller actions should redirect to some GET action after POST has successfully completed as it is in your case.
public ActionResult Process()
{
return View(new Data { Id = -1 });
}
[HttpPost]
public ActionResult Process(Data data)
{
if (!this.ModelState.IsValid)
{
return View(data);
}
new MyService().ProcessData(data);
return RedirectToAction("Process");
}
And if you display all previously entered data you can provide those in in the GET action or transfer them from POST to GET action using TempData dictionary.
This is because the build in input helpers will look at the posted data first and use those values if they exist. Then it will look at the model.

Categories