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) %>
<%:
}
%>
Related
I'm having an issue with the bindings of my model to a partial view and feel that there must be a way to do what I want. I wonder if my design is flawed and a small refactoring might be necessary.
A very simplified (and abstract) version of my models would be like so:
Public Class BaseClass
{
Public string Name { get; set; }
Public List<SomeClass> Things { get; set; }
}
Public Class DerivedClass : BaseClass
{
Public List<LineItem> Items { get; set; }
}
Public Class Library
{
Public List<LineItem> Items { get; set; }
}
Public Class LineItem
{
Public string Name { get; set; }
Public string Value { get; set; }
}
I have Editor Templates for the BaseClass, SomeClass, and LineItem. These are shown in the view for DerivedClass and work as intended by submitting changes to the controller. The LineItem template is wrapped in a LineItemList partial view because I intend to use it for a view for Library and don't want to repeat all of that layout and javascript. The LineItemList partial view is included on the DerivedClass view by Html.PartialView since there doesn't seem to be a way to create an Editor Template for the List type. So my views look like this:
DerivedClassView
BaseClassPartialView
SomeClassPartialView
LineItemListPartialView
LineItemParialView
When I submit my form, the controller gets all of the data for the BaseClass and SomeClass list but none for the LineItem list. The difference of course being that one is rendered using Html.EditorFor and the other Html.PartialView.
Refactoring the classes will be tough as they have to be backwards compatible with an old XML format for serialization, but I'm sure I can work some magic if necessary.
As Chris Pratt mentioned, I forgot to include my controller methods:
Public ActionResult DerivedClassEditor()
{
Return View(New DerivedClass());
}
[HttpPost]
Public ActionResult DerivedClassEditor(DerivedClass dc)
{
// Do Stuff
}
I just noticed in the rendered Html, the SomeClass controls are named SomeClass.[0].Name while those of the LineItem are [0].Name. I have a feeling that might be a symptom of the issue.
And my views look similar to this:
DerivedClassEditor
#model DerivedClass
#using (Html.BeginForm())
{
#Html.EditorFor(model => model)
#Html.Partial("LineItemListPartialView")
<input type="submit" />
}
LineItemListPartialView
#model List<LineItem>
<div name="Items">
#Html.EditorFor(model => model)
</div>
LineItemPartialView
#model LineItem
<div name="LineItem">
#Html.LabelFor(model => model.Name)
#Html.TextEditorFor(model => model.Name)
</div>
Edit:
A link to my view: https://github.com/melance/TheRandomizer/blob/Initial/TheRandomizer.WebApp/Views/UserContent/AssignmentEditor.cshtml
I've narrowed down the issue to the fact that when I load one of the lists using #Html.EditorFor it names the inputs Collection[index].Property yet when I add one dynamically using the same call it simply names the input Property. Is there an easy and reusable way to have the addition of new items have the same naming structure?
Crucially, you failed to post your controller code, so I'm stabbing in the dark, but I think I can guess pretty well what's happening. You most likely have something like:
[HttpPost]
public ActionResult MyAwesomeAction(BaseClass model)
{
...
}
And you're assuming that since your view is working with DerivedClass and posting DerivedClass that you should end up with an instance of DerivedClass rather than BaseClass in your action. That assumption is incorrect.
All that exists on post is a set of string key-value pairs. The modelbinder looks at the action signature and attempts to bind the posted data to an instance of the parameter(s), newing up classes as necessary. Given this, the only information the modelbinder has in this scenario is that it's expected to bind values to an instance of BaseClass and a set of data to attempt to do that with. As a result, it will create an instance of BaseClass and bind what data it can, dropping anything it can't. Importantly, since BaseClass doesn't include stuff like your Items property, all of that data will be discarded.
Long and short, polymorphism isn't supported with action parameters. The type of your parameter must be the type you want. Period.
For what it's worth, you can use editor templates for list properties. EditorFor will simply render the editor template for the contained type for each item in the list. For example, calling:
#Html.EditorFor(m => m.Items)
Is essentially the same as:
#for (var i = 0; i < Model.Items.Count(); i++)
{
#Html.EditorFor(m => m.Items[i])
}
So after much research, trial and error and help from Erik, I was able to solve my problem. The issue turned out to be the naming of the form elements in my partial view. When added by the model they are indexed as such: name = "ListProperty[Index].PropertyName". When I was adding my partial views using ajax, the were named for just the Property Name. In order to fix this, I had to handle my ajax calls for the partial view like this:
public ActionResult CreateParameter(Int32 index)
{
ViewData.TemplateInfo.HtmlFieldPrefix = $"Parameters[{index}]";
return PartialView("~/Views/Shared/EditorTemplates/Configuration.cshtml");
}
I have a layout page which has a partial view. The partial view needs to loop through a property on the view model to show a list of categories. When a category is displayed I need to show a list of documents in that category. /Home/Index works, but when I try to view /Documents/Category/{id}, I get an error:
Additional information: The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[ViewModels.DocumentViewModel]', but this dictionary requires a model item of type 'ViewModels.HomeViewModel'.
_Layout.cshtml
...
<body>
#Html.Partial("_CategoryViewModel")
<div class="content">
#RenderBody()
</div>
HomeViewModel.cs
public class HomeViewModel {
...
public ICollection<DocumentCategory> Categories { get; set; }
public ICollection<Documents> Documents { get; set; }
...
}
_CategoryViewModel.cshtml (this should show a list of all categories)
#model ViewModels.HomeViewModel
...
#foreach (DocumentCategory item in Model.Categories)
{
<li>
<a href="#Url.Action("Category", "Documents", new { #id = #item.CategoryId })" title="View documents in the #item.Name category">
<span class="fa fa-files-o"></span> #item.Name
</a>
</li>
}
DocumentsController.cs
public ActionResult Category(int id)
{
var thisCategory = _ctx.Categories.Get(c => c.CategoryId == id).FirstOrDefault();
IEnumerable<DocumentViewModel> docs = null;
if(thisCategory == null)
{
TempData.Add("errorMessage", "Invalid category");
} else {
docs = thisCategory.Documents.ToList();
}
return View("Category", docs);
}
What's happening kind of makes sense - the PartialView on the Layout page needs to enumerate over a collection which isn't present in the ViewModel I'm using. I have no idea how to achieve this - the only way would seem to be to add a Categories property to every ViewModel in my site.
By default, using #Html.Partial() will pass the current model to the partial view, and because your Category.cshtml view uses #model List<DocumentViewModel>, then List<DocumentViewModel> is passed to a partial that expects HomeViewModel.
If you want to render a partial view for HomeViewModel on every page, then use #Html.Action() to call a ChildActionOnly method that returns the partial
[ChildActionOnly]
public ActionResult Categories
{
var model = new HomeViewModel()
{
.... // initialize properties
}
return PartialView("_CategoryViewModel", model)
}
and in the layout
#Html.Action("Categories", yourControllerName)
// or
#{ Html.RenderAction("Categories", yourControllerName); }
As I see it you have a few different alternatives.
1. Use Html.Action and create an Action that returns your view.
#Html.Action("Index", "Category") // Or your controller name.
I believe that there are some performance draw-backs with this approach because the whole MVC lifecycle will run again in order to render the result of the action. But then you can render the result of an action without having the correct model in the view that called it.
One may also argue that this breaks the MVC pattern, but it might be worth it.
2. Use a generic model (or an interface) in your _Layout.cshtml, and let your viewmodels inherit from that model.
In your _Layout.cshtml:
#model IBaseViewModel
And let all your viewmodels implement this interface.
public interface IBaseViewModel
{
ICollection<DocumentCategory> Categories { get; set; }
}
public interface IBaseViewModel<T> : IBaseViewModel
{
T ViewModel {get; set;}
}
Since you're placing #Html.Partial("_CategoryViewModel") in _Layout.cshtml I assume that it should be visible in all pages, so I think it's logical that all the controllers that are using _Layout.cshtml make sure that it gets the information it needs, and thus adding Categories to the model.
I use this approach all the time for stuff like breadcrumbs and menu-information (stuff that is used in all pages). Then I have a basecontroller that makes sure that Categories is populated with the correct info.
I am making WCF service call using MyViewRequest view fields inside HttpPost ActionHandler. The goal is to show response using partial view, MyViewResponse
In brief I need to achieve these two items-
Disable load of partial view on first load.
Display Response (along with Request) after service call.
MyViewRequest.cshtml
#using (Html.BeginForm())
{
#Html.ValidationSummary(false)
//html code
}
</div>
<div id="dvResponse">
#Html.Partial("MyViewResponse");
</div>
Partial view: MyViewResponse.cshtml
#model MvcApplication3.Models.MyModel
#{
ViewBag.Title = "MyViewResponse";
}
<h2>MyView</h2>
#Html.Label(Model.MyName, "My Name")
This was pretty straight forward in Asp.Net using userControl, But stuck up here, How can we achieve this in MVC3.
I think the best way is to transfer your data using ViewModels. Let's assume you want to have an app something like stackoverflow where you have a question and user can post an answer and it will be shown after the post along with the question.
public class PostViewModel
{
public int ID { set;get;}
public string Text { set;get;}
public List<PostViewModel> Answers { set;get;}
public string NewAnswer { set;get;}
}
in your GET action, you show the question. Get the id from the url and get the Question details from your service/repositary.
public ActionResult Show(int id)
{
var post=new PostViewModel();
post=yourService.GetQuestionFromID(id);
post.Answers=yourService.GetAnswersFromQuestionID(id);
return View(post);
}
Assuming yourService.GetQuestionFromID method returns an object of PostViewModel with the propety values filled. The data can be fetched from your database or via a WCF service call. It is up to you. Also yourService.GetAnswersFromQuestionID method returns a list of PostViewModel to represent the Answers for that question. You may put both these into a single method called GetQuestionWithAnswers. I wrote 2 methods to make it more clear.
Now in your Show view
#model PostViewModel
#Html.LabelFor(x=>x.Text);
#using(Html.Beginform())
{
#Html.HiddenFor(x=>x.ID);
#Html.TextBoxFor(x=>x.NewAnswer)
<input type="submit" />
}
<h3>Answers</h3>
#if(Model.Answers!=null)
{
#Html.Partial("Responses",Model.Answers)
}
And your Partial view will be strongly typed to a collection of PostViewModel
#model List<PostViewModel>
#foreach(var item in Model)
{
<div> #item.Text </div>
}
Handling the postback is simple (HttpPost)
[HttpPost]
public ActionResult Show(PostViewModel model)
{
if(ModelState.IsValid)
{
//get your data from model.NewAnswer property and save to your data base
//or call WCF method to save it.
//After saving, Let's redirect to the GET action (PRG pattern)
return RedirectToAction("Show",new { #id=model.ID});
}
}
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.