ViewData is always null - asp.net-mvc

I know lots of people asked this but none have solved my issue, please look at the simple two code snippets, I'm using dotnet core 2.2.
What's wrong with the way I'm setting the data inside of ViewData.
Controller.cs:
public async Task<IActionResult> GetWebTripMetaData(Guid tripId)
{
try
{
ViewData["Greeting"] = "Hello World!";
return View();
}
catch (Exception)
{
return BadRequest("Internal Server Error");
}
}
View:
#page
#model TripTale.Backend.Pages.tripModel
<html>
<head>
<link href="#Url.Content("~/Styles/trip.css")" rel="stylesheet" type="text/css" />
</head>
<body>
#ViewData["Greeting"]
</body>
</html>
Please note that when removing the ViewData["Greeting"] from view page it work fine. When adding it, Object reference not set to an instance is thrown.

I simply used ViewBag.Greeting instead of ViewData["Greeting"] and it worked fine

If you are needing to set ViewBag data in .NET Core when your controller loads, override OnActionExecuting. My ViewBag data was always null when setting it in the controller's constructor.
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
this.ViewBag.FullAccountName = $"{this.CurrentLdapUser.DisplayName} ({this.CurrentLdapUser.UserNameWithDomain})";
}

We had a similar issue. We have changed ViewData to ViewBag.Greeting as Mohamad Mousheimish said. This worked for cases like ViewBag.Greeting.Title = "Title" but on my page I had some elements and model expressions that kept using ViewData. Then we removed the #Page directive and after that everything worked fine.

public IActionResult Index()
{
try
{
ViewData["Greeting"] = "Hello World!";
return View();
}
catch (Exception)
{
return BadRequest("Internal Server Error");
}
}

Related

Handling ActionResult from inside the view

Describing my scenario will be the best way to describe what I'm trying to achieve. I'm looking for a cleaner solution, if it exists at all.
We have content that would like to lock. I abstracted the unlocking models because we could have different types. It could be a Redirect or rendered partial view or something else they could come up in the future, so I decided to try returning an ActionResult.
public abstract class AContentUnlocker
{
public abstract ActionResult GetUnlockActionResult();
}
public class RedirectUnlocker : AContentUnlocker
{
public override ActionResult GetUnlockActionResult()
{
return new RedirectResult("http://www.url1.com?returnUrl=mywebsiteagain");
}
}
public class PartialViewUnlocker: AContentUnlocker
{
public override ActionResult GetUnlockActionResult()
{
PartialViewResult view = new PartialViewResult();
view.ViewName = "_PartialViewToUnlock";
return view;
}
}
My Content would be represented in a model with the proper Unlocking Mechanism
public class MyContent
{
public string Description { get; set; }
public AContentUnlocker ContentUnlocker { get; set; }
}
In my controller, I would simply return my desired Content with the proper unlocking mechanism set.
public ActionResult Index()
{
MyContent myContent = new MyContent() {
Description = "Content 1",
ContentUnlocker = new PartialViewUnlocker()
};
return View(myContent);
}
In my index View, I would then Execute the ActionResult.
#{
Model.ContentUnlocker.GetUnlockActionResult().ExecuteResult(this.ViewContext);
}
return View(myContent);
The Redirect ActionResult works fine.
My problem is that with the Partial View Action Results, given the execution cycle of MVC, the partial view is rendered before the controller view. So I'm getting something like:
<!-- html of the partial view rendered -->
<div> blah blah </div>
<!-- html of the parent view -->
<html>
<head></head>
<body> blah .... </body>
</html>
I'm not sure it's possible, but is there a way to execute my ActionResult the same way Html.RenderPartial would?

How to dynamically change Themes and _Layout in ASP.Net MVC4

