Translate to razor Syntax from MVC 2.0 code - asp.net

I am trying to convert the popular asp.net MVC 2.0 solr.net sample app code to Razor syntax. I am not able to understand the last line ... Kindly help
<% Html.Repeat(new[] { 5, 10, 20 }, ps => { %>
<% if (ps == Model.Search.PageSize) { %>
<span><%= ps%></span>
<% } else { %>
#ps
<% } %>
<% }, () => { %> | <% }); %>
[update]
Source for Html.Repeat extension
HtmlHelperRepeatExtensions.cs

For this to work you will have to modify the Html.Repeat extension method to take advantage of Templated Razor Delegates as illustrated by Phil Haack. And then:
#{Html.Repeat(
new[] { 5, 10, 20 },
#<text>
#if (item == Model.Search.PageSize)
{
<span>#item</span>
}
else
{
<a href="#Url.SetParameters(new { pagesize = item, page = 1 })">
#item
</a>
}
</text>,
#<text>|</text>
);}
UPDATE:
According to your updated question you seem to be using a custom HTML helper but as I stated in my answer you need to updated this helper to use the Templated Razor Delegates syntax if you want it to work. Here's for example how it might look:
public static class HtmlHelperRepeatExtensions
{
public static void Repeat<T>(
this HtmlHelper html,
IEnumerable<T> items,
Func<T, HelperResult> render,
Func<dynamic, HelperResult> separator
)
{
bool first = true;
foreach (var item in items)
{
if (first)
{
first = false;
}
else
{
separator(item).WriteTo(html.ViewContext.Writer);
}
render(item).WriteTo(html.ViewContext.Writer);
}
}
}
or if you want to have the helper method directly return a HelperResult so that you don't need to use the brackets when calling it:
public static class HtmlHelperRepeatExtensions
{
public static HelperResult Repeat<T>(
this HtmlHelper html,
IEnumerable<T> items,
Func<T, HelperResult> render,
Func<dynamic, HelperResult> separator
)
{
return new HelperResult(writer =>
{
bool first = true;
foreach (var item in items)
{
if (first)
first = false;
else
separator(item).WriteTo(writer);
render(item).WriteTo(writer);
}
});
}
}
and then inside your view:
#Html.Repeat(
new[] { 5, 10, 20 },
#<text>
#if (item == Model.Search.PageSize)
{
<span>#item</span>
}
else
{
<a href="#Url.SetParameters(new { pagesize = item, page = 1 })">
#item
</a>
}
</text>,
#<text>|</text>
)

Possibly something like:
}, () => { <text>|</text> });

Related

I cant using Html.RenderAction in .cshtml

FRONTEND
#{
<div>
#Html.RenderAction("UrunOzellikTipWidget", "Admin");
#Html.RenderAction("UrunOzellikDegerWidget", "Admin");
</div>
}
BACKEND
public ActionResult UrunOzellikEkle()
{
return View(Context.Baglanti.Urun.ToList());
}
public PartialViewResult UrunOzellikTipWidged(int? katID)
{
if (katID != null)
{
var data = Context.Baglanti.OzellikTip
.Where(x => x.KategoriID == katID)
.ToList();
return PartialView(data);
}
else
{
var data = Context.Baglanti.OzellikTip.ToList();
return PartialView(data);
}
}
public PartialViewResult UrunOzellikDegerWidget(int? tipID)
{
if (tipID != null)
{
var data = Context.Baglanti.OzellikDeger
.Where(x => x.OzellikTipID == tipID)
.ToList();
return PartialView(data);
}
else
{
var data = Context.Baglanti.OzellikDeger
.ToList();
return PartialView(data);
}
}
**ERROR CODE:Severity Code Description Project File Line Suppression State Error CS0029 Cannot implicitly convert type 'void' to 'object'
**
#Html.RenderAction renders the result and, instead of returns it as string, writes it directly to the response and returns Void. So you will have to use it inside a C# block, denoted as #{ }, and end it with a semicolon at the end, just like how you invoke/call a void function.
So your front-end needs to be changed to:
<div>
#{
Html.RenderAction("UrunOzellikTipWidget", "Admin");
Html.RenderAction("UrunOzellikDegerWidget", "Admin");
}
</div>
The other's answer (removing the redundant ampersands) won't work because those 2 RenderAction are wrapped inside a <div />. If you remove the ampersand, they're treated as just regular HTML. It will output as:
Revise your code in FRONTEND to the following (remove the redundant ampersands from both Html.RenderAction statements).
<div>
#{ Html.RenderAction("UrunOzellikTipWidget", "Admin"); }
#{ Html.RenderAction("UrunOzellikDegerWidget", "Admin") };
</div>

