I'm start learning ASP.NET MVC 4 and stuck displaying Data From 2 model in 1 File
Here's my Model
public class mst_item
{
[Key]
[DisplayName("Item Code")]
[Required]
public string item_code{get;set;}
[DisplayName("Item Name")]
[Required]
public string item_name{get;set;}
[DisplayName("Unit")]
[Required]
public mst_item_unit unit_id{ get; set; }
}
public class mst_item_unit
{
[Key]
public int unit_id { get; set; }
[DisplayName("Unit")]
public string unit_name { get; set; }
}
Then My Controller :
public ActionResult Item()
{
var list_item = db.mst_item.Include("mst_item_unit").ToList();
return View(list_item);
}
Then How to display the unit_name based on the mst_item.unit_id in View using INNER JOIN or Include? something like :
#foreach (var item in Model)
{
#Html.DisplayFor(modelItem => item.item_name)
#Html.DisplayFor(modelItem => item.unit_name)
}
I get stuck here, But I success while displaying the mst_item data without joining mst_item_unit (just display the ID based on mst_item.unit_id) before.
You just have a couple of issues:
The parameter to Include should be a navigation property on your entity, not the table name. In other words, change it to:
var list_item = db.mst_item.Include("unit_id").ToList();
You have to access the properties of this second entity through the navigation property. In other words, this is the view code you would need:
#foreach (var item in Model)
{
#Html.DisplayFor(modelItem => item.item_name)
#Html.DisplayFor(modelItem => item.unit_id.unit_name)
}
That said, you've also got some stylistic problems here, which since you're new, I'll point out.
Class and property names should be camel-cased, i.e. MstItem, rather than mst_item.
Navigation properties should be named after the objects they connect to, i.e. unit_id should be something like MstItemUnit, or just Unit, if you prefer. That removes the need to specify a display name for it, as well.
The use of the _id suffix on this navigation property is particularly troubling, because it implies that this property is an int or Guid - you know, something that could be used an id - whereas actually you're referencing a full-fledged object.
While not as important, it makes little sense to repeat the class name or a portion thereof in the property names of that class. For example, unit_name should just be Name. Obviously, it's the name of the unit, because that's the class.
With those in place, your code becomes much more readable and "human". For example:
#Html.DisplayFor(m => item.Name)
#Html.DisplayFor(m => item.Unit.Name)
You should be creating a ViewModel that will relate both your models, populate that ViewModel in your controller and use that in your View. The below link is very much your case.
Multiple Models in a Single View (C# MVC3)
Related
I'm building a Roles/Permissions component for my application. My Role view model looks like this:
public class RoleView
{
public int Id { get; set; }
public ApplicationWithPermissionsView Application { get; set; }
public List<RolePermissionView> Permissions { get; set; }
public IEnumerable<KeyValuePair<string, int>> PermissionValues
{
get
{
return EnumHelper.ToList<PermissionValue>();
}
}
[Required]
public string Name { get; set; }
}
The RolePermission view model looks like this:
public class RolePermissionView
{
public int PermissionId { get; set; }
public int RoleId { get; set; }
public PermissionValue Value { get; set; }
}
PermissionValue is an enum that has members NotSet, Allow, Deny, and stores an int value in the database.
If I go to a role edit page, I display a list of permission categories, which each have a list of permissions, with the respective values (RolePermission) for each permission for that role. As you can see, RolePermissionView has composite keys (PermissionId, RoleId).
How can I build a drop down list, that shows a list of possible permission values and selects the correct permission value for that particular permission?
The following generates the drop down lists, but doesn't select any values. Also, doesn't give each dropdown list a unique name or ID, so binding definitely won't work. I assume if I later move this into an Editor Template they will have uniques.
#foreach (var permission in category.Permissions)
{
<tr>
<td>#permission.Description</td>
<td>#Html.DropDownListFor(m => m.Permissions.Find(p => p.PermissionId == permission.Id).PermissionValue, new SelectList(Model.PermissionValues, "Value", "Key"))</td>
</tr>
}
The trouble here is mostly syntactic. Bear in mind that for things like DropDownListFor and the other *For-style helpers, you're passing an expression, not a literal instance of something. The helper then attempts to parse that expression to create a name attribute value on the input that will (hopefully) bind when the data is posted back. Obviously, then, doing something like Find is not going to work, because there's no way to translate that so that the same thing happens on the other end after posting. Instead, you should do something like:
#for (var i = 0; i < category.Permissions.Count(); i++)
{
<tr>
<td>#category.Permissions[i].Description</td>
<td>#Html.DropDownListFor(m => category.Permissions[i].PermissionValue, Model.PermissionValues)</td>
</tr>
}
Now, the expression is concrete enough to bind to.
Also, Change your PermissionValues property to be IEnumerable<SelectListItem> instead of using KeyValuePair. Then, there's no need to manually create a SelectList, which will save you a lot of potential headaches. To construct the Value of the SelectListItem, I would simply append the two composite ids as strings (since the value must be a string anyways). Join them by some delimiter like a comma. Then, on post, you can get the posted value and split it by the same delimiter to get your two ids back. Obviously, you'll also then need to convert them both back to ints. You can only post one value back from a select, so this is really your only option when it comes to composite keys.
I want to display items in view in mvc dynamically.
following is the code for displaying records in view
#using MvcWcf.ServiceReference1
#model IEnumerable<WcfService.MyAddress>
<table>
<tr>
<td>#Html.DisplayNameFor(model => model.Address1)</td>
<td>#Html.DisplayNameFor(model => model.City)</td>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.Address1)</td>
<td>#Html.DisplayFor(modelItem => item.City)</td>
</tr>
}
</table>
my requirement how to display items in th and records dynamically
for example <td>#Html.DisplayNameFor(model => model.Address1)</td><td>#Html.DisplayNameFor(model => model.City)</td>
here i wrote address1 and City because i know the fields.
if i don't know the fields how to display that.
also in displaying records
You can create a generic model with this property
public List<GenericItem> items { get; set; }
than you create your generic item with the property you need, like type (textbox, button, ...), text inside and anything you need
public class GenericItem
{
public enum Type { get; set; }
public string Text { get; set; }
[...]
}
than you must create a custom helper (like a custom editorfor) that accepts a generic model and with that model show on screen the appropriate control with what you have inside your model.
https://www.asp.net/mvc/overview/older-versions-1/views/creating-custom-html-helpers-cs
I have view model which has another child model to render the partial view (below).
public class ExamResultsFormViewModel
{
public PreliminaryInformationViewModel PreliminaryInformation { get; set; }
public string MemberID { get; set; }
public string MemberName { get; set; }
public int PatientID { get; set; }
public string ConfirmationID { get; set; }
public bool IsEditable { get; set; }
#region Select Lists
public SelectList ProviderOptions { get; set; }
#endregion
}
public class PreliminaryInformationViewModel
{
public string ProviderName { get; set; }
public string ProviderID { get; set; }
public string ServiceLocation { get; set; }
}
This PreliminaryInformationViewModel view model also used as a child models in another view model since this preliminary information can be updated at different pages.
So I created this preliminary information as a separate partial and to include in other pages.
#{Html.RenderPartial("_PreliminaryInformation", Model.PreliminaryInformation);}
Inside the partial
#model Web.Models.Preliminary.PreliminaryInformationViewModel
<div>
#Html.TextBoxFor(x => x.DateOfService })
</div>
But the problem is during submit this preliminary model is always null due to the reason HTML name attribute is always is rendered as
but when I pass the parent model to the partial as below.
#model Web.Models.Exam.ExamResultsFormViewModel
<div>
#Html.TextBoxFor(x => x.PreliminaryInformation.DateOfService })
</div>
Now the HTML element is generated as
<input type = 'text' name='PreliminaryInformation.DateOfService.DateOfService' id='PreliminaryInformation.DateOfService'>
and it binds properly during the submit.
I understand MVC bind the element value based on the name attribute value, but the second implementation would need me to create a multiple partial for each page, which I don't like.
So far I couldn't find a solution to work with the first implementation, is there way I can make preliminary information model value bind during submit with the first implementation.
I know its a bit late but it might help to someone
If you have complex model, you can still pass it into partial using:
#Html.Partial("_YourPartialName", Model.Contact, new ViewDataDictionary()
{
TemplateInfo = new TemplateInfo()
{
HtmlFieldPrefix = "Contact"
}
})
where I have defined model with property "Contact". Now what HtmlFieldPrefix do is add the property binding for each model "so the model binder can find the parent model"
There is a blog post about it: http://www.cpodesign.com/blog/bind-partial-view-model-binding-during-submit/
.NET Core 2 binding
In .NET Core 2 and MVC the answer above will not work, the property is no longer settable.
How ever the solution is very similar.
#{ Html.ViewData.TemplateInfo.HtmlFieldPrefix = "Contact"; }
#await Html.PartialAsync("_YourPartialName", Model.Contact)
after you can submit your model, it will bind again.
Hope that helps
You can add the HtmlFieldPrefix to the top of your partial view:
#{
ViewData.TemplateInfo.HtmlFieldPrefix = "Contact";
}
This is the same approach as that described by #cpoDesign but it means you can keep the prefix in your partial view if you need to do that.
You need to create an editor template for PreliminaryInformationViewModel to replace the partial view, then call with Html.EditorFor( m => m.PreliminaryInformation ). Reference this solution. Creating the template should be as simple as moving your partial view to the Views/Shared/EditorTemplates directory. Html.EditorFor(...) will automatically use this template based on the type you're passing in as the model (in this case, PreliminaryInformationViewModel)
I also ran into this problem. I will explain my solution using your code. I started with this:
#{
Html.RenderPartial("_PreliminaryInformation", Model.PreliminaryInformation);
}
The action corresponding to the http post was looking for the parent model. The http post was submitting the form correctly but there was no reference in the child partial to the parent partial. The submitted values from the child partial were ignored and the corresponding child property remained null.
I created an interface, IPreliminaryInfoCapable, which contained a definition for the child type, like so:
public interface IPreliminaryInfoCapable
{
PreliminaryInformationViewModel PreliminaryInformation { get; set; }
}
I made my parent model implement this interface. My partial view then uses the interface at the top as the model:
#model IPreliminaryInfoCapable
Finally, my parent view can use the following code to pass itself to the child partial:
#{
Html.RenderPartial("ChildPartial", Model);
}
Then the child partial can use the child object, like so:
#model IPreliminaryInfoCapable
...
#Html.LabelFor(m => m.PreliminaryInformation.ProviderName)
etc.
All of this properly fills the parent model upon http post to the corresponding action.
Quick Tip: when calling your EditorFor method, you can set the name of the template as a parameter of the Html.EditorFor method. Alternatively, naming conventions can be your friend; just make sure your editor template filename is exactly the same name as the model property type
i.e. model property type 'CustomerViewModel' => 'CustomerViewModel.cshtml' editor template.
Please make the below changes to your partial page. so it will come with your Parent model
//Parent Page
#{Html.RenderPartial("_PreliminaryInformation", Model.PreliminaryInformation);}
//Partial Page
#model Web.Models.Preliminary.PreliminaryInformationViewModel
#using (Html.BeginCollectionItem("PreliminaryInformation", item.RowId, true))
{
<div>
#Html.TextBoxFor(x => x.DateOfService })
</div>
}
For .net core 2 and mvc, use do like below:
#{
Html.ViewData.TemplateInfo.HtmlFieldPrefix = "Contact";
}
#await Html.PartialAsync("_YourPartialViewName", Model.Contact)
I'm developing a project using Silverlight 4 and Entity Framework 4 and I'm trying to auto-load the details (with conditions) associated with an entity when the client loads the EntityQuery.
So far, I've been able to put in place a solution, using the Include attribute, that returns all the details associated with the master entity. What I'm missing here is to be able to filter out the details based on some criteria.
As an example, here's what my entities look like:
Entity Movie
Id (int)
[Include]
MovieLocalizedInformations (EntityCollection<MovieLocalizedInformation>)
Entity MovieLocalizedInformation
Id (int)
Movie_Id (int)
LanguageCode (eg.: en)
Title
On my DomainService object, I expose the following method:
public IQueryable<Movie> GetMovies( string languageCode )
{
return this.ObjectContext.Movies.Include( "MovieLocalizedInformations" );
}
This works fine. But when I try to add where clause to filter out the localized information based on the language code, only the movies get loaded on the client.
Is there a way to achieve the filtering in one query?
Note: I'm also using the DomainDataSource with paging on the client so the solution needs to work with that.
Any help would be greatly appreciated!
Thanks,
Jacques.
Not sure about Enitity Framework but with a LinqToSqlDomainService you use the LoadWith loadOption
to include the details entities and then use the AssociateWith LoadOption to filter the detail e.g
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Movies>(i => i.MovieLocalizedInformations);
options.AssociateWith<Movies>(i => i.MovieLocalizedInformations.Where(d=> myListOfIds.Contains(d.LocationId)));
Ok,
For efficiency reason, I decided to go with custom DTO object that fetches the localized information and flatten the result.
But, the same problem occurred when my custom DTO needed to reference another custom localized DTO.
Here is how I came to do the same as the .Include( "PropertyName" ) that the ObjectSet offers:
Entity LocalizedMovieCollection
public class LocalizedMovieCollection
{
[Key]
public int Id { get; set; }
public string Name { get; set; } (the result of a sub query based on the language)
[Include]
[Association( "LocalizedMovieCollection_LocalizedMovies", "Id", "MovieCollection_Id" )]
public IEnumerable<LocalizedMovie> Movies { get; set; }
}
Entity LocalizedMovie
public class LocalizedMovie
{
[Key]
public int Id { get; set; }
public string Name { get; set; } (the result of a sub query based on the language)
public int MovieCollection_Id { get; set; }
[Include]
[Association( "LocalizedMovie_LocalizedMovieCollection", "MovieCollection_Id", "Id", IsForeignKey = true]
public LocalizedMovieCollection MovieCollection { get; set; }
}
Then, I've declared two methods: One that returns an IQueryable of LocalizedMovieCollection and the other, an IQueryable of LocalizedMovie. (Note: There must be at least one method that returns each type of entity for the entity to get auto-generated on the Silverlight client)
My goal is to automatically load the MovieCollection associated with a Movie so the method definition to get the movies is as follow:
public IQueryable<LocalizedMovie> GetMovies( string languageCode )
{
return from movie in this.ObjectContext.Movies
join movieLocalizedInfo in this.ObjectContext.MovieLocalizedInformations
on movie equals movieLocalizedInfo.Movie
join movieCollection in this.ObjectContext.MovieCollections
on movie.MovieCollection equals movieCollection
join movieCollectionLocalizedInfo in this.ObjectContext.MovieCollectionLocalizedInformations
on movieCollection equals movieCollectionLocalizedInfo.MovieCollection
where movieLocalizedInfo.LanguageCode == languageCode && movieCollectionLocalizedInfo.LanguageCode == languageCode
select new LocalizedMovie()
{
Id = movie.Id,
Name = movieLocalizedInfo.Name
MovieCollection_Id = movieCollection.Id,
MovieCollection = new LocalizedMovieCollection(){ Id = movieCollection.Id, Name = movieCollectionLocalizedInfo.Name }
}
}
When the Silverlight client loads the query, all the LocalizedMovies and their associated LocalizedMovieCollections will be loaded into the context.
I'm experiencing an odd problem with FluentNHibernate: when I save my entity, one of the (reference) properties is not updated. Other properties, both fields and references, are updated, and the failing property is correctly mapped (retrieving entities works like a charm).
A (slightly simplified) description of what I'm doing:
Into my MVC action method, an InputModel is bound and set. It has a property for the TypeID, where I wish to set the Type of my entity (let's call the entity type Thing).
A new Thing object is created, and the simple properties of the InputModel is copied over. For a couple of complex properties, among them the Type property which isn't working and another property which is, the following is done:
2.1. The correct ThingType is fetched from the repository, based on the provided type id.
2.2. The type is set (using thing.Type = theType) on the new Thing object.
The Thing that I want to update is fetched from the repository, based on the id on the input model (not the same id as the TypeID).
All properties, complex and other, are copied over from the new thing (created by me) to the original one (fetched from db).
The original Thing is saved, using session.Save();.
As stated above, it's only one property that isn't working - other properties, following (as far as I can tell) the exact same pattern, work. I've also debugged and verified that the original Thing has the correct, updated Type when it is passed to session.Save().
I have no idea where to start troubleshooting this...
Update: The classes are plain POCOs:
public class Thing
{
public int ID { get; set; }
public string SomeSimpleProp { get; set; }
public ThingType Type { get; set; }
public OtherEntity OtherReference { get; set; }
}
public class ThingType
{
public int ID { get; set; }
public string Name { get; set; }
}
My exact mappings (except for the names of types and properties) are these:
// In ThingMap : ClassMap<Thing> constructor:
Id(t => t.ID).Column("ThingID");
Map(t => t.SomeSimpleProp);
References(t => t.Type).Column("ThingTypeID");
References(t => t.OtherReference).Column("OtherReferenceID");
// In ThingTypeMap : ClassMap<ThingType> constructor:
Id(t => t.ID).Column("ThingTypeID");
Map(t => t.Name);
As I said, OtherReference is updated correctly while Type is not. They are mapped identically, so I don't see how this could be a mapping error.
You should specify <many-to-one .... cascade="save-update"/> in order to update references.