views:

174

answers:

2

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.

+3  A: 

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.

LukLed
I believe @arch's edit action is correct. He's passing the entire object to the ActionResult. I've done exactly the same before. What's not clear in the question is how the task is given to the view in the first place. Whether he passes the object to the edit action or does a lookup. If it's a lookup then your answer stands, if not then all's good with his.
griegs
Question is not clear enough. It would be good to know what author is trying to achieve. He could also decorate action with [HttpGet] or [HttpPost] attribute.
LukLed
Yeah agreed. He could decorate with HttpPost but in the code he's provided it's not required but like you say, it's kinda unclear at this point.
griegs
ActionLink usage suggests it is GET method. Passing complex object to GET method doesn't look here like a good idea.
LukLed
Look I agree with you 100%. I'm just saying that in this case I don't think it's the issue. I do believe however that he needs to wrap it all in a Form but I think the main issue here, and the reason it's not working, is "new TaskFormModel() { Ta..." He should drop the ().
griegs
As far as I know `new TaskFormModel() { Task = item, LoginInfo = Model.LoginInfo } == new TaskFormModel { Task = item, LoginInfo = Model.LoginInfo }`
LukLed
I think that there is some misunderstanding in how MVC works that needs to be explained.
LukLed
I wasn't aware that the former works. I tried it in C# and there were errors.
griegs
In VS2008 Resharper just shows that brackets are not needed, but code compiles.
LukLed
Ah yes, Resharper is indeed the issue here. Nice skills @LukLed.
griegs
Thank you all, i guess I should give some more clarification. What I actually wanted to achieve is to pass an instance from one view to another, without going to repository/db again in my controller's action.
arch stanton
You can place object in TempData in your POST method, redirect to another GET method and take it out of TempData. Maybe you'll have to explain more.
LukLed
A: 

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.

Alexander Taran