Using HTML.Grid to display child object - asp.net-mvc

I have an ASP.NET MVC application that is using a single view to display the properties and children (with their properties) of a model entity.
My model looks something like this:
public class Market
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public IList<EmailAddress> EmailAddresses { get; set; }
}
public class EmailAddress
{
public virtual int ID { get; set; }
public virtual int MarketID { get; set; }
public virtual string Email { get; set; }
}
On the view, I want to use a table to display the list of related email addresses. To do this, I am using Html.Grid.
<%= Html.Grid(Model.EmailAddresses).Columns( column =>
{
column.For(x => x.Email + Html.Hidden("ID", x.ID)).Encode(false).Sortable(false);
})
.Attributes(style => "width:100%")
.Attributes(id => "emailGrid")
.Empty("There are no Email Addresses set up") %>
However, when I do this, the hidden ID is that of the parent entity Market, not that of the EmailAddress.
How do I remedy this?

It seems it could be a bug in the WebGrid. Have you tried renaming your ID field in the EmailAddress class, e.g. EmailID and pass that to the WebGrid and see if it displays correctly?

This works for me, could it be that you have something wrong in the filling of your model?

Since you're using the lambda expression for the Column.For() method, the x parameter is re referring to a single email. I think you mean to refer to the Model, not a single email ID
Instead of doing x.ID, I think you just want Model.ID

Related

Entity framework Navigation Properties

I am currently trying to develop my first .NET MVC application and so learning the main concepts.
In my application I have a table that displays a list of animals from an animals table. In the table I am also trying to display the animal breed, but I am pulling the breed from the Breed table on the foreign key stored in the Animal table
I am currently trying to use a Navigation Property to display the Breed text and not the ID so I
altered my Animal model to look like this
public partial class Animal
{
public int AnimalId { get; set; }
public Nullable<int> UserId { get; set; }
public string TagNo { get; set; }
public Nullable<int> Species { get; set; }
public Nullable<bool> Sex { get; set; }
public Nullable<int> AnimalBreed { get; set; }
public Nullable<System.DateTime> DOB { get; set; }
public Nullable<int> OwnershipStatus { get; set; }
public Nullable<System.DateTime> DateAdded { get; set; }
public Nullable<bool> BornOnFarm { get; set; }
public virtual Breed Breed { get; set; }
}
And my breed model looks like
public partial class Breed
{
public int id { get; set; }
public Nullable<int> SpeciesID { get; set; }
public string Breed1 { get; set; }
}
In my view I am trying to display the Breeds field from my animal model as shown below, but the breed column is just empty
<td>
#Html.DisplayFor(modelItem => item.Breed.Breed1)
</td>
Also finally, here is the code that i am using to send the model to the view
List<Animal> Animal1 = (from animals in db.Animals
where animals.Species == 2 && animals.OwnershipStatus == 1
&& animals.UserId == WebSecurity.CurrentUserId
select animals).ToList();
return View(Animal1);
First, don't pluralize single items. It creates confusion in your code:
public virtual Breed Breed { get; set; }
-or-
public virtual ICollection<Breed> Breeds { get; set; }
The virtual attribute allows lazy-loading (a query to fetch the breed will be issued the first time you try to access it). You pretty much always want to include virtual with the property so Entity Framework does not unnecessarily issue joins if you don't actually end up using the property. However, in this case, you are, so you'll want to tell EF to eager-load it by including .Include("Breed") in your query. However, that's just for optimization; it's not your problem here.
Your problem here is that Razor doesn't know how to display Breed. It's not a normal type, obviously, because you created it. So, what you really need is to display the actual property on Breed that you want:
#Html.DisplayFor(m => m.Breed.Breed1)
There's an alternate method, but it's more complex and probably overkill for this scenario. If you really want to use Breed directly, then you need to define a display template for Breed. You do that by adding a new folder to Views\Shared named DisplayTemplates. Inside that folder, add a view named Breed.cshtml. The name of the view here corresponds to the class name, not the property name. Inside that view, you'd do something like:
#model Namespace.To.Breed
#Html.DisplayFor(m => m.Breed1)
Then, in your view you could just do:
#Html.DisplayFor(m => m.Breed)
And Razor will use the display template to render the appropriate thing. Like I said, it's overkill for this, but in more complex object rendering, it might come in handy.
If lazy loading is not enabled in your DbContext, then you have to explicitly load (or use eager loading) the navigation properties.
See http://msdn.microsoft.com/en-us/data/jj574232.aspx
You'll end-up with something like:
var res = (from animals in db.Animals.Include("Breeds")
where animals.Species == 2 & animals.OwnershipStatus == 1
& animals.UserId == WebSecurity.CurrentUserId
select animals).ToList();