Generate the Tree structure in the Asp.Net Mvc

I create a tree structure in Asp.Net MVC Core along with the possibility of operating on this tree, i.e. adding branches, leaves, editing, etc. And I have a question whether this implementation of generating this tree is ok? Are there better ways to do this?
Branch generation method called from the controller:
public ActionResult GenerateNodes(int? parentId)
{
var listOfNodes = _context.NodeEntitie.Where(x => x.ParentNodeId == parentId);
return PartialView(listOfNodes);
}
Generated view:
#model IEnumerable<Tree10.Models.Node>
#if (Model.Any())
{
foreach (var element in Model)
{
<li class="listElement" onclick="Select(this)" id="#element.Id">+#element.Name</li>
if (element.HasChildrens())
{
<ol>
#Html.Action("GenerateNodes", "Node", new { parentId = element.Id })
#Html.Action("GenerateLeafs", "Leaf", new { parentId = element.Id })
</ol>
}
}
}
The method of generating leaves from the controller:
public ActionResult GenerateLeafs(int parentId)
{
var listOfLeafs = _context.LeafEntities.Where(x => x.ParentId == parentId);
return PartialView(listOfLeafs);
}
Generated view:
#model IEnumerable<Tree10.Models.Leaf>
#if (Model.Any())
{
foreach (var element in Model)
{
<li class="listElement" onclick="Select(this)" id="l #element.Id">#element.Name</li>
}
}
ehh ok it was stupid. I did a better solution
#{
void ShowTree(int? parentId)
{
var newNodes = Model.Where(x => x.ParentNodeId == parentId);
<ul>
#foreach(var element in newNodes)
{
<li>#element.Name</li>
if (element.SubNodes.Any())
{
ShowTree(element.ParentNodeId);
}
}
</ul>
}
ShowTree(null);
}

Making TPL Async calls from mvc controller on click of submit button

