Hello I am sure this has been asked before but I cannot find the resolution.
The model that gets used
public class TaskList : Base
{
public TaskModel taskModel;
public List<Task> tasks;
public TaskList(string role, int userId) : base(role, userId)
{
taskModel = new TaskModel(userId,role);
}
public void GetTasks(int skip,int take)
{
tasks = taskModel.GetTasks(skip, take);
}
}
The controller
public ActionResult TaskListPartial(int skip, int take)
{
var taskList = new TaskList(role,userId);
taskList.GetTasks(skip, take);
return View(taskList);
}
Web.config compilation part
<system.web>
<compilation>
<assemblies>
<add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
How do I get the Model to resolve so I can get intellisense?
It show error because in view you have only single model class in namespace and in logic you are going to get value from list of object class.
Please define your view model as:
#model List<wrikeMVCNoAuth.ViewModel.Tasks.TaskList>
I apologise for the mistake I was under the impression that I didnt have to compile the project each time to get the intellisense to display int he views.
Once I had a successful compile everything came back.
Related
I'm trying to work out why Umbraco 7.2.4 just doesn't seem to handle asynchronous tasks in my ASP.NET MVC controller. I feel like I've read almost every possible stack overflow and umbraco q&a, and tried many possible methods to try narrow down the problem. This is both for Umbraco 7 & MVC 4 and & MVC 5. It works just fine in an MVC project without Umbraco.
HomeController.cs:
using System.Threading.Tasks;
using System.Web.Mvc;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
namespace Umbraco.Async.Website.Controllers
{
public class HomeController : RenderMvcController
{
public new async Task<ActionResult> Index(RenderModel model)
{
var menuModel = new HomeViewModel(model);
await Task.Delay(1000);
return View("Home", menuModel);
}
}
public class HomeViewModel : RenderModel
{
public string Test = "Pizza is awesome!!!!";
public HomeViewModel(RenderModel model)
: base(model.Content, model.CurrentCulture)
{
}
}
}
Home.cshtml:
#*#inherits Umbraco.Web.Mvc.UmbracoTemplatePage*#
#inherits UmbracoViewPage<Umbraco.Async.Website.Controllers.HomeViewModel>
#{
Layout = null;
}
<h1>#Model.Test</h1>
In the end the browser shows no rendered view, and just the text string:
System.Threading.Tasks.Task`1[System.Web.Mvc.ActionResult]
Do you have this key in your web.config file? If not, please add it and then try.
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
I've raised an issue with Umbraco for this issue as I have ben struggling with this for quite a while. I have written an article on a solution here but in a nutshell you can use this instead:
public async Task<ActionResult> Home(RenderModel model)
{
var menuModel = new HomeViewModel(model);
await Task.Delay(1000);
return View("Home", menuModel);
}
The difference being that the Home action takes precedence over the failing Index action as it is routed from the template name not the document type alias.
I am using Health Monitoring for catching all errors and sending them to email. While it works in the development environment it did not when I deploy it in Prod. The only difference being the "customerrors" set to "on/off". So, I verified it again and it seems it will not log when the custom errors is set to "On/RemoteOnly". Below is part of my configuration in question.
Is there a workaround to this issue? Thanks.
<healthMonitoring enabled="true">
<eventMappings>
<clear />
<add name="All Errors" type="System.Web.Management.WebBaseErrorEvent"
startEventCode="0" endEventCode="2147483647" />
</eventMappings>
<providers>
<clear />
<add
name="SimpleMailWebEventProvider"
type="System.Web.Management.SimpleMailWebEventProvider"
to="dev#net"
from="de#net"
buffer="false"
/>
</providers>
<rules>
<clear />
<add name="All Errors Default" eventName="All Errors" provider="SimpleMailWebEventProvider"
profile="Default" minInstances="1" maxLimit="Infinite" minInterval="00:00:00" />
</rules>
</healthMonitoring>
--update
This is MVC3 project
In an ASP.NET MVC 3 project you will have a global HandleError action filter registered by default in global.asax.cs:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
This attribute gets applied to every controller action and if customErrors are set to On only the custom error page is displayed and the exception that occured in a controller action is marked as handled. ASP.NET Health Monitoring doesn't see this exception anymore and can't log it.
An approach to use Health Monitoring together with the HandleError attribute and a custom error page is described here and here and here:
You create a custom error attribute derived from HandleError:
public class HandleErrorHealthMonitoringAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
// Do the default, i.e. show custom error page if custom errors are on
base.OnException(filterContext);
// Suppress raising the health monitoring event below if custom errors
// are off. In that case health monitoring will receive the exception
// anyway and raise the event
if (!filterContext.HttpContext.IsCustomErrorEnabled)
return;
// Raise health monitoring event
var errorEvent = new GenericWebRequestErrorEvent(
"Unhandled exception occurred.", this,
WebEventCodes.WebExtendedBase + 1, filterContext.Exception);
errorEvent.Raise();
}
}
And then register this attribute instead of default HandleError:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorHealthMonitoringAttribute());
}
The GenericWebRequestErrorEvent is a custom error event derived from the base WebRequestErrorEvent. It doesn't do anything custom and only exists because WebRequestErrorEvent doesn't have any public constructors, so we can't use var errorEvent = new WebRequestErrorEvent(...):
public class GenericWebRequestErrorEvent : WebRequestErrorEvent
{
public GenericWebRequestErrorEvent(string message, object eventSource,
int eventCode, Exception exception) :
base(message, eventSource, eventCode, exception)
{
}
public GenericWebRequestErrorEvent(string message, object eventSource,
int eventCode, int eventDetailCode, Exception exception) :
base(message, eventSource, eventCode, eventDetailCode, exception)
{
}
}
Note, that you will receive an email titled with MyNamespace.GenericWebRequestErrorEvent and not with System.Web.Management.WebRequestErrorEvent and the event code will always be 100001 (= WebEventCodes.WebExtendedBase + 1).
I created the following helper in my main directory under /Helpers:
HtmlHelpers.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
namespace Website.Helpers
{
public static class HtmlHelpers
{
public static MvcHtmlString ActiveActionLinkHelper(this HtmlHelper Html, string text, string action, string controller, string activeClass = "active", bool actionCheck = false)
{
if (Html.ViewContext.RouteData.GetRequiredString("controller") == controller)
{
if (actionCheck)
{
if (Html.ViewContext.RouteData.GetRequiredString("action") == action)
return Html.ActionLink(text, action, controller, new { Class = activeClass });
}
else
{
return Html.ActionLink(text, action, controller, new { Class = activeClass });
}
}
return Html.ActionLink(text, action, controller);
}
}
}
I added the namespace to the Views web.config in my Publishers Areas folder:
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="Website.Helpers" />
</namespaces>
</pages>
</system.web.webPages.razor>
I keep getting this message:
Compiler Error Message: CS1061: 'System.Web.Mvc.HtmlHelper' does not contain a definition for 'ActiveActionLink' and no extension method 'ActiveActionLink' accepting a first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are you missing a using directive or an assembly reference?)
Core: #Html.ActiveActionLink("Dashboard", "Index", "Dashboard")
Does anyone know what I am doing wrong? There are hardly any tutorials on how or where to store an HTML helper. Can someone please advise me?
Core: #Html.ActiveActionLink("Dashboard", "Index", "Dashboard") // here it is the problem
as your method is ActiveActionLinkHelper, your calling different method.
#Html.ActiveActionLinkHelper("Dashboard", "Index", "Dashboard") // try like this.
The compiler is right. You're method is named ActiveActionLinkHelper. Change it to ActiveActionLink and all should be well
So my scenarios a little funny but theres a reason for it.
I have a parent web application, called Parent, and a second web application called Child. Child is a virtual directory in IIS7 under Parent which is an application in IIS. Child is not a child directory of parent in the file system, only in IIS as a virtual directory. On application loading (Application_Start in global.asax) in the parent web application i tell it to load the child web dlls from childs bin folder using Assembly.LoadFrom() loading it into the app domain of Parent. Then when i try to visit /Child/Default.aspx I get an error saying:
Parser Error
Parser Error Message: Could not load type 'Child._Default'.
Now the Child.dll (the web dll containing childs code behind etc) is in the app domain of the parent application and i can successfully reflect it and its members from code behind in the Parent page Default.aspx.
Furthermore on the Child/Default.aspx if i change the Inherits="Child._Default" to Inherits="System.Web.UI.Page" and then in <% %> tags on the page enumerate the dlls in the app domain i can see Child.dll and reflect its members and invoke functions.
One thing that works is changing CodeBehind to CodeFile in the page directive. Then the page parses correctly. However this only works when the websites are in uncompiled, non published form.
What's happening is the appdomain isn't looking within it's assembly list when it's trying to resolve the "Child" assembly for the page in the Child project.
What you need to do is use the AssemblyResolve event handler in the AppDomain. You can do so like this:
First we create and AssemblyLoader class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;
using System.IO;
namespace Parent
{
internal class AssemblyLoader
{
private static List<AssemblyInformation> virtualDirectoryAssemblies = new List<AssemblyInformation>();
private static readonly string virtualDirectoryBinFolderFormatString = "~/{0}/bin/";
private static readonly string[] pathSplitParams = new string[1] { "\\" };
private static readonly string[] assemblyNameSplitParams = new string[1] { "," };
internal static Assembly AssemblyResolve(object sender, ResolveEventArgs e)
{
var name = e.Name.Split(assemblyNameSplitParams, StringSplitOptions.RemoveEmptyEntries).First();
if (!virtualDirectoryAssemblies.Exists(a => a.Name.Equals(name)))
return null;
return Assembly.LoadFrom(virtualDirectoryAssemblies.Single(a => a.Name.Equals(name)).Path);
}
internal static void LoadVirtualDirectories(List<string> virtualDirectories)
{
foreach (var v in virtualDirectories)
{
var path = HttpContext.Current.Server.MapPath(string.Format(virtualDirectoryBinFolderFormatString, v));
AppDomain.CurrentDomain.AppendPrivatePath(path);
AppDomain.CurrentDomain.SetShadowCopyPath(path);
var assemblies = Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories).ToList();
foreach (var a in assemblies)
{
var name = a.Split(pathSplitParams, StringSplitOptions.RemoveEmptyEntries).Last().Replace(".dll", string.Empty);
if(!virtualDirectoryAssemblies.Exists(i => i.Name.Equals(name)))
{
virtualDirectoryAssemblies.Add(new AssemblyInformation
{
Name = name,
Path = a
});
}
}
}
}
class AssemblyInformation
{
public string Name { get;set; }
public string Path { get; set; }
}
}
}
In the web.config file for the Parent project I added this (if you have more virtual directories, the idea is to have a comma deliminated list):
<appSettings>
<add key="VirtualDirectories" value="Child"/>
</appSettings>
In the web.config of the child project, you add this reference to the assembly Child assembly:
<system.web>
<compilation>
<assemblies>
<add assembly="Child, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</assemblies>
</compilation>
</system.web>
It can also be like this:
<system.web>
<compilation>
<assemblies>
<add assembly="Child"/>
</assemblies>
</compilation>
</system.web>
Now, last but not least, we put this into the Global.asax:
protected void Application_Start(object sender, EventArgs e)
{
AppDomain.CurrentDomain.AssemblyResolve += AssemblyLoader.AssemblyResolve;
var virtualDirectories =
ConfigurationManager.AppSettings.Get("VirtualDirectories").Split(new string[1] { "," }, StringSplitOptions.RemoveEmptyEntries).ToList();
AssemblyLoader.LoadVirtualDirectories(virtualDirectories);
}
And we're done... :P
Python frameworks always provide ways to handle URLs that convey the data of the request in an elegant way, like for example http://somewhere.overtherainbow.com/userid/123424/
I want you to notice the ending path /userid/123424/
How do you do this in ASP.NET?
This example uses ASP.NET Routing to implement friendly URLs.
Examples of the mappings that the application handles are:
http://samplesite/userid/1234 - http://samplesite/users.aspx?userid=1234
http://samplesite/userid/1235 - http://samplesite/users.aspx?userid=1235
This example uses querystrings and avoids any requirement to modify the code on the aspx page.
Step 1 - add the necessary entries to web.config
<system.web>
<compilation debug="true">
<assemblies>
…
<add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</assemblies>
</compilation>
…
<httpModules>
…
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</httpModules>
</system.web>
<system.webServer>
…
<modules>
…
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</modules>
<handlers
…
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</handlers>
</system.webServer>
Step 2 - add a routing table in global.asax
Define the mapping from the friendly URL to the aspx page, saving the requested userid for later use.
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add("UseridRoute", new Route
(
"userid/{userid}",
new CustomRouteHandler("~/users.aspx")
));
}
Step 3 - implement the route handler
Add the querystring to the current context before the routing takes place.
using System.Web.Compilation;
using System.Web.UI;
using System.Web;
using System.Web.Routing;
public class CustomRouteHandler : IRouteHandler
{
public CustomRouteHandler(string virtualPath)
{
this.VirtualPath = virtualPath;
}
public string VirtualPath { get; private set; }
public IHttpHandler GetHttpHandler(RequestContext
requestContext)
{
// Add the querystring to the URL in the current context
string queryString = "?userid=" + requestContext.RouteData.Values["userid"];
HttpContext.Current.RewritePath(
string.Concat(
VirtualPath,
queryString));
var page = BuildManager.CreateInstanceFromVirtualPath
(VirtualPath, typeof(Page)) as IHttpHandler;
return page;
}
}
Code from users.aspx
The code on the aspx page for reference.
protected void Page_Load(object sender, EventArgs e)
{
string id = Page.Request.QueryString["userid"];
switch (id)
{
case "1234":
lblUserId.Text = id;
lblUserName.Text = "Bill";
break;
case "1235":
lblUserId.Text = id;
lblUserName.Text = "Claire";
break;
case "1236":
lblUserId.Text = id;
lblUserName.Text = "David";
break;
default:
lblUserId.Text = "0000";
lblUserName.Text = "Unknown";
break;
}
This is an alternative example that also uses ASP.NET Routing to implement friendly URLs.
Examples of the mappings that the application handles are:
http://samplesite/userid/1234 - http://samplesite/users.aspx?userid=1234
http://samplesite/userid/1235 - http://samplesite/users.aspx?userid=1235
This example does not use querystrings but requires additional code on the aspx page.
Step 1 - add the necessary entries to web.config
<system.web>
<compilation debug="true">
<assemblies>
…
<add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</assemblies>
</compilation>
…
<httpModules>
…
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</httpModules>
</system.web>
<system.webServer>
…
<modules>
…
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</modules>
<handlers
…
<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</handlers>
</system.webServer>
Step 2 - add a routing table in global.asax
Define the mapping from the friendly URL to the aspx page, saving the requested userid for later use.
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.Add("UseridRoute", new Route
(
"userid/{userid}",
new CustomRouteHandler("~/users.aspx")
));
}
Step 3 - implement the route handler
Pass the routing context, containing the parameter, to the page. (Note the definition of IRoutablePage)
using System.Web.Compilation;
using System.Web.UI;
using System.Web;
using System.Web.Routing;
public interface IRoutablePage
{
RequestContext RequestContext { set; }
}
public class CustomRouteHandler : IRouteHandler
{
public CustomRouteHandler(string virtualPath)
{
this.VirtualPath = virtualPath;
}
public string VirtualPath { get; private set; }
public IHttpHandler GetHttpHandler(RequestContext
requestContext)
{
var page = BuildManager.CreateInstanceFromVirtualPath
(VirtualPath, typeof(Page)) as IHttpHandler;
if (page != null)
{
var routablePage = page as IRoutablePage;
if (routablePage != null) routablePage.RequestContext = requestContext;
}
return page;
}
}
Step 4 - Retrieve the parameter on the target page
Note the implemetation of IRoutablePage.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Routing;
public partial class users : System.Web.UI.Page, IRoutablePage
{
protected RequestContext requestContext;
protected object RouteValue(string key)
{
return requestContext.RouteData.Values[key];
}
protected void Page_Load(object sender, EventArgs e)
{
string id = RouteValue("userid").ToString();
switch (id)
{
case "1234":
lblUserId.Text = id;
lblUserName.Text = "Bill";
break;
case "1235":
lblUserId.Text = id;
lblUserName.Text = "Claire";
break;
case "1236":
lblUserId.Text = id;
lblUserName.Text = "David";
break;
default:
lblUserId.Text = "0000";
lblUserName.Text = "Unknown";
break;
}
}
#region IRoutablePage Members
public RequestContext RequestContext
{
set { requestContext = value; }
}
#endregion
}
Here's another way of doing it using ASP.NET MVC
First off, here's the controller code with two actions. Index gets a list of users from the model, userid gets an individual user:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
namespace MvcApplication1.Controllers
{
public class UsersController : Controller
{
public ActionResult Index()
{
return View(Models.UserDB.GetUsers());
}
public ActionResult userid(int id)
{
return View(Models.UserDB.GetUser(id));
}
}
}
Here's the Index.asp view, it uses an ActionLink to create links in the correct format:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Index" %>
<%# Import Namespace="MvcApplication1.Controllers" %>
<%# Import Namespace="MvcApplication1.Models" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title></title>
</head>
<body>
<div>
<h2>Index of Users</h2>
<ul>
<% foreach (User user in (IEnumerable)ViewData.Model) { %>
<li>
<%= Html.ActionLink(user.name, "userid", new {id = user.id })%>
</li>
<% } %>
</ul>
</div>
</body>
</html>
And here's the userid.aspx view which displays an individual's details:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="userid.aspx.cs" Inherits="MvcApplication1.Views.Users.userid" %>
<%# Import Namespace="MvcApplication1.Controllers" %>
<%# Import Namespace="MvcApplication1.Models" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<div>
<table border ="1">
<tr>
<td>
ID
</td>
<td>
<%=((User)ViewData.Model).id %>
</td>
</tr>
<tr>
<td>
Name
</td>
<td>
<%=((User)ViewData.Model).name %>
</td>
</tr>
</table>
</div>
</body>
</html>
And finally for completeness, here's the model code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcApplication1.Models
{
public class UserDB
{
private static List<User> users = new List<User>{
new User(){id=12345, name="Bill"},
new User(){id=12346, name="Claire"},
new User(){id=12347, name="David"}
};
public static List<User> GetUsers()
{
return users;
}
public static User GetUser(int id)
{
return users.First(user => user.id == id);
}
}
public class User
{
public int id { get; set; }
public string name { get; set; }
}
}
I've been using a URL rewriter by Intelligencia:
http://urlrewriter.net/
It was so easy to configure - maybe an hour to get it all up and running. Very few problems with it...
I'd recommend it, but I should mentioned I've not tried any other ones.
Good luck!
Also, check out ASP.NET MVC or if you're set on webforms, the new System.Web.Routing namespace in ASP.NET 3.5 SP1
I've developed an open source NuGet library for this problem which implicitly converts EveryMvc/Url to every-mvc/url.
Dashed urls are much more SEO friendly and easier to read. Lowercase URLs tend to create less problems. (More on my blog post)
NuGet Package: https://www.nuget.org/packages/LowercaseDashedRoute/
To install it, simply open the NuGet window in the Visual Studio by right clicking the Project and selecting NuGet Package Manager, and on the "Online" tab type "Lowercase Dashed Route", and it should pop up.
Alternatively, you can run this code in the Package Manager Console:
Install-Package LowercaseDashedRoute
After that you should open App_Start/RouteConfig.cs and comment out existing route.MapRoute(...) call and add this instead:
routes.Add(new LowercaseDashedRoute("{controller}/{action}/{id}",
new RouteValueDictionary(
new { controller = "Home", action = "Index", id = UrlParameter.Optional }),
new DashedRouteHandler()
)
);
That's it. All the urls are lowercase, dashed, and converted implicitly without you doing anything more.
Open Source Project Url: https://github.com/AtaS/lowercase-dashed-route