I am trying to run my ASP.NET MVC application but it is not running would you be kind to let me understand what I did wrong, thanks.
Model
namespace Mytest.Models {
public class ProductsListViewMode {
public IEnumerable<Product> Products { get; set; }
public PagingInfo PagingInfo { get; set; }
public string CurrentCategory { get; set; }
}
}
Controller
public ViewResult List(string categoryStr,int page = 1)
{
try
{
ProductsListViewModel model = new ProductsListViewModel{
Products = repository.Products
.Where(p => categoryStr == null || p.CategoryInfo == categoryStr)
.OrderBy(p => p.ProductID)
.Skip((page - 1) * PageSize)
.Take(PageSize),
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = categoryStr == null ?
repository.Products.Count() :
repository.Products.Where(c=>c.CategoryInfo==categoryStr).Count()
},
CurrentCategory = categoryStr
};
return View(model);//These change
}
catch(InvalidOperationException ex)
{
return View(model:ex);
}
}
and #view
#model Mytest.Models.ProductsListViewModel
#{
ViewBag.Title = "Products";
}
#foreach (var p in Model.Products)
{
Html.RenderPartial("ProductSummary",p);
}<hr/>
<div class="pager">
#Html.PageLinks(Model.PagingInfo, x=>Url.Action("List", new{page=x, categoryStr=Model.CurrentCategory}))
</div>
These are the error messages
[InvalidOperationException: The model item passed into the dictionary is of type 'System.InvalidOperationException', but this dictionary requires a model item of type 'LeoSaFashion.Models.ProductsListViewModel'.]
System.Web.Mvc.ViewDataDictionary`1.SetModel(Object value)+175
System.Web.Mvc.ViewDataDictionary..ctor(ViewDataDictionary dictionary)+107
System.Web.Mvc.WebViewPage`1.SetViewData(ViewDataDictionary viewData)+49
System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance)+99
System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer)+107
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)+291
System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)+13
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)+56
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)+420
System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult)+52
System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c()+173
System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult)+100
System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult)+10 System.Web.Mvc.Async.WrappedAsyncResultBase`1.End()+49
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult)+27
System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState)+13
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)+29
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End()+49
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult)+36
System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller)+12
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)+22
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End()+49
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult)+26
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult)+10
System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState)+21
System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)+29
System.Web.Mvc.Async.WrappedAsyncResultBase`1.End()+49
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)+28
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)+9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()+9748493 System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)+48 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)+159
Maybe this is because you return "View(model:ex)" from your catch block. Please debug your code and check why an exception is thrown.
You're trying to pass an exception to your view:
return View(model:ex);
But, as the error states, the view isn't expecting an exception. It's expecting a specific model:
#model Mytest.Models.ProductsListViewModel
So it appears that an attempt to handle an error is itself causing an error. The question then really becomes:
How do you want to handle an error?
Do you really want to return the view but without any data? Do you want to redirect to an error page? Log the error in some way? Something else?
Ultimately you need to decide specifically what you want to do in order to handle an error. You can even rely on the framework to direct to an error page for you and simply not handle it yourself at all. (There are a variety of options available to you here, a Google search for "ASP .NET MVC error handling" will yield many articles and examples.)
Then, once you are meaningfully handling the error, you're half-way there. At that point you need to find out what that error was in the first place and begin to correct that error.
Related
I am working on a form that is generated dynamically based on some meta-data tables in my database. I create input tags with names like setting_1, setting_53, setting_22 where the number is the primary key of the meta-data. Since the content is dynamic, I am using FormCollection as the sole parameter on POST requests.
Question 1: Is there a FormCollection-like class for GET requests? I want direct access to the query parameters.
Question 2: If I need to pass these query parameters around, is there an easy/safe way to build my URLs?
One of my big concerns is that some of the settings are populated via OAuth, so the user will be redirected to an external page. I will have to pass the query string as "state" which I will need to recover once the user returns. I will need to use this state to pick up where the user left off in the form entry process. All the more reason why I need a very fool-proof mechanism for passing query parameters around.
Has anyone dealt with dynamic pages like these? Are there good patterns and practices for passing these pages around?
Well, you can certainly look at Request.QueryString inside of a controller action.
But if it were me doing it, I'd write a custom model binder instead.
Here's a sample model binder. I haven't tested this!
public class MyModelBinder: DefaultModelBinder
{
private static void BindSettingProperty(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor)
{
if (propertyDescriptor.PropertyType != typeof(IDictionary<string, string>))
{
throw new InvalidOperationException("This binder is for setting dictionaries only.");
}
var originalValue = propertyDescriptor.GetValue(bindingContext.Model) as IDictionary<string, string>;
var value = originalValue ?? new Dictionary<string, string>();
var settingKeys = controllerContext.HttpContext.Request.QueryString.AllKeys.Where(k => k.StartsWith("setting_", StringComparison.OrdinalIgnoreCase));
foreach (var settingKey in settingKeys)
{
var key = settingKey.Substring(8);
value.Add(key, bindingContext.ValueProvider.GetValue(settingKey).AttemptedValue);
}
if (value.Any() && (originalValue == null))
{
propertyDescriptor.SetValue(bindingContext.Model, value);
}
}
protected override void BindProperty(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor)
{
if (propertyDescriptor.Name.StartsWith("setting_", StringComparison.OrdinalIgnoreCase)
{
BindSettingProperty(controllerContext, bindingContext, propertyDescriptor);
}
else
{
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}
}
I have this ASP.NET MVC3 code that's powered by Spring and Fluent NHibernate (NHIB 3.1)
I have this error:
could not resolve property: User.Full_Name of: Harrods.Core.Entities.Teacher
[QueryException: could not resolve property: User.Full_Name of: Harrods.Core.Entities.Teacher]
NHibernate.Persister.Entity.AbstractPropertyMapping.GetColumns(String propertyName) +104
NHibernate.Persister.Entity.AbstractPropertyMapping.ToColumns(String alias, String propertyName) +57
NHibernate.Persister.Entity.BasicEntityPropertyMapping.ToColumns(String alias, String propertyName) +100
NHibernate.Persister.Entity.AbstractEntityPersister.ToColumns(String alias, String propertyName) +53
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetColumns(ICriteria subcriteria, String propertyName) +184
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetColumnsUsingProjection(ICriteria subcriteria, String propertyName) +134
NHibernate.Criterion.CriterionUtil.GetColumnNamesUsingPropertyName(ICriteriaQuery criteriaQuery, ICriteria criteria, String propertyName) +45
NHibernate.Criterion.CriterionUtil.GetColumnNames(String propertyName, IProjection projection, ICriteriaQuery criteriaQuery, ICriteria criteria, IDictionary`2 enabledFilters) +46
NHibernate.Criterion.InsensitiveLikeExpression.ToSqlString(ICriteria criteria, ICriteriaQuery criteriaQuery, IDictionary`2 enabledFilters) +101
NHibernate.Loader.Criteria.CriteriaQueryTranslator.GetWhereCondition(IDictionary`2 enabledFilters) +298
NHibernate.Loader.Criteria.CriteriaJoinWalker..ctor(IOuterJoinLoadable persister, CriteriaQueryTranslator translator, ISessionFactoryImplementor factory, ICriteria criteria, String rootEntityName, IDictionary`2 enabledFilters) +447
NHibernate.Loader.Criteria.CriteriaLoader..ctor(IOuterJoinLoadable persister, ISessionFactoryImplementor factory, CriteriaImpl rootCriteria, String rootEntityName, IDictionary`2 enabledFilters) +175
NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results) +412
NHibernate.Impl.CriteriaImpl.List(IList results) +80
NHibernate.Impl.CriteriaImpl.List() +104
NHibernate.Criterion.QueryOver`1.List() +96
NHibernate.Criterion.QueryOver`1.NHibernate.IQueryOver<TRoot>.List() +75
This is the code that the problem seems to be coming from:
return session.QueryOver<Teacher>()
.Where(x => x.User.Full_Name.IsInsensitiveLike("%" + Search + "%"))
.OrderBy(x => x.Id).Asc
.Skip((skip - 1) * take)
.Take(take)
.List<Teacher>();
My Teacher class looks like this:
[Serializable]
public class Teacher
{
public virtual User User { get; set; }
}
And my User class looks something like this:
public class User : BaseEntity<User>
{
public virtual string Email { get; set; }
public virtual string Full_Name { get; set; }
}
Not sure what's giving the problem. Everything looks okay to me. Anyone have any ideas whats wrong? :)
Not sure but they look somewhat similar, but don't really understand how to solve anyway:
NHibernate.QueryException with dynamic-component
A correct way to load entities by Id list when Id is not mapped
EDIT:
Tried this, but still giving the same error:-
return session.QueryOver<Teacher>()
.JoinQueryOver<User>(u => u.User)
.Where(x => x.Full_Name.IsInsensitiveLike("%" + FullNameSearchFilter + "%"))
.OrderBy(x => x.Id).Asc
.Skip((skip - 1) * take)
.Take(take)
.List<Teacher>();
Tried alias as well..:
return session.QueryOver<Teacher>()
.JoinAlias(() => ML.User, () => U)
.Where(() => U.Full_Name.IsInsensitiveLike("%" + FullNameSearchFilter + "%"))
.OrderBy(x => x.Id).Asc
.Skip((skip - 1) * take)
.Take(take)
.List<Teacher>();
Neither working!
EDIT 2
the sql codes is being showed correctly; I can verify this via my NHibernate Profiler. But for some reason once the data has been loaded, it pops the error and the transaction is rolled back. Now it appears to me that this is no longer just about queryover?
You'll have to use a JoinQueryOver or JoinAlias to accomplish what you are asking for.
What is the difference between JoinQueryOver and JoinAlias?
Take a look at sections Associations and Aliases in this article: http://nhforge.org/blogs/nhibernate/archive/2009/12/17/queryover-in-nh-3-0.aspx
This worked for me:
User userAlias = null;
var list = session.QueryOver<Teacher>()
.JoinQueryOver(x => x.User, () => userAlias, JoinType.LeftOuterJoin)
.Where(x => x.FullName.IsInsensitiveLike("%" + "test" + "%"))
.OrderBy(x => x.Id).Asc
.Skip(1)
.Take(2)
.List(); //Without MasterLicensee
Produced SQL (I'm using SQL-CE for tests):
SELECT
this_.Id as Id2_1_,
this_.User_id as User2_2_1_,
useralias1_.Id as Id3_0_,
useralias1_.Email as Email3_0_,
useralias1_.FullName as FullName3_0_
FROM
"Teacher" this_
left outer join
"User" useralias1_
on this_.User_id=useralias1_.Id
WHERE
lower(useralias1_.FullName) like #p0
ORDER BY
useralias1_.Id asc;
#p0 = '%test%' [Type: String (0)]
How can i tell my controller/model what kind of culture it should expect for parsing a datetime?
I was using some of this post to implement jquery datepicker into my mvc application.
When i submit the date it gets "lost in translation" i'm not using the US formatting for the date, so when it gets sent to my controller it simply becomes null.
I have a form where the user chooses a date:
#using (Html.BeginForm("List", "Meter", FormMethod.Get))
{
#Html.LabelFor(m => m.StartDate, "From:")
<div>#Html.EditorFor(m => m.StartDate)</div>
#Html.LabelFor(m => m.EndDate, "To:")
<div>#Html.EditorFor(m => m.EndDate)</div>
}
I've made an edit template for this, to implement the jquery datepicker:
#model DateTime
#Html.TextBox("", Model.ToString("dd-MM-yyyy"), new { #class = "date" })
I then create the datepicker widgets like this.
$(document).ready(function () {
$('.date').datepicker({ dateFormat: "dd-mm-yy" });
});
All this works fine.
Here is where the problems start, this is my controller:
[HttpGet]
public ActionResult List(DateTime? startDate = null, DateTime? endDate = null)
{
//This is where startDate and endDate becomes null if the dates dont have the expected formatting.
}
This is why i would like to somehow tell my controller what culture it should expect?
Is my model wrong? can i somehow tell it which culture to use, like with the data annotation attributes?
public class MeterViewModel {
[Required]
public DateTime StartDate { get; set; }
[Required]
public DateTime EndDate { get; set; }
}
Edit: this link explains my issue and a very good solution to it aswell. Thanks to gdoron
you can change the default model binder to use the user culture using IModelBinder
public class DateTimeBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
var date = value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);
return date;
}
}
And in the Global.Asax write:
ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeBinder());
Read more at this excellent blog that describe why Mvc framework team implemented a default Culture to all users.
You can create a Binder extension to handle the date in the culture format.
This is a sample I wrote to handle the same problem with Decimal type, hope you get the idea
public class DecimalModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
ModelState modelState = new ModelState { Value = valueResult };
object actualValue = null;
try
{
actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
Update
To use it simply declare the binder in Global.asax like this
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//HERE you tell the framework how to handle decimal values
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
DependencyResolver.SetResolver(new ETAutofacDependencyResolver());
}
Then when the modelbinder has to do some work, it will know automatically what to do.
For example, this is an action with a model containing some properties of type decimal. I simply do nothing
[HttpPost]
public ActionResult Edit(int id, MyViewModel viewModel)
{
if (ModelState.IsValid)
{
try
{
var model = new MyDomainModelEntity();
model.DecimalValue = viewModel.DecimalValue;
repository.Save(model);
return RedirectToAction("Index");
}
catch (RulesException ex)
{
ex.CopyTo(ModelState);
}
catch
{
ModelState.AddModelError("", "My generic error message");
}
}
return View(model);
}
This issue arises because you are using the GET method on your Form. The QueryString Value Provider in MVC always uses Invariant/US date format. See: MVC DateTime binding with incorrect date format
There are three solutions:
Change your method to POST.
As someone else says, change the date format to ISO 8601 "yyyy-mm-dd" before submission.
Use a custom binder to always treat Query String dates as GB. If you do this you have to make sure that all dates are in that form:
public class UKDateTimeModelBinder : IModelBinder
{
private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Fixes date parsing issue when using GET method. Modified from the answer given here:
/// https://stackoverflow.com/questions/528545/mvc-datetime-binding-with-incorrect-date-format
/// </summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="bindingContext">The binding context.</param>
/// <returns>
/// The converted bound value or null if the raw value is null or empty or cannot be parsed.
/// </returns>
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var vpr = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (vpr == null)
{
return null;
}
var date = vpr.AttemptedValue;
if (String.IsNullOrEmpty(date))
{
return null;
}
logger.DebugFormat("Parsing bound date '{0}' as UK format.", date);
// Set the ModelState to the first attempted value before we have converted the date. This is to ensure that the ModelState has
// a value. When we have converted it, we will override it with a full universal date.
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, bindingContext.ValueProvider.GetValue(bindingContext.ModelName));
try
{
var realDate = DateTime.Parse(date, System.Globalization.CultureInfo.GetCultureInfoByIetfLanguageTag("en-GB"));
// Now set the ModelState value to a full value so that it can always be parsed using InvarianCulture, which is the
// default for QueryStringValueProvider.
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, new ValueProviderResult(date, realDate.ToString("yyyy-MM-dd hh:mm:ss"), System.Globalization.CultureInfo.GetCultureInfoByIetfLanguageTag("en-GB")));
return realDate;
}
catch (Exception)
{
logger.ErrorFormat("Error parsing bound date '{0}' as UK format.", date);
bindingContext.ModelState.AddModelError(bindingContext.ModelName, String.Format("\"{0}\" is invalid.", bindingContext.ModelName));
return null;
}
}
}
When submitting a date you should always try and submit it in the format "yyyy-MM-dd". This will allow for it to become culture independent.
I normally have a hidden field which maintains the date in this format. This is relatively simple using jQuery UI's datepicker.
Why not simply inspect the culture of the data and convert it as such? This simple approach allowed me to use strongly typed dates in models, show action links and edit fields in the desired locale and not have to fuss at all binding it back into a strongly typed DateTime:
public class DateTimeBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
return value.ConvertTo(typeof(DateTime), value.Culture);
}
}
that did the trick for me
<system.web>
<globalization enableClientBasedCulture="true" uiCulture="Auto" culture="Auto" />
</system.web>
I have a updated solution for MVC5 based on the Post of #gdoron. I will share it in case anyone else is looking for this. The class inherits from DefaultModelBinder and has exception handling for invalid dates. It also can handle null values:
public class DateTimeModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object result = null;
var modelName = bindingContext.ModelName;
var attemptedValue = bindingContext.ValueProvider.GetValue(modelName)?.AttemptedValue;
// in datetime? binding attemptedValue can be Null
if (attemptedValue != null && !string.IsNullOrWhiteSpace(attemptedValue))
{
try
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
result = DateTime.Parse(value.AttemptedValue, CultureInfo.CurrentCulture);
}
catch (FormatException e)
{
bindingContext.ModelState.AddModelError(modelName, e);
}
}
return result;
}
}
And just like the mentioned sample in the Global.Asax write
ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeBinder());
Basically, I was wondering if anyone knows of a way that you can set up MVC3 in a way that it will first look for an action, and if none exists, it will automatically return the view at that location. Otherwise each time I make a page, I will have to rebuild it after adding the action.
It isn't something that's stopping the project from working nor is it an issue, it would just be a very nice thing to include in the code to help with speed of testing more than anything.
EDIT:
Just for clarity purposes, this is what I do every time I create a view that doesn't have any logic inside it:
public ActionResult ActionX()
{
return View();
}
Sometimes I will want some logic inside the action, but majority of the time for blank pages I will just want the above code.
I would like it if there was any way to always return the above code for every Controller/Action combination, UNLESS I have already made an action, then it should use the Action that I have specified.
Thanks,
Jake
Why not just create a single action for this. This will look for a view with the specified name and return a 404 if it doesn't exist.
[HttpGet]
public ActionResult Page(string page)
{
ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, page, null);
if (result == null)
{
return HttpNotFound();
}
return View(page);
}
Then make your default route fall back to this:
routes.MapRoute("", "{page}", new { controller = "Home", action = "Page" });
So a request to http://yoursite.com/somepage will invoke Page("somepage")
I'm not altogether sure how useful this will be (or whether its really a good idea) but I guess if you have pages which are purely static content (but maybe use a layout or something so you can't use static html) it could be useful
This is how it could be done though anyway (as a base class, but it doesn't have to be)
public abstract class BaseController : Controller
{
public ActionResult Default()
{
return View();
}
protected override IActionInvoker CreateActionInvoker()
{
return new DefaultActionInvoker();
}
private class DefaultActionInvoker : ControllerActionInvoker
{
protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
var actionDescriptor = base.FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor == null)
actionDescriptor = base.FindAction(controllerContext, controllerDescriptor, "Default");
return actionDescriptor;
}
}
}
has anyone seen this issue? I'm an ASP.NET MVC newbie. I have an ASP.NET MVC 3 site that uses .aspx and .ascx views. Throughout the site, I use return View() or return View(viewName) in my controller methods, which directs to the appropriate aspx/ascx view. However, in my stock AccountController (modified to use DotNetOpenAuth), I take the same approach, but the MVC framework doesn't seek out aspx or ascx views. Instead, it's searching the path for .cshtml or .vbhtml views (Razor engine I'm assuming). Why woulnd't it just seek out aspx and ascx views like the rest of my site? Controller method follows:
public ActionResult Authenticate()
{
var response = openid.GetResponse();
var statusMessage = "";
if (response == null)
{
Identifier id;
//make sure your users openid_identifier is valid.
if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
{
try
{
//request openid_identifier
return openid.CreateRequest(Request.Form["openid_identifier"])
.RedirectingResponse.AsActionResult();
}
catch (ProtocolException ex)
{
statusMessage = ex.Message;
return View("LogOn", statusMessage);
}
}
else
{
statusMessage = "Invalid identifier";
return View("LogOn", statusMessage);
}
}
else
{
//check the response status
switch (response.Status)
{
//success status
case AuthenticationStatus.Authenticated:
Session["FriendlyIdentifier"] = response.FriendlyIdentifierForDisplay;
FormsAuthentication.SetAuthCookie(response.ClaimedIdentifier, false);
//TODO: response.ClaimedIdentifier, to login or create new account
return RedirectToAction("Index", "Home");
case AuthenticationStatus.Canceled:
statusMessage = "Canceled at provider";
return View("LogOn", statusMessage);
case AuthenticationStatus.Failed:
statusMessage = response.Exception.Message;
return View("LogOn", statusMessage);
}
}
return View("LogOn");
}
Error detail follows:
The view 'LogOn' or its master was not
found or no view engine supports the
searched locations. The following
locations were searched:
~/Views/Account/Canceled at
provider.master
~/Views/Shared/Canceled at
provider.master
~/Views/Account/LogOn.cshtml
~/Views/Account/LogOn.vbhtml
~/Views/Shared/LogOn.cshtml
~/Views/Shared/LogOn.vbhtml
~/Views/Account/Canceled at
provider.cshtml
~/Views/Account/Canceled at
provider.vbhtml
~/Views/Shared/Canceled at
provider.cshtml
~/Views/Shared/Canceled at
provider.vbhtml
Description: An unhandled exception
occurred during the execution of the
current web request. Please review the
stack trace for more information about
the error and where it originated in
the code.
Exception Details:
System.InvalidOperationException: The
view 'LogOn' or its master was not
found or no view engine supports the
searched locations. The following
locations were searched:
~/Views/Account/Canceled at
provider.master
~/Views/Shared/Canceled at
provider.master
~/Views/Account/LogOn.cshtml
~/Views/Account/LogOn.vbhtml
~/Views/Shared/LogOn.cshtml
~/Views/Shared/LogOn.vbhtml
~/Views/Account/Canceled at
provider.cshtml
~/Views/Account/Canceled at
provider.vbhtml
~/Views/Shared/Canceled at
provider.cshtml
~/Views/Shared/Canceled at
provider.vbhtml
Source Error:
An unhandled exception was generated
during the execution of the current
web request. Information regarding the
origin and location of the exception
can be identified using the exception
stack trace below.
Stack Trace:
[InvalidOperationException: The view
'LogOn' or its master was not found or
no view engine supports the searched
locations. The following locations
were searched:
~/Views/Account/Canceled at
provider.master
~/Views/Shared/Canceled at
provider.master
~/Views/Account/LogOn.cshtml
~/Views/Account/LogOn.vbhtml
~/Views/Shared/LogOn.cshtml
~/Views/Shared/LogOn.vbhtml
~/Views/Account/Canceled at
provider.cshtml
~/Views/Account/Canceled at
provider.vbhtml
~/Views/Shared/Canceled at
provider.cshtml
~/Views/Shared/Canceled at
provider.vbhtml]
System.Web.Mvc.ViewResult.FindView(ControllerContext
context) +315050
System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext
context) +129
System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext
controllerContext, ActionResult
actionResult) +13
System.Web.Mvc.<>c_DisplayClass1c.b_19()
+23 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter
filter, ResultExecutingContext
preContext, Func1 continuation) +260
System.Web.Mvc.<>c__DisplayClass1e.<InvokeActionResultWithFilters>b__1b()
+19 System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext
controllerContext, IList1 filters,
ActionResult actionResult) +177
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext
controllerContext, String actionName)
+343 System.Web.Mvc.Controller.ExecuteCore()
+116 System.Web.Mvc.ControllerBase.Execute(RequestContext
requestContext) +97
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext
requestContext) +10
System.Web.Mvc.<>c_DisplayClassb.b_5()
+37 System.Web.Mvc.Async.<>c_DisplayClass1.b_0()
+21 System.Web.Mvc.Async.<>c_DisplayClass81.<BeginSynchronous>b__7(IAsyncResult
_) +12 System.Web.Mvc.Async.WrappedAsyncResult1.End()
+62 System.Web.Mvc.<>c_DisplayClasse.b_d()
+50 System.Web.Mvc.SecurityUtil.b_0(Action
f) +7
System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action
action) +22
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult
asyncResult) +60
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult
result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
+8841105 System.Web.HttpApplication.ExecuteStep(IExecutionStep
step, Boolean& completedSynchronously)
+184
Thanks
Shan
The key is in the first line of your exception:
The view 'LogOn' or its master was not found or no view engine supports
the searched locations. The following locations were searched:
~/Views/Account/Canceled at provider.master
If you pass two strings to View(), the first one is the view name and the second one is the name of the master view or template to use. If you want to pass statusMessage as the model for your view you can cast it to object which will force calling the overridden View() method where you pass a model:
return View("LogOn", (object)statusMessage);
You are apparently getting the "Cancelled at provider" message and passing that as the master page name to use. If the "Logon.aspx" view used a master page and you had a master page "Canceled at provider.master" in your Shared view folder for instance, this would load the "LogOn.aspx" view and force it to use the "Canceled at provider.master" master page even if it was setup to use a different master page by default:
string statusMessage = "Canceled at provider";
return View("LogOn", statusMessage);