Basically I want to implement simple search functionality, whenever user enters some keyword in the text box on view and clicks submit button I want to make ASYNC calls to predefined website urls using TPL Async mechanism. When I do the same with console application it works like a charm but not with ASP.NET MVC3.
I couldn't find the reason
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
return View();
}
[HttpPost]
public ActionResult Index(string text)
{
string[] url = { "http://www.msnbc.com", "http://www.yahoo.com",
"http://www.nytimes.com", "http://www.washingtonpost.com",
"http://www.latimes.com", "http://www.newsday.com" };
Task<string[]> webTask = this.GetWordCounts(url, text);
string[] results = null;
try
{
results = webTask.Result;
}
catch (AggregateException e)
{
}
return View("Index", results);
}
//Taken from MSDN
Task<string[]> GetWordCounts(string[] urls, string name)
{
TaskCompletionSource<string[]> tcs = new TaskCompletionSource<string[]>();
WebClient[] webClients = new WebClient[urls.Length];
object m_lock = new object();
int count = 0;
List<string> results = new List<string>();
for (int i = 0; i < urls.Length; i++)
{
webClients[i] = new WebClient();
#region callback
// Specify the callback for the DownloadStringCompleted
// event that will be raised by this WebClient instance.
webClients[i].DownloadStringCompleted += (obj, args) =>
{
if (args.Cancelled == true)
{
tcs.TrySetCanceled();
return;
}
else if (args.Error != null)
{
// Pass through to the underlying Task
// any exceptions thrown by the WebClient
// during the asynchronous operation.
tcs.TrySetException(args.Error);
return;
}
else
{
// Split the string into an array of words,
// then count the number of elements that match
// the search term.
string[] words = null;
words = args.Result.Split(' ');
string NAME = name.ToUpper();
int nameCount = (from word in words.AsParallel()
where word.ToUpper().Contains(NAME)
select word)
.Count();
// Associate the results with the url, and add new string to the array that
// the underlying Task object will return in its Result property.
results.Add(String.Format("{0} has {1} instances of {2}", args.UserState, nameCount, name));
}
// If this is the last async operation to complete,
// then set the Result property on the underlying Task.
lock (m_lock)
{
count++;
if (count == urls.Length)
{
tcs.TrySetResult(results.ToArray());
}
}
};
#endregion
// Call DownloadStringAsync for each URL.
Uri address = null;
try
{
address = new Uri(urls[i]);
// Pass the address, and also use it for the userToken
// to identify the page when the delegate is invoked.
webClients[i].DownloadStringAsync(address, address);
}
catch (UriFormatException ex)
{
// Abandon the entire operation if one url is malformed.
// Other actions are possible here.
tcs.TrySetException(ex);
return tcs.Task;
}
}
// Return the underlying Task. The client code
// waits on the Result property, and handles exceptions
// in the try-catch block there.
return tcs.Task;
}
this is my view - for now I have hard coded keyword as microsoft
#using (Html.BeginForm("Index", "Home", new { text = "Microsoft" }))
{
<input type="submit" />
}
Update: It stays forever and inside the try block of Index Post method
I would recommend you using an AsyncController for this task to avoid jeopardizing ASP.NET worker threads which is one the worst thing that might happen to an ASP.NET application => running out of worker threads. It's like running out of fuel in the middle of the desert. You most certainly die.
So let's start by writing an extension method that will allow us converting the legacy WebClient event based pattern into the new task based pattern:
public static class TaskExtensions
{
public static Task<string> DownloadStringAsTask(this string url)
{
var tcs = new TaskCompletionSource<string>(url);
var client = new WebClient();
client.DownloadStringCompleted += (sender, args) =>
{
if (args.Error != null)
{
tcs.SetException(args.Error);
}
else
{
tcs.SetResult(args.Result);
}
};
client.DownloadStringAsync(new Uri(url));
return tcs.Task;
}
}
Armed with this extension method in hand we could now define a view model that will basically reflect the requirements of our view:
public class DownloadResultViewModel
{
public string Url { get; set; }
public int WordCount { get; set; }
public string Error { get; set; }
}
Then we move on to an asyncrhonous controller that will contain 2 actions: a standard synchronous Index action that will render the search form and an asynchronous Search action that will perform the actual work:
public class HomeController : AsyncController
{
public ActionResult Index()
{
return View();
}
[AsyncTimeout(600000)]
[HttpPost]
public void SearchAsync(string searchText)
{
AsyncManager.Parameters["searchText"] = searchText;
string[] urls =
{
"http://www.msnbc.com",
"http://www.yahoo.com",
"http://www.nytimes.com",
"http://www.washingtonpost.com",
"http://www.latimes.com",
"http://www.unexistentdomainthatwillcrash.com",
"http://www.newsday.com"
};
var tasks = urls.Select(url => url.DownloadStringAsTask());
AsyncManager.OutstandingOperations.Increment(urls.Length);
Task.Factory.ContinueWhenAll(tasks.ToArray(), allTasks =>
{
var results =
from task in allTasks
let error = task.IsFaulted ? task.Exception.Message : null
let result = !task.IsFaulted ? task.Result : string.Empty
select new DownloadResultViewModel
{
Url = (string)task.AsyncState,
Error = error,
WordCount = result.Split(' ')
.Where(x => string.Equals(x, searchText, StringComparison.OrdinalIgnoreCase))
.Count()
};
AsyncManager.Parameters["results"] = results;
AsyncManager.OutstandingOperations.Decrement(urls.Length);
});
}
public ActionResult SearchCompleted(IEnumerable<DownloadResultViewModel> results)
{
return View("index", results);
}
}
Now we define an ~/Views/Home/Index.cshtml view that will contain the search logic as well as the results:
#model IEnumerable<DownloadResultViewModel>
#using (Html.BeginForm("search", null, new { searchText = "politics" }))
{
<button type="submit">Search</button>
}
#if (Model != null)
{
<h3>Search results</h3>
<table>
<thead>
<tr>
<th>Url</th>
<th>Word count</th>
</tr>
</thead>
<tbody>
#Html.DisplayForModel()
</tbody>
</table>
}
And of course the corresponding display template that will be rendered automatically for each element of our model (~/Views/Shared/DisplayTemplates/DownloadResultViewModel.cshtml):
#model DownloadResultViewModel
<tr>
<td>#Html.DisplayFor(x => x.Url)</td>
<td>
#if (Model.Error != null)
{
#Html.DisplayFor(x => x.Error)
}
else
{
#Html.DisplayFor(x => x.WordCount)
}
</td>
</tr>
Now, since the search operation could take quite a long time your users could quickly get bored without being able to use some of the other hundredths of functionalities that your webpage has to offer them.
In this case it is absolutely trivial to invoke the Search controller action using an AJAX request and showing a spinner to inform the users that their search is in progress but without freezing the webpage allowing them to do other things (without navigating away from the page obviously).
So let's do that, shall we?
We start by externalizing the results into a partial (~/Views/Home/_Results.cshtml) without touching at the display template:
#model IEnumerable<DownloadResultViewModel>
#if (Model != null)
{
<h3>Search results</h3>
<table>
<thead>
<tr>
<th>Url</th>
<th>Word count</th>
</tr>
</thead>
<tbody>
#Html.DisplayForModel()
</tbody>
</table>
}
and we adapt our ~/Views/Home/Index.cshtml view to use this partial:
#model IEnumerable<DownloadResultViewModel>
#using (Html.BeginForm("search", null, new { searchText = "politics" }))
{
<button type="submit">Search</button>
}
<div id="results">
#Html.Partial("_Results")
</div>
and of course the SearchCompleted controller action that must now return only the partial result:
public ActionResult SearchCompleted(IEnumerable<DownloadResultViewModel> results)
{
return PartialView("_Results", results);
}
Now all that's left is to write a simple javascript that will AJAXify our search form. So this could happen into a separate js that will reference in our layout:
$(function () {
$('form').submit(function () {
$.ajax({
url: this.action,
type: this.method,
success: function (results) {
$('#results').html(results);
}
});
return false;
});
});
Depending on whether you referenced this script in the <head> section or at the end of the body you might not need to wrap it in a document.ready. If the script is at the end you could remove the wrapping document.ready function from my example.
And the last part is to give some visual indication to the user that the site is actually performing a search. This could be done using a global ajax event handler that we might subscribe to:
$(function () {
$(document).ajaxStart(function () {
$('#results').html('searching ...');
});
});