MVC MultiSelect modelbinding

i want to be able to display and update my User's Organisations preferably using the htmlhelper Html.TextBoxFor(
I have an entityframework 5 database first database with relationships defined as expected on the 3 tables
User
Organisation
UserOrganisation
which yield the classes below
public partial class User
{
public System.Guid UserId { get; set; }
public string Fullname { get; set; }
...
}
public partial class Organisation
{
public int OrganisationID { get; set; }
public string Title { get; set; }
...
}
public partial class UserOrganisation
{
public System.Guid UserId { get; set; }
public int OrganisationID { get; set; }
}
I pass in the user as the model and also populate a list of potential organisations in the viewbag i.e.
ViewBag.PossibleOrganisations = OrganisationFactories.GetOrganisations()
and the razor markup is.
#Html.ListBoxFor(model => model.UserOrganisations,
new MultiSelectList(ViewBag.PossibleOrganisations,"OrganisationID","Title"))
Now this displays the list of Organisations correctly and i can multiselect them. But it doesn't show the selected Organisations, and it also wont write this back to the database when posting back (incidentally all other fields did write back prior to this change).
Does anyone have any suggestions or examples of a multiselect list working in this fashion?
Cheers
Tim

MVC 4, Upshot entities cyclic references

I have a DbDataController which delivers a List of Equipment.
public IQueryable<BettrFit.Models.Equipment> GetEquipment() {
var q= DbContext.EquipmentSet.OrderBy(e => e.Name);
return q;
}
In my scaffolded view everything looks ok.
But the Equipment contains a HashSet member of EquipmentType. I want to show this type in my view and also be able to add data to the EquipmentType collection of Equipment (via a multiselect list).
But if I try to include the "EquipmentType" in my linq query it fails during serialisation.
public IQueryable<BettrFit.Models.Equipment> GetEquipment() {
var q= DbContext.EquipmentSet.Include("EquipmentType").OrderBy(e => e.Name);
return q;
}
"Object Graph for Type EquipmentType Contains Cycles and Cannot be Serialized if Reference Tracking is Disabled"
How can I switch on the "backtracking of references"?
Maybe the problem is that the EquipmentType is back-linking through a HashSet? But I do not .include("EquipmentType.Equipment") in my query. So that should be ok.
How is Upshot generating the model? I only find the EquipmentViewModel.js file but this does not contain any model members.
Here are my model classes:
public class Equipment
{
public Equipment()
{
this.Exercise = new HashSet<Exercise>();
this.EquipmentType = new HashSet<EquipmentType>();
this.UserDetails = new HashSet<UserDetails>();
}
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Picture { get; set; }
public string Link { get; set; }
public string Producer { get; set; }
public string Video { get; set; }
public virtual ICollection<EquipmentType> EquipmentType { get; set; }
public virtual ICollection<UserDetails> UserDetails { get; set; }
}
public class EquipmentType
{
public EquipmentType()
{
this.Equipment = new HashSet<Equipment>();
this.UserDetails = new HashSet<UserDetails>();
}
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<Equipment> Equipment { get; set; }
public virtual ICollection<UserDetails> UserDetails { get; set; }
}
try decorating one of the navigation properties with [IgnoreDataMember]
[IgnoreDataMember]
public virtual ICollection<Equipment> Equipment { get; set; }
The model generated by upshot can be found on the page itself. In your Index view you will see the UpshotContext HTML helper being used (given that you are using the latest SPA version), in which the dataSource and model type are specified.
When the page is then rendered in the browser, this helper code is replaced with the actual model definition. To see that, view the source code of your page in the browser and search for a <script> tag that starts with upshot.dataSources = upshot.dataSources || {};
Check here for more info about how upshot generates the client side model.
As for the "backtracking of references", I don't know :)
I figured out - partially how to solve the circular reference problem.
I just iterated over my queried collection (with Include() ) and set the backreferences to the parent to NULL. That worked for the serialisation issue which otherwise already breaks on the server.
The only problem now is the update of a data entity - its failing because the arrays of the referenced entitycollection are static...
To solve the cyclic backreference, you can use the IgnoreDataMember attribute. Or you can set the back reference to NULL before returning the data from the DbDataController
I posted a working solution to your problem in a different question, but using Entity Framework Code First.
https://stackoverflow.com/a/10010695/1226140
Here I show how to generate your client-side model manually, allowing to you to map the data however you please