I want to be able to change the _Layout.cshtml view based on a setting in my database.
I understand that it is probably done in the _ViewStart.cshml view.
I am using EF 4.2 and want to adapt a solution that will not break any design pattern.
Not sure how to go about doing this in MVC.
In web forms, I could easily do this in the code-behind for the masterpage.
I am doing something like this in my base controller:
public abstract class BaseController : Controller
{
private IUserRepository _userRepository;
protected BaseController()
: this(
new UserRepository())
{
}
public BaseController(IUserRepository userRepository)
{
_userRepository = userRepository;
}
I have looked at FunnelWeb source as well but I am not quite getting how they are injecting things..
Add this code to in the RegisterBundles method of the BundleConfig class. Note that I am creating a separate bundle for each css so that I don't render each css to the client. I can pick which bundle I want to render in the HEAD section of the shared _Layout.cshtml view.
bundles.Add(new StyleBundle("~/Content/Ceruleancss").Include(
"~/Content/bootstrapCerulean.min.css",
"~/Content/site.css"));
bundles.Add(new StyleBundle("~/Content/defaultcss").Include(
"~/Content/bootstrap.min.css",
"~/Content/site.css"));
Then put some logic in the shared_Layout.cshtml to render the appropriate bundle. Since this layout view fires for every page, this is a good place to put it.
I think this approach could be used for branding if you support multiple corps for your app. It could also be used to provide a custom style by user I suppose.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>#ViewBag.Title - Contoso University</title>
#{
if (HttpContext.Current.User.Identity.Name == "MARK")
{
#Styles.Render("~/Content/defaultcss");
}
else
{
#Styles.Render("~/Content/Ceruleancss");
}
}
Old Question but for anyone coming across this question here is a nice solution using Action Filters Attributes
public class LoadUserLayoutAttribute : ActionFilterAttribute
{
private readonly string _layoutName;
public LoadUserLayoutAttribute()
{
_layoutName = MethodToGetLayoutNameFromDB();
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var result = filterContext.Result as ViewResult;
if (result != null)
{
result.MasterName = _layoutName;
}
}
}
and then, you can add an attribute to your base controller (or action) with this custom attribute:
[LoadUserLayout]
public abstract class BaseController : Controller
{
...
}

Custom render with unobtrusive validation, dont work, why?

Im trying to do a little render framework, since I need some more control over the render process. Fx. if a property need to be rendered in a tab.
So I set out to, render a TextBox, but it does not validate with server side or client side validation (the MVC unobtrusive validation)
I have taken my framework out, and recreated a little eksampel
public class Foo
{
public virtual int Id { get; set; }
[System.ComponentModel.DataAnnotations.Required]
public virtual string Name { get; set; }
public virtual DateTime StartTime { get; set; }
}
My extension method:
public static MvcHtmlString DummyForm(this HtmlHelper html)
{
StringBuilder sb = new StringBuilder();
Type oftype = typeof(Foo);
string[] propertyNameToRender = oftype.GetProperties().Select(o => o.Name).ToArray();
foreach (string s in propertyNameToRender)
{
MvcHtmlString htmlstring = System.Web.Mvc.Html.InputExtensions.TextBox(html, s);
sb.AppendLine(htmlstring.ToHtmlString());
}
return MvcHtmlString.Create(sb.ToString());
}
And on the Edit.cshtml
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true);
#Html.DummyForm()
}
If I look at the rendered html output, its the same (without the validation attri)
Can anyone tell me, why the validation attri, is not rendered.
Im using the mvc's own render controls, HtmlHelper is passed from the view, with all ModelMetadata and ModelState.
Unobtrusive validation data-val-* attributes are rendered when FormContext is initialized. Html.BeginForm does that, so
#using (Html.BeginForm())
{
#Html.DummyForm()
}
Should render inputs with validation attributes.
There is one thing that seems odd is that you are calling System.Web.Mvc.Html.InputExtensions.TextBox method yourself. This method is internally called by Html.TextBox and other strongly typed extensions. plz try changing
MvcHtmlString htmlstring = System.Web.Mvc.Html.InputExtensions.TextBox(html, s);
to
MvcHtmlString htmlstring = html.TextBox(s);
I have try to create a new ASP.net MVC site, and added the code from my RenderProject, and it works fine. The conclusion is my asp.net MVC project messed up. I dont why. :S

Can I use an ASP.Net MVC Razor view to generate an nicely formatted HTML Body as the input for an email sent from the server?