ASP.Net MVC help with refactoring

I'm quite new to both ASP.Net and MVC.
I got the following code in my master page:
<div id="main-menu" class="menu">
<%
var items = (IList<CompanyName.Framework.Web.MenuItem>)ViewData["MainMenu"];
if (items.Count > 0)
{
%><ul><%
foreach (var item in items)
{
if (!string.IsNullOrEmpty(item.RequiredRole) && !System.Threading.Thread.CurrentPrincipal.IsInRole(item.RequiredRole))
continue;
%><li><%= item.Title %></li><%
}
%></ul><%
}
%>
</div>
Can I move the code to another file or refactor the code in any way?
edit:
My ApplicationController that all controllers derive:
public class ApplicationController : Controller
{
List<MenuItem> _mainMenu = new List<MenuItem>();
List<MenuItem> _contextMenu = new List<MenuItem>();
protected IList<MenuItem> MainMenu
{
get { return _mainMenu; }
}
protected IList<MenuItem> ContextMenu
{
get { return _contextMenu; }
}
protected string PageTitle { get; set; }
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
ViewData["PageTitle"] = PageTitle;
ViewData["MainMenu"] = MainMenu;
ViewData["ContextMenu"] = ContextMenu;
base.OnResultExecuting(filterContext);
}
}
Here are a couple of suggestions:
Improvement number 1: use view models and strongly typed views instead of ViewData
public ActionResult Index()
{
// TODO: Fetch this data from a repository
var menus = new[] {
new MenuItem(), new MenuItem()
}.ToList();
return View(menus);
}
and then in your view:
<div id="main-menu" class="menu">
<%
if (Model.Count > 0)
{
%><ul><%
foreach (var item in Model)
{
if (!string.IsNullOrEmpty(item.RequiredRole) && !System.Threading.Thread.CurrentPrincipal.IsInRole(item.RequiredRole))
continue;
%><li><%= item.Title %></li><%
}
%></ul><%
}
%>
</div>
Still horrible and completely unreadable tag soup.
Improvement number 2: use editor/display templates:
In ~/Views/Home/DisplayTemplates/MenuItem.ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CompanyName.Framework.Web.MenuItem>" %>
<% if (!string.IsNullOrEmpty(Model.RequiredRole) &&
System.Threading.Thread.CurrentPrincipal.IsInRole(Model.RequiredRole)) { %>
<li>
<%= Model.Title %>
</li>
<% } %>
And then in your main view:
<div id="main-menu" class="menu">
<ul>
<%= Html.DisplayForModel() %>
</ul>
</div>
Improvement number 3: Avoid coding business rules in a view. So in your view model add a property:
public bool IsLinkVisible
{
get
{
return !string.IsNullOrEmpty(RequiredRole) &&
Thread.CurrentPrincipal.IsInRole(RequiredRole);
}
}
so that your display template now looks like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CompanyName.Framework.Web.MenuItem>" %>
<% if (Model.IsLinkVisible) { %>
<li>
<%= Model.Title %>
</li>
<% } %>
Improvement number 4: Write a custom HTML helper to render this anchor because writing C# in a view is still ugly and untestable:
public static class HtmlExtensions
{
public static MvcHtmlString MenuItem(this HtmlHelper<MenuItem> htmlHelper)
{
var menuItem = htmlHelper.ViewData.Model;
if (!menuItem.IsLinkVisible)
{
return MvcHtmlString.Empty;
}
var li = new TagBuilder("li");
var a = new TagBuilder("a");
a.MergeAttribute("href", menuItem.Uri);
a.SetInnerText(menuItem.Title);
li.InnerHtml = a.ToString();
return MvcHtmlString.Create(li.ToString());
}
}
and finally your display template:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<CompanyName.Framework.Web.MenuItem>" %>
<%= Html.MenuItem() %>
Yes, you can just put that block into an .ascx file and use:
<% html.RenderPartial("myPartialFile.asx"); %>
The above assumes that myPartialFile.ascx is located in the same folder as your master page, usually, the Views/Shared folder.

