I have amaster page that calls render action:
<% Html.RenderAction("CategoryList", "Category", new { selectedCategoryId = Model.selectedCategoryId }); %>
and the action looks like:
[ChildActionOnly]
[OutputCache(Duration = 10, VaryByParam = "none")]
public ActionResult CategoryList(int? selectedCategoryId)
{
CategoryList cl = CategoryManager.GetList();
if (selectedCategoryId.HasValue)
CategoryManager.SetSelectedCategory(cl, selectedCategoryId.Value);
return PartialView(cl);
}
But when i run SQL profiler i see that the GetList() query is always called, meaning the action is not being cached.
Any idea what i'm doing wrong?
Thanks!
It's a child action meaning that it is only a part of the final HTML and cannot be cached. For caching fragments of your HTML checkout this blog post.
its easy, use OutputCacheAttribute.
[OutputCache(Duration=60, VaryByParam="None")]
public ActionResult CacheDemo() {
return View();
}
Take care, Ragims
Related
I think about migrating from .net core mvc to razor pages so I am building demo application where I try the features from mvc i used and i stucked a little when I am trying to reload some part of the page based on ajax request using partial view. Sometimes partial view is very simple, like in the following example and sometimes very complex (it can contains additional nested partial views with forms etc. and suprisingly its working well).
My CustomersModel : PageModel handler looks like
it has return JsonResult because i need feedback about errors
sometimes I return more than one partial view
public JsonResult OnGetCustomerDetailPartialView(int id)
{
PopulateCustomers();
var model = new PartialViews.CustomerDetailViewModel()
{
Customer = Customers.Where(x => x.Id == id).FirstOrDefault()
};
var partialView = PartialViewHelper.PartialView("/PartialViews/CustomerDetailViewModel.cs", model, ViewData, TempData);
return new JsonResult(new { success = true, html = partialView.ToStringExtension() });
}
Partial View helper
public static class PartialViewHelper
{
public static PartialViewResult PartialView(string viewName, object model,
ViewDataDictionary viewData, ITempDataDictionary tempData)
{
viewData.Model = model; <-- this line throws error
return new PartialViewResult()
{
ViewName = viewName,
ViewData = viewData,
TempData = tempData
};
}
}
and the problem here is that i got an error
System.InvalidOperationException: 'The model item passed into the
ViewDataDictionary is of type
'RazorPages.PartialViews.CustomerDetailViewModel', but this
ViewDataDictionary instance requires a model item of type
'RazorPages.Pages.CustomersModel'.'
So the ViewData are bind to the CustomerModel, it is possible to return partial view specific ViewModel ?
The bottom line question is, should I approach Razor Pages as an replacement for the MVC or they are intended for less complicated projects than MVC ?
In response to the technical issue, try this version of your method:
public static class PartialViewHelper
{
public static PartialViewResult PartialView<T>(string viewName, object model, ViewDataDictionary viewData, ITempDataDictionary tempData)
{
return new PartialViewResult()
{
ViewName = viewName,
ViewData = new ViewDataDictionary<T>(viewData, model),
TempData = tempData
};
}
}
Then call it as follows (although the name of the partial view doesn't look right to me):
var partialView = PartialViewHelper.PartialView<PartialViews.CustomerDetailViewModel>("/PartialViews/CustomerDetailViewModel.cs", model, ViewData, TempData);
And in reply to the bottom line question, Razor Pages builds on MVC. Anything that you can do with MVC, you can also do with Razor Pages. It is intended to replace MVC for server-side generation of HTML. You can build as complicated an application as you like with it. But your code will likely be a lot simpler than an equivalent MVC application, which is a good thing, right?
I am trying to load an edit form which is a PartialView, into a div using jQuery. I am overloading the EditUser action. The 1st one is for passing the id and loading the form with existing details. The 2nd one is for posting back the form for save. But it seems to call the 2nd method when I load using jQuery and Url.Action. If I comment out the 2nd EditUser method, then it calls the 1st method. Why is that? How can I make it call the 1st one when I pass staffID? Or is there a better way to implement this Edit form in partial view scenario??
And the CreateUser action works just fine as there is no ambiguity on the overloaded methods as 1 has no parameters and the other has a model as parameter.
Thanks
This is my controller:
public PartialViewResult EditUser(String staffId)
{
User um = userService.GetUserDetails(1, staffId, true);
return PartialView(um);
}
[HttpPost]
public PartialViewResult EditUser(User um)
{
if (!TryUpdateModel(um))
{
ViewBag.updateError = "Edit Failure";
return PartialView("EditUser", um);
}
userService.CreateUpdateUser(um);
return PartialView("ViewUser", um);
}
public PartialViewResult CreateUser()
{
ViewBag.Message = "Create New User";
return PartialView(new User());
}
[HttpPost]
public ActionResult CreateUser(User um)
{
if (!TryUpdateModel(um))
{
ViewBag.updateError = "Create Failure";
return PartialView(um);
}
userService.CreateUpdateUser(um);
return View("Index");
}
This is how I am loading my EditUser partialview:
function menuEdit() {
$('#ActionMenu').hide();
$('#SearchBar').hide();
$('#SearchPanel').hide();
$('#SearchResult').hide();
$('#AddViewEditUser').load("#Url.Action("EditUser","User")", {staffId : sId});
$('#AddViewEditUser').show();
}
According to jQuery .load() "The POST method is used if data is provided as an object; otherwise, GET is assumed." Since you are providing data, .load() is using the method "POST", thus your second EditUser() is being called.
I'm creating a mvc3 canvas app using facebook c# sdk
The method name is create.
I also do a post and have another create method with [HttpPost] attribute.
When I add the [CanvasAuthorize(Permissions = ExtendedPermissions)] attribute to both the create methods, and a link from another page calls this create method, normally the get method should get called but in this case the post method gets called
But if I comment the post method then it goes to the get method.
Any ideas how to solve this.
Thanks
Arnab
This is because of the canvas authorization posting the access token into the page. The only way around it I've found is to create a different action that deals the post and use that action inside the view as post target. It will look something like this:
// /MyController/MyAction
// Post and Get
[CanvasAuthorize(Permissions = ExtendedPermissions]
public ActionResult MyAction(MyModel data)
{
MyModel modelData = data;
if(data==null)
{
modelData = new MyModel();
}
else
{
modelData = data;
}
return View(modelData);
}
// /MyController/MyActionPost
// POST only
[HttpPost]
[CanvasAuthorize(Permissions = ExtendedPermissions]
public ActionResult MyActionPost(MyModel data)
{
if(Model.IsValid)
{
//Processing code with a redirect at the end (most likely)
}
else
{
return View("MyAction", data);
}
}
Then in your MyAction view:
#using (Html.BeginForm("MyActionPost", "MyController"))
{
<!-- Form items go here-->
<inpuy type="submit" value="Submit" />
#Html.FacebookSignedRequest()
}
I have the same issue. It was doing a GET before, then suddenly when browse to an action with [CanvasAuthorize(Permissions = ExtendedPermissions)] attribute, it's doing a POST instead of a GET.
I have an Index action on a controller that's not doing anything.
public EmptyModel Index()
{
return null;
}
The Index view simply displays some html, with jQuery-driven ajax and the MasterPage doing all the heavy lifting on this particular page. When I remove this action function from it's controller, the aspx view will no longer display.
More Information and Update:
After making the changes mentioned in Chad's answer the url that used to return the index view now instead returns a 404. This issue may exist because most of the views' folder structure is done in the early Fubu Framework style (with View_Page_Type_Declarations.cs and no code-behinds), rather than using the more intuitive and more recent default folder conventions. But it's possible my analysis is off.
Here's my FubuRegistry:
public WebAppFubuRegistry()
{
IncludeDiagnostics(true);
Services(x => x.SetServiceIfNone<IWebAppSecurityContext, WebAppSecurityContext>());
Applies.ToThisAssembly()
.ToAssemblyContainingType<HomeController>();
Actions
.IncludeClassesSuffixedWithController();
Routes
.UrlPolicy<WebAppUrlPolicy>()
.IgnoreControllerNamespaceEntirely()
.ConstrainToHttpMethod(action => action.Method.Name.StartsWith("Perform"), "POST");
Views
.TryToAttach(x=> x.by<ViewAndActionInDifferentFolders>())
.TryToAttachWithDefaultConventions()
.RegisterActionLessViews(WebFormViewFacility.IsWebFormView,
chain => chain.PartialOnly());
/*Behavior Code */
}
WebAppUrlPolicy:
public class WebAppUrlPolicy : IUrlPolicy
{
public bool Matches(ActionCall call, IConfigurationObserver log)
{
return true;
}
public IRouteDefinition Build(ActionCall call)
{
if(call.IsForHomeController())
return new RouteDefinition("home");
if(call.IsAnIndexCall())
return new RouteDefinition(call.ControllerPrefix());
var otherRoute = new RouteDefinition(call.ToControllerActionRoute());
return otherRoute;
}
}
ViewAndActionInDifferentFolders:
public class ViewAndActionInDifferentFolders : IViewsForActionFilter
{
public IEnumerable<IViewToken> Apply(ActionCall call, ViewBag views)
{
if (call.IsForHomeController())
{
var viewTokens = views.ViewsFor(call.OutputType()).Where(x => x.Name == "HomeIndexView");
return new[] { new WebAppViewToken(call, viewTokens, "home") };
}
if (call.IsJsonCall())
{
return new List<IViewToken>();
}
return CreateSingleTokenList(call, views);
}
private static IEnumerable<WebAppViewToken> CreateSingleTokenList(ActionCall call, ViewBag views)
{
return new[] { new WebAppViewToken(call, views.ViewsFor(call.OutputType())) };
}
}
How do I reconfigure Fubu so that I can use a view without the action?
What changes need to be made to remove the action function above, and still maintain the same functionality?
In your FubuRegistry, in the "Views" section, add:
.RegisterActionLessViews(WebFormViewFacility.IsWebFormView, chain => chain.PartialOnly());
For example, the whole views section may look like:
Views
.TryToAttachWithDefaultConventions()
.RegisterActionLessViews(
WebFormViewFacility.IsWebFormView,
chain => chain.PartialOnly());
Note that you can both ASPX and ASCX for headless views. If you only want ASCX files, then you can use WebFormViewFacility.IsWebFormControl instead.
Works for me:
Views.RegisterActionLessViews(type => type.Name == "StaticView",
chain => chain.Route = new RouteDefinition("StaticView"));
I'm a little new to asp.net mvc and I have a question (very basic). I have hacked around but I am not totally sure about this and I could'nt find anything particularly helpful.
Assume that I have 2 controllers A and B and 2 views FullView and PartView
public class AController:...
{
//Renders FullView
public ActionResult Create
{
....
}
[HttpPost]
public ActionResult Create
{
....
}
}
public class BController:...
{
//Renders an Arbitrary partial View (PartView)
public ActionResult Create
{
....
}
//Saves the data of the partial View
[HttpPost]
public ActionResult Create
{
....
}
}
the 1st view (FullView) has the code
<%Html.RenderAction("Create", "B"); %>
my question is on submit will BController's action ([HttpPost] Create) run?
Thank you
That depends on what action you specify in your <form /> tag. This doesn't have anything to do with asp.net mvc. If you use Html.BeginForm() without parameters it will post to the current url (not the create action on BController).
Well 1st thing you could do is toggle some breakpoints in your actions and hit f5.
Second - what action is called purely depends on what url you hit with what http method.
But for your case, when you post form A and controller A processes post you might get into validation problems and that's when you return View() on a post action and that's why form B is rendered via its post method.