I'd like to make use of the Model Binding / Rendering capabilities of a Razor View to generate the HTML Body Content for an email I'm sending from my ASP.NET MVC Application.
Is there a way to render a view to a string instead of returning it as the ActionResult of a GET request?
To illustrate I'm looking for something that will do the following...
public ActionResult SendEmail(int id)
{
EmailDetailsViewModel emailDetails = EmailDetailsViewModel().CreateEmailDetails(id);
// THIS IS WHERE I NEED HELP...
// I want to pass my ViewModel (emailDetails) to my View (EmailBodyRazorView) but instead of Rending that to the Response stream I want to capture the output and pass it to an email client.
string htmlEmailBody = View("EmailBodyRazorView", emailDetails).ToString();
// Once I have the htmlEmail body I'm good to go. I've got a utilityt that will send the email for me.
MyEmailUtility.SmtpSendEmail("stevejobs#apple.com", "Email Subject", htmlEmailBody);
// Redirect another Action that will return a page to the user confirming the email was sent.
return RedirectToAction("ConfirmationEmailWasSent");
}
If you just need to render the view into a string try something like this:
public string ToHtml(string viewToRender, ViewDataDictionary viewData, ControllerContext controllerContext)
{
var result = ViewEngines.Engines.FindView(controllerContext, viewToRender, null);
StringWriter output;
using (output = new StringWriter())
{
var viewContext = new ViewContext(controllerContext, result.View, viewData, controllerContext.Controller.TempData, output);
result.View.Render(viewContext, output);
result.ViewEngine.ReleaseView(controllerContext, result.View);
}
return output.ToString();
}
You'll need to pass in the name of the view and the ViewData and ControllerContext from your controller action.
You may checkout Postal for using views for sending emails.
Try MvcMailer:
http://www.codeproject.com/KB/aspnet/MvcMailerNuGet.aspx
Another one would be ActionMailer.Net: https://bitbucket.org/swaj/actionmailer.net/wiki/Home
From the website: An MVC 3-based port of the Rails ActionMailer library to ASP.NET MVC. The goal is to make it easy and relatively painless to send email from your application.
NuGet: Install-Package ActionMailer
There is also Essential Mail: Razor package from NuGet. It is build over RazorEngine and provides simple interface for email rendering.
Email message template looks something like
#inherits Essential.Templating.Razor.Email.EmailTemplate
#using System.Net;
#{
From = new MailAddress("example#email.com");
Subject = "Email Subject";
}
#section Html
{
<html>
<head>
<title>Example</title>
</head>
<body>
<h1>HTML part of the email</h1>
</body>
</html>
}
#section Text
{
Text part of the email.
}
The project is hosted on GitHub: https://github.com/smolyakoff/essential-templating/wiki/Email-Template-with-Razor
Based on Ryan's answer, I did an extension method:
public static string RenderViewToString(this Controller source, string viewName)
{
var viewEngineResult = ViewEngines.Engines.FindView(source.ControllerContext, viewName, null);
using (StringWriter output = new StringWriter())
{
viewEngineResult.View.Render(new ViewContext(source.ControllerContext, viewEngineResult.View, source.ViewData, source.TempData, output), output);
viewEngineResult.ViewEngine.ReleaseView(source.ControllerContext, viewEngineResult.View);
return output.ToString();
}
}
To call from inside a controller action (example usage):
[AllowAnonymous]
public class ErrorController : Controller
{
// GET: Error
public ActionResult Index(System.Net.HttpStatusCode id)
{
Exception ex = null; // how do i get the exception that was thrown?
if (!Debugger.IsAttached)
Code.Email.Send(ConfigurationManager.AppSettings["BugReportEmailAddress"],
$"Bug Report: AgentPortal: {ex?.Message}",
this.RenderViewToString("BugReport"));
Response.StatusCode = (int)id;
return View();
}
}
Still working outside a web app with the last MVC FullFW
http://fabiomaulo.blogspot.com/2011/08/parse-string-as-razor-template.html
You can create a worker consuming queue rendering and sending emails outside the web.
Few code lines, you don't need another package over Razor.

My Property is not rendring on the jsp in struts 2.0

I have set a property in Action class as follows
public class HelloWorld{
public String execute() { ANNOTATION #1
setCustomGreeting( GREETING + getName() );
return "SUCCESS";
}
private String customGreeting;
public String getCustomGreeting()
{
return customGreeting;
}
public void setCustomGreeting( String customGreeting ){
this.customGreeting = customGreeting;
}
}
And i m trying to render it on jsp as follows
<%# page contentType="text/html; charset=UTF-8" %>
<%# taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>HelloWorld</title>
</head>
<body>
<h3>Custom Greeting Page</h3>
<h4><s:property value="customGreeting"/></h4>
</body>
</html>
But it's printing nothing on the jsp, please help me?
Debugging suggestions:
Put breakpoints (or trace statements, or whatever) in the methods to confirm whether or not they are called.
Place an <s:debug/> tag on the page.
If that doesn't give any hints, then enable more detailed logging (how to do this will depend on what logging framework is being used), specifically for OGNL.
Your Code looks fine?
Do you call the JSP directly?
Is your Action mapped in the struts.xml?

Resources