Error messages in ASP.NET with jQuery UI

I've been using my own Error reporting module which was combination of simple c# and jQueryUI Dialog. Problem is that once error or success occurs i do write it's value to session. It does work pretty good on pages with Responce.Redirect on error but not on pages where i catch an error and then return to same form.
My question is why does session which added pre-postback fails to load in pages where i have return statement on some condition.
And if there another way to save errors and success message except in session ? Maybe global variables or something like that ...
CODE EXAMPLES
this is Error class
public static string getMessage()
{
HttpContext c = HttpContext.Current;
string messageType = "";
if (c.Session["errorMessage"] != null)
{
messageType = "errorMessage";
}
else if (c.Session["successMessage"] != null)
{
messageType = "successMessage";
}
if (!string.IsNullOrEmpty(messageType))
{
string[] messageBody = c.Session[messageType].ToString().Split('|');
StringBuilder userMessageSb = new StringBuilder();
userMessageSb.Append(string.Format("<div id=\"{0}\" title=\"{1}\">{2}</div>", messageType, messageBody[0], messageBody[1]));
// fix so message will not re-appear
c.Session.Remove(messageType);
messageType = userMessageSb.ToString();
}
return messageType;
}
public static void setSuccess(string successMessage)
{
HttpContext.Current.Session["successMessage"] = setMessage("success", successMessage);
}
public static void setError(string errorMessage)
{
HttpContext.Current.Session["errorMessage"] = setMessage("error", errorMessage);
}
private static string setMessage(string messageTitle, string messageBody)
{
return string.Format("{0}|{1}", messageTitle, messageBody);
}
i set message like this prior to redirect or return
Errors.setError(my error is");
i get error on bottom of my masterpage like this
<%= Errors.getMessage() %>
and this is JS
$(function () {
$("#errorMessage").dialog("destroy");
$("#successMessage").dialog("destroy");
if ($("#errorMessage").length != 0) {
$("#errorMessage").dialog({
modal: true,
height: 300,
width: 400,
buttons: {
Ok: function () {
$(this).dialog('close');
}
}
});
}
if ($("#successMessage").length != 0) {
$("#successMessage").dialog({
modal: true,
height: 300,
width: 400,
buttons: {
Ok: function () {
$(this).dialog('close');
}
}
});
}
});
There is a possibility that <%= Errors.getMessage() %> executes before you call Errors.setError(my error is") in case when you are not redirecting.
Hope below answer helps.
Create a property in your master page code behind
public string MessagePlaceholder
{
get { return messagePlaceholder.InnerHtml; }
set { messagePlaceholder.InnerHtml = value; }
}
Replace <%= Errors.getMessage() %> with a div place holder like below
<div id="messagePlaceholder" runat="server"></div>
And here is your setError method
public static void setError(string errorMessage, bool redirecting)
{
HttpContext.Current.Session["errorMessage"] = setMessage("error", errorMessage);
if (!redirecting)
{
((HttpContext.Current.Handler as System.Web.UI.Page).Master as YourMasterPageType).MessagePlaceholder = getMessage();
}
}
EDIT
Sorry I forgot this
In Page_Load event of your master page
if(!IsPostBack)
{
messagePlaceholder.InnerHtml = Errors.getMessage();
}

Resources