ASP MVC 3.0 Complex View

I am developing a application for Sales Order Management using ASP.NET MVC 3.0. I need to develop a page where Customer Details can be added.
Customer Details Include
public class Customer
{
public int ID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Alias { get; set; }
public int DefaultCreditPeriod { get; set; }
public Accounts Accounts { get; set; }
public IList<Address> Addresses { get; set; }
public IList<Contact> Contacts { get; set; }
}
public class Accounts
{
public int ID { get; set; }
public string VATNo { get; set; }
public string CSTNo { get; set; }
public string PANNo { get; set; }
public string TANNo { get; set; }
public string ECCNo { get; set; }
public string ExciseNo { get; set; }
public string ServiceTaxNo { get; set; }
public bool IsServiceTaxApplicable { get; set; }
public bool IsTDSDeductable { get; set; }
public bool IsTCSApplicable { get; set; }
}
public class Address
{
public int ID { get; set; }
public AddressType Type { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string Line4 { get; set; }
public string Country { get; set; }
public string PostCode { get; set; }
}
public class Contact
{
public int ID { get; set; }
public ContactType Type { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string PhoneNumber { get; set; }
public string Extension { get; set; }
public string MobileNumber { get; set; }
public string EmailId { get; set; }
public string FaxNumber { get; set; }
public string Website { get; set; }
}
Customer Requires a single page to fill all the customer details(General info, Account Info,Address Info and Contact Info). There will be multiple Addresses(Billing, Shipping, etc) and multiple Contacts (Sales, Purchase). I am new to MVC. How to Create the View for the above and Add multiple Address dynamically?
I often create wrapper models to handle this kind of situation e.g.
public class CustomerWrapperModel
{
public Customer Customer { get; set;}
public Accounts Accounts { get; set;}
public List<Address> AddressList { get; set}
//Add
public CustomerWrapperModel()
{
}
//Add/Edit
public CustomerWrapperModel(Customer customer, Accounts accounts, List<Address> addressList)
{
this.Customer = customer;
this.Accounts = accounts;
this.AddressList = addressList;
}
}
then declare the View to be of type CustomerWrapperModel and use editors like so:
#model MyNamespace.CustomerWrapperModel
#Html.EditorFor(model => model.Customer)
#Html.EditorFor(model => model.Accounts)
#Html.EditorFor(model => model.AddressList)
and have a controller to receive the post that looks like this:
[HttpPost]
public ActionResult(Customer customer, Accounts accounts, List<Address> addressList)
{
//Handle db stuff here
}
As far as adding addresses dynamically I found the best way to do this if you're using MVC validation and want to keep the list structured correctly with the right list indexes so that you can have the List parameter in your controller is to post the current Addresses to a helper controller like this:
[HttpPost]
public PartialResult AddAddress(List<Address> addressList)
{
addressList.Add(new Address);
return PartialView(addressList);
}
then have a partial view that just renders out the address fields again:
#model List<MyNamespace.Address>
#{
//Hack to get validation on form fields
ViewContext.FormContext = new FormContext();
}
#Html.EditorForModel()
make sure you address fields are all in one container and then you can just overwrite the existing ones with the returned data and your new address fields will be appended at the bottom. Once you have updated your container you can do something like this to rewire the validation:
var data = $("form").serialize();
$.post("/Customer/AddAddress", data, function (data) {
$("#address-container").html(data);
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
});
NB. I know some people with have an issue with doing it this way as it requires a server side hit to add fields to a page that could easily just be added client side (I always used to do it all client side but tried it once with this method and have never gone back). The reason I do it this way is because it's the easiest way to keep the indexes on the list items correct especially if you have inserts as well as add and your objects have a lot of properties. Also, by using the partial view to render the data you can ensure that the validation is generated on the new fields for you out of the box instead of having to hand carve the validation for the newly added client side fields. The trade off is in most cases a minor amount of data being transferred during the ajax request.
You may also choose to be more refined with the fields you send to the AddAddress controller, as you can see I just post the entire form to the controller and ignore everything but the Address fields, I am using fast servers and the additional (minor) overhead of the unwanted form fields is negligible compared to the time I could waste coding this type of functionality in a more bandwidth efficient manner.
You pass your root model object to the View call in your controller like this:
public ActionResult Index() {
var customer = GetCustomer(); // returns a Customer
return View(customer);
}
And then your view looks something like this:
#model Customer
<!DOCTYPE html>
<!-- etc., etc. -->
<h1>Customer #Model.Name</h1>
<ul>
#foreach (var address in Model.Addresses) {
<li>#address.Line1</li>
}
</ul>
One gets the picture.
The code above depends on the #model directive, which is new in ASP.NET MVC 3 (see this blog post).
Is a good question :D for normal navigation properties such as Accounts doing this is not to hard:
#Html.EditorFor(model => model.Accounts.ID)
#Html.EditorFor(model => model.Accounts.VATNo)
will do something you want. But for collection navigation properties (Addresses and Contacts) you can't do this in one place by default. I suggest you use a different page for Addresses (and one for Contacts). Because it is the easiest way. But if you want to do this in one place (and also with out AJAX requests), you can create view by Customer, use scaffolding for model and it's simple navigation properties, and for lists (Addresses, Contacts) you must add them with JavaScript to the input fields (for example for each Address added, put it in an Array) and post fields to server. At server you can get main model and simple properties by default model-binder and for lists, you can 1) create your own model binder 2) parse them from inputted strings by yourself. Good lock

ASP.NET MVC DisplayAttribute and interfaces

I have some interface and classes
public inteface IRole
{
int Id { get; }
string Name { get; set; }
}
public class Role : IRole
{
public int Id { get; }
[Display("Role Name")]
public string Name { get; set; }
}
public class Member
{
[Display("Login")]
public string Login { get; set; }
[Display("Password")]
public string Password { get; set; }
public IRole Role { get; set; }
}
on View I try to use, View is strongly type of Member
on this line displays correct message from DisplayAttribute
<%= Html.LabelFor(m => m.Login) %>
on this line it does not display correct label from DisplayAttribute
<%= Html.LabelFor(m => m.Role.Name) %>
How can I fix this to have correct labels in such architecture?
Thanks
P.S. I know about adding DisplayAttribute to the interface field and all will work, but maybe there are different solution.
Everything is working as designed here.
You already know your answer. If you display a form for IRole you must also have the attributes on IRole.
In order to have the correct labels you'd have to implement your own TypeDescriptors or ModelMetadataProvider in order to "smush" together the metadata for an interface into any concrete classes that implement them.
This will become really complex real fast.
Why can't you just add the attribute to the IRole interface?

Resources