I'm working on my first ASP.NET MVC application and have a strange issue. All the tutorials in regards to using strongly typed ViewData don't require casting/eval of ViewData / Model object but I get compilation errors if I don't cast to the ViewData object
ViewData class:
public class CategoryEditViewData
{
public Category category { get; set; }
}
Controller Action:
public ActionResult Edit(int id)
{
Category category = Category.findOneById(id);
CategoryEditViewData ViewData = new CategoryEditViewData();
ViewData.category = category;
return View("Edit", ViewData);
}
Works:
<%=Html.TextBox("name",
((Project.Controllers.CategoryEditViewData)Model).category.Name)) %>
Doesn't Work:
<%=Html.TextBox("name", Model.category.Name)) %>
Is there something that I am doing incorrectly - or do I have to cast to the object in the view all the time?
First, you should move the CategoryEditViewData class out of your controllers namespace, and into your models namespace. Create a new class under the Models folder to see what it should look like. It is good practice to put your models under the models folder.
Then your Control directive should look like this:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Models.CategoryEditViewData >" %>
Wait, just thought of something. In your view are you inheriting from you model????
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<namespace.Controllers.YourFormViewModel>" %>
Related
I have been trying to figure out how to populate a specfic interface via a form in a view. The interface is in a different project and namespace then that of my controller / view and is automatically generated for storing data in the database:
Interface namespace and Code:
DataAccess.DAL.IVehicle
namespace DataAccess.DAL
{
public partial interface IVehicle
{
String vehicleName { get; set; }
int maxSpeed { get; set; }
}
}
I have a controller which has an action method for receiving information from the form in the view:
Controller Code:
namespace coreproject.Controllers
{
public class NewVehicleController
{
[HttpPost, ValidateInput(false)]
public JsonResult AddVechicle(IVehicle newVehicle)
{
// I expect that newVechicle is populated via the form
}
}
}
I understand that I should be using Html.BeginForm in the view. Below is some code I came up with what I understand would be needed in the view.
View Code:
<%
// This is not working, I am not sure how to tell the view I want the form
// to use the interface located in the following namespace.
#Model DataAccess.DAL.IVehicle;
using (Html.BeginForm("AddVehicle", "NewVechicle", FormMethod.Post))
// Below I understand that I would need some code in the form of Html.EditorFor to
// populate the IVehicle interface in the form. I have seen this as an example:
<%: Html.EditorFor(model => model.VehicleName) %>
<%: Html.EditorFor(model => model.maxSpeed) %>
<%:
}
%>
The questions I have are twofold and are related to the view:
How do I tell the view I want to use an interface located in DataAccess.DAL, which resides in a different project and namespace than the view?
How do I populate the aforementioned interface in the form in order to pass it to the controller?
Any help would be greatly appreciated.
You are mixing a lot of concepts here.
Go to Visual Studio and create a new MVC website.
Run it and see how it works.
Then go on google and lookup the concept of interfaces.
Go back to your newly created MVC website and see the difference to what you have postet here.
Edit:
What you are trying is not possible!
You are asking the MVC framework to create an instance of an interface, this is not possible!
What you must do is to have a concrete class in the Action parameter:
[HttpGet]
public ActionResult AddVechicle()
{
return View(new Vehicle());
}
[HttpPost, ValidateInput(false)]
public JsonResult AddVechicle(Vehicle newVehicle)
{
// I expect that newVechicle is populated via the form
}
you could then declare the "Vehicle" class as follows
public class Vehicle :IVehicle
{
String vehicleName { get; set; }
int maxSpeed { get; set; }
}
I havent testet if the view will accept an interface as a model, you might better change it into the class "Vehicle"
<%
// view name: AddVehicle
// This is not working, I am not sure how to tell the view I want the form
// to use the interface located in the following namespace.
#Model Vehicle;
using (Html.BeginForm("AddVehicle", "NewVechicle", FormMethod.Post))
// Below I understand that I would need some code in the form of Html.EditorFor to
// populate the Vehicle concrete class in the form.
<%: Html.EditorFor(model => model.VehicleName) %>
<%: Html.EditorFor(model => model.maxSpeed) %>
<%:
}
%>
Our team is currently developing a web application, in that we have a class library with Entity Framework .edmx added and have generated the POCO classes.
Our Web Application is based on MVC, we have defined our classes in models with the same name and attributes (copy paste of the POCO classes from the .edmx). The .edmx class library is refrenced to MVC web application.
The Views are strongly typed of MVC Model classes. We have used MVC Models for Display, StringLength & Required.
In our controller when there is a CRUD operation we are accepting the POCO Classes Type such as
public ActionResult Create(EFModels.User user) { }
EFModels.User is a class from the .edmx (POCO generated class) and the MVC View is strongly typed to the model which is MvcWebApplication.Models.User.
Question is how are we getting data from the MvcWebApplication.Models.User (from Model) to EFModels.User (EF class) in the ActionResult Create ??
I am able to get the data, I know it is coz of the same property name. I tried changing the class name but still it works, but if we change the property name it does not work. I cannot understand the logic behind it.
Initially we never knew it didn`t work and we were using AutoMapper to convert the Model Class to Edmx POCO class.
Any ideas, Thanks.
The question is how are we getting the values of the Model Class to the EF class with any mapping. I don`t need to use AutoMapper, without using that I am getting the values.
Have a look at the code, hope that explains better...
//POCO CLASS
namespace EFModels
{
using System;
using System.Collections.Generic;
public partial class User
{
public int Id { get; set; }
public string Type { get; set; }
public string Name { get; set; }
}
}
//MVC Model Class
namespace MvcWebSamp.Models
{
public class User
{
public int Id { get; set; }
[Display(ResourceType = typeof(BasicTags), Name = "Type")]
[StringLength(15, ErrorMessageResourceName = "TypeLength", ErrorMessageResourceType = typeof(BasicTags))]
[Required(ErrorMessageResourceName = "TypeRequired", ErrorMessageResourceType = typeof(BasicTags))]
public string TypeName { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
}
//MVC VIEW PAGE
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcWebSamp.Models.User>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
User
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>User</h2>
<% using (Html.BeginForm("Create", "User", FormMethod.Post))
{
%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>User</legend>
<div class="editor-field">
<%: Html.LabelFor(model => model.TypeName) %>
<%: Html.EditorFor(model => model.TypeName)%>
<%: Html.ValidationMessageFor(model => model.TypeName)%>
</div>
<div class="editor-field">
<%: Html.LabelFor(model => model.Name) %>
<%: Html.EditorFor(model => model.Name)%>
<%: Html.EditorFor(model => model.Address.street)%>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>
</asp:Content>
//Controller Method
public ActionResult Create(EFModels.User user)
{
Model1Container con = new Model1Container();
con.Users.Add(user);
con.SaveChanges();
return View("User");
}
When I hit the Create Button, I am posting data of the type MvcWebSamp.Models.User and in the Create Action I am able to get the data of the type EFModels.User user without using any AutoMapper. I want to know how this works???
You should be using your view model as the argument type for your create method:
[HttpPost]
public ActionResult Create(UserViewModel model)
{
if (ModelState.IsValid)
{
int id = UserService.CreateFromViewModel(model);
return RedirectToAction("View", new { id });
}
return View(model);
}
You controller should be designed to create and accept view models, and it passes those to an appropriate service which interacts with your data layer to create your domain model. This keeps your controller action quite thin.
You can use something like AutoMapper in your service to easily map between your view model and your domain model:
var user = Mapper.Map<UserViewModel, User>(model);
By giving DbContext to UI Layer you are creating dependancy between UI and database. Try to seperate it and use repository pattern and dependency injection.
Reference:
http://blogs.msdn.com/b/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx
http://prodinner.codeplex.com/releases/view/66899
You aren't using your MvcWebSamp at all - as you can see, the controller takes the EFModel
public ActionResult Create(EFModels.User user)
It works because the properties are the same. You just need to modify the controller method signatures to take the MvcWebSamp objects instead, and then transform those objects to the EFModel objects.
Automapper should work. We use it all the time even with different property names. Please post usage of automapper that does not work for you. Otherwise see following post to make it work with different property names.
Usage of Automapper when property names are different
In order to use the Entity Framework, you need to create an Entity Data Model. For adding Entity Model:
1) Right click on Model Folder in the solution explorer.
2) Select Add a New Item.
for more details please check out the following link....
http://www.mindstick.com/Articles/6f3bb3c6-d195-487b-8b82-244bb417b249/?Model%20classes%20with%20Entity%20Framework%20in%20MVC
Thanks !!!
At the end of my view, I call this:
<%= Html.Action("ProductLayoutAndLimits", this.Model) /* Render product-specific options*/ %>
That action is virtual in my controller:
[ChildActionOnly]
public virtual ActionResult ProductLayoutAndLimits(DeliveryOptionsViewModel optionsViewModel)
{
return new EmptyResult();
}
The intent was that I would override this method in a product specific controller. So naturally, I did this:
public override System.Web.Mvc.ActionResult ProductLayoutAndLimits(DeliveryOptionsViewModel optionsViewModel)
{
But the breakpoint isn't hitting, so my override is not getting picked up. Is there a different overload I should be using? Or do I need to pass in a different object? Or is there an annotation that I need on my product specific action in order for it to be detected?
Any help is appreciated. Thanks!
Edit
While all suggestions are appreciated, I am most interested in solutions that actually answer my question, rather than suggesting a different technique.
Templates have been suggested, but please note that I need controller code to be executed before any new additional view code is rendered. The base controller is in a solution that serves as a platform to other products. They cannot do anything product specific. After they render their view, the intent is that my override of the child action will be called. My controller code will check a number of things in order to determine how to set properties on my model before it renders the view.
Edit
I found the problem. I feel silly, as usual. Html.Action was being called from Platform's view code. It turned out that we have been using a product specific view for this since July. I didn't notice because we don't typically use product specific views. Whoops!
Why are you using child actions and overriding controllers to handle this? Why not display templates:
<%= Html.DisplayForModel("ProductLayoutAndLimits") %>
where ProductLayoutAndLimits would be the name of the corresponding display template.
Example:
Model:
public class BaseViewModel
{
public string BaseProp { get; set; }
}
public class DerviedViewModel : BaseViewModel
{
public string DerivedProp { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new DerviedViewModel
{
BaseProp = "base prop",
DerivedProp = "derived prop"
});
}
}
View (~/Views/Home/Index.cshtml):
#model AppName.Models.BaseViewModel
#Html.DisplayForModel()
Display Template (~/Views/Home/DisplayTemplates/DerviedViewModel.cshtml):
#model AppName.Models.DerviedViewModel
#Html.DisplayFor(x => x.BaseProp)
#Html.DisplayFor(x => x.DerivedProp)
Of course you could also have a display template for the base class (~/Views/Home/DisplayTemplates/BaseViewModel.cshtml):
#model AppName.Models.BaseViewModel
#Html.DisplayFor(x => x.BaseProp)
and this template would have been rendered if your controller action had returned the base class as model.
Maybe this question is quite simple because I'm new to MVC2. I have a simple demo MVC project.
(1) A weak-typed view: Index.aspx
<% Html.RenderPartial("ArticalList", ViewData["AllArticals"] as List<Artical>); %>
(2) A strong-typed partial view: ArticalList.ascx
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<Artical>>" %>
<% foreach (Artical a in Model) { %>
<%= Html.ActionLink(a.Title, "About", new { id = a.ID })%><br />
<%} %>
(3) Here is the HomeController.cs
public ActionResult Index()
{
ViewData["AllArticals"] = Artical.GetArticals();
return View();
}
public ActionResult ArticalList()
{
return PartialView(Artical.GetArticals());
}
Sorry I'm using a Web-Form "angle", because if I'm using a Web-Form, when I visit Index.aspx, rendering ArticalList.ascx will call public ActionResult ArticalList(). But here I need to write Artical.GetArticals() twice in two actions. How can I put them in one?
From what I understand, as a recent newbie in MVC too, is that the partial view does not use a action method in a controller. The "ArticalList" is a reference to the partial view file only and does not make another request for an action method. The partial view gets all of it's data from the view it is called from.
Html.RenderAction might be the behavior you're getting confused with.
i have build everything in order to create a model from 2 models so that i can create a view for the single model. one table holds info for 1 quote and the second table holds a list of items within that quote.
the error i get is:
The model item passed into the dictionary is of type 'graniteConcepts.Controllers.QuoteViewModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[graniteConcepts.Controllers.QuoteViewModel]'.
here is my calling actionresult
public ActionResult finaliseQuote(string quoteid)
{
ViewData["quoteid"] = quoteid;
var model = new QuoteViewModel
{
quoteInfo = new_online_quote.SingleOrDefault(x => x.quoteid == Convert.ToInt32(quoteid)),
quoteItems = new_quote_item.Find(x => x.quote_id == Convert.ToInt32(quoteid))
};
return View(model);
}
and here is my custom model to hold the partnership:
public class QuoteViewModel
{
public new_online_quote quoteInfo { get; set; }
public IEnumerable<new_quote_item> quoteItems { get; set; }
}
i have tried IList, List and also tried having nothing
if i have nothing, ie it just new_quote_item quoteItems then i get an error:
on
quoteItems = new_quote_item.Find(x => x.quote_id == Convert.ToInt32(quoteid))
saying that IList cannot be converted to new_online_quote
this is why i first tried it as an IList thinking one list would just drop into another.
im sure it is something small that i am missing but i just dont know what it is.
thanks
What you need to do is remove System.Collections.Generic.IEnumerable'.
as a Model for the viewpage...
You can do this at the top directive, instead you should type something like this:
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<graniteConcepts.Controllers.QuoteViewModel>" %>
EDIT: To clarify, this is in the View .ASPX file that you wish to present the Model in.