Navigation Properties on Post Action - asp.net-mvc

I need to access navigation properties on a post edit action but the current way I did that looks not the best alternative as I make a call to database and "reupdate" the model. Is there a better solution?
public class Foo
{
public int Id { get; set; }
public string SomeProp { get; set; }
}
public class Bar
{
public int Id { get; set; }
public int FooId { get; set; }
public Foo Foo { get; set; }
}
[HttpPost]
public ActionResult Edit(Bar bar)
{
// Here bar.FooId is set but bar.Foo is null as bar is not a Dynamic Proxy.
...
bar = db.Bar.Find(bar.id);
TryUpdateModel(bar);
return View(bar); // Here bar.Foo is set.
}
Another way I found is:
db.Bar.Attach(bar);
db.Entity<Bar>(bar).Reference(b => b.Foo).Load();
But it requires I make a reference to all navigation properties I need.

I am not 100% sure if this is the correct or best way of doing it but, in your UI form that is handling the post either the UI elements need to be bound to the navigation properties or you could also pass them along as hidden fields if they are not being modified
<!-- example of hidden properties -->
#Html.HiddenFor(x=>x.Foo.FooId)
<!-- exampld of editable UI element mapped to navigation property's someprop property -->
#Html.TextBoxFor(x=>x.Foo.SomeProp)

Related

ModelState.IsValid is false due to hidden id field

ModelState.Isvalid always shows false.
I have the below model
public class MyModel: MyEntity
{
[Key]
public int MyModelID { get; set; }
[Required]
[StringLength(255)]
public string Forename { get; set; }
[Required]
[StringLength(255)]
public string Surname { get; set; }
}
And I have this viewmodel
public class MydatabaseVM
{
public MyModel myMode { get; set; }
public virtual Tab2 tab2 { get; set; }
public virtual Employee employee { get; set; }
public virtual Student student { get; set; }
}
Am using this in a view
<div id="email" class="tab-pane fade">
#Html.HiddenFor(model => model.MyModel.MyModelID )
</div>
In my view I have the MyModelID in a hidden field, because I have several tabs in my page, and I want to retain the id to pass to the other tabs.
Once I save, I need the MyModelID in the hidden field. Here if I click on save and when it come to the post action the ModelState.IsValid always return false.
I tried removing the hidden field and it is coming as true. Can you help me what I am doing wrong here? My actual requirement is in a page i have multiple tabs and the main tab has some basic detials .When i save the data on the first tab the details should go to database and should generate and id .when i click on other tabs(using ajax and jquery) the is should pass to those and the data should save to the tables tab2
ModelState is invalid because your are using a data model in your view which contain 2 properties (Forename and Surname) which are decorated with the [Required] attribute and you have not posted values for them.
When you remove the hidden input, the DefaultModelBinder initializes a new instance of MydatabaseVM but the value of myMode is the default (null) so there is nothing to validate.
But when you include the hidden input, its value is posted, which forces the DefaultModelBinder to initialize a new instance of MyModel and set the MyModelID. So now all properties of MyModel are validated and errors are added for the other 2 properties.
Rule 1: Always use ViewModels when editing data. In your case it should be
public class MydatabaseVM
{
public int MyModelID { get; set; }
public virtual Tab2 tab2 { get; set; }
}
and in the view
#Html.HiddenFor( m=> m.MyModelID )

How to fill a dropdown list dynamically in MVC3?

I'm creating a search application in mvc3 where I have 2 tables :
1.State:Id(pk) and state_name
2.District:Id(pk),s_id(f.k.), District_name
I am using code first and EF and have database created for it called Search
I want my index to show all states in drop down list
following is my State.cs code
public partial class State
{
public State()
{
this.Districts = new HashSet<District>();
this.Search_master = new HashSet<Search_master>();
}
public int Id { get; set; }
public string State_name { get; set; }
public virtual ICollection<District> Districts { get; set; }}
this is my District class:
public partial class District
{
public District()
{
this.Search_master = new HashSet<Search_master>();
}
public int Id { get; set; }
public string District_name { get; set; }
public int StateId { get; set; }
public virtual State State { get; set; } }
How can I call the data stored in my tables in my index page
thank you in advance!!
You can use JQuery to bind to the onChange event of the State dropdown. And do an ajax call passing the stateId to the server that will return a json list with the correct districts.
Look here for a sample
Update
on how to populate a dropdownlist look here
Pass the contents of the first dropdown in the model and use a
#Html.DropDownListFor(m => m.MyList)
Bind to the change event and send a jQuery ajax call to a server method you create in a controller. Pass the dynamic list back using JSON and parse the results. Manually add them to your dropdown.
Alternatively just do a full post back and send the second list back in the model, hiding the other dropdown while it has an empty result set.

Filter some property value in post data by ASP.NET MVC 3

I have a model like the followings:
public class MyModel {
[Required]
public string Name { get; set; }
public string Family { get; set; }
[Required]
public int Number { get; set; }
}
So for example in Edit View I have 3 Editorfor() objects and I am interesting to filter the post data of this page, actually I want to ignore Number field and just want to post Name and Family Also I need the validations of Number be active, One way is I remove Number property from MyModel and define in view by hand and write all validation script by own, but I am interesting to know is there any simpler way in MVC. Does anyone have any idea?
Controlling all that validation and model binding manually is way too complicated and error-prone. You should be using ViewModels
public class SomeSpecificViewModel
{
[Required]
public string Name { get; set; }
public string Family { get; set; }
}
public ActionResult SomeSpecificAction(SomeSpecificViewModel model)
{
//...
}
Now MVC wil validate only Name and Family
Any value not filled in the view will not be posted to the controller. However, if a field which is [Required] is not filled, then ViewModel.isValid will be false.

Pass data between viewmodels

I think I am getting somewhere with mvc.
I have created a view that is bound to my view model that looks like this
public class CreatePurchaseViewModel
{
public Order Order { get; set; }
public IEnumerable<Product> Products { get; set; }
public DateTime OrderDate { get; set; }
public bool OrderSent { get; set; }
}
I have also created another view model
public class ProductsViewModel
{
public int ProductID { get; set; }
public int OrderID { get; set; }
}
I have create a partial view that is bound to this viewmodel.
I am loading it from my view
#Html.Partial("_AddProductItem", new MVC_WireFrame.ViewModels.ProductsViewModel())
Now how do I get the items from my ProductsViewModel into my CreatePurchaseViewModel or is that not the correct way of doing things?
I can access the data from the ProductsViewModel in my controller like
[HttpPost]
public ActionResult InsertProduct(string input)
{}
2 questions really, how do I pass the value into the other viewcontroller? and
how do I populate a list within the AddProduct partial view so I can show what products have been added?
Thanks in advance for any help.
Normally you should not be passing data from one view model to another. You should create your view model classes in such a way that they contain all the required information for the view to display. It is the controller's responsibility to fill this information. Concerning you second question about the list, well you simply add a property which is of type IEnumerable<T> to this view model which will be filled by the controller and the view can show what products have been added. So think of what information your views require and add it to the view models as properties.

Passing complex object from view to controller/view in ASP.NET MVC

In my MVC application I have a problem with passing data from view to controller. I have fairly complex domain classes:
public class TaskBase : PersistableObject
{
public virtual TaskCategory Category { get; set; }
public virtual IList<TaskNote> Notes { get; set; }
public virtual string TaskTitle { get; set; }
public virtual string TaskBody { get; set; }
public virtual DateTime? CreationTime { get; set; }
public virtual User CreatedBy { get; set; }
public virtual int CompletionRatio { get; set; }
}
public class MainTask : TaskBase
{
public virtual IList<TaskBase> ChildTasks { get; set; }
public virtual User AssignedTo { get; set; }
public virtual IList<TaskHistory> History { get; set; }
}
public class TaskFormModel : ViewDomainBase
{
public MainTask Task { get; set; }
public LoginForm LoginInfo { get; set; }
}
And in my view I want to pass an instance of TaskFormModel to the controller.
<%= Html.ActionLink<TaskController>("Edit Task", (x) => x.Edit(new TaskFormModel() { Task = item, LoginInfo = Model.LoginInfo }))%>
And here is the controller action:
public ActionResult Edit (TaskFormModel taskInfo)
{
return View(ViewPageName.TaskDetailsForm, task.Task);
}
In this action method taskInfo comes null even if I pass non-null instance from view. I think I have a binding problem here. I think, writing custom model binder requires every property to be converted and also when new fields added then binder class should also be changed, so I don't want custom model binder to do this. Is there any other way to pass data to controller in this scenario? Or could custom model binder can be coded so that less code written and also when new properies are added binder class will not need to be changed?
Edit After Comments: What I am trying to achieve is basically to pass an instance from one view to another view, without querying repository/db in my controller's action.
First version of answer:
Your GET edit method should be like:
public ActionResult Edit (int id)
{
var model = taskRepository.GetTaskEditModel(id);
return View(ViewPageName.TaskDetailsForm, model);
}
and ActionLink:
<%= Html.ActionLink("Edit Task", "Edit", "Task", new { model.Task.id })%>
If you want to pass complex objects to controller, you should wrap them up in html form and pass to POST action.
In my opinion you are doing something wrong.
As I understand: you are trying to instantiate a new object, pass it to browser and get it back.
well you cant.
If object you want to edit exists already in your storage, then you should alter your ActionLink to reference it by id, and instantiate it inside your Edit action.
Take a look at default strongly typed index views created by tooling.

Resources