2

I am working with a View that returns List<ViewModel> to the Controller's Create action method.

Questions:

  1. Is it correct to return List<ViewModel> to the controller? The reason I am doing is that I have to make multiple rows entry to the DB for each user. 2. My Controller implemenation is incorrect because I am trying to add data through single User object where I have recieved List of ViewModels which is only the representation of the View for single user. So how the controller should be implemented?
  2. Do I need to have a public ActionResult Create() method in the controller?

Limitations:

  1. Can't change database
  2. None of the User or Role table in the database has int type PK

Logic:

User would be able to select multiple checkboxes(represents ROLE) for each user and that should be persisted in the database as single row. For example

enter image description here

DB: dbo.User table should have the following entries on Save

(abcdefg, levelA, locationB, Role2, null, null, Y)

(msjdhcdu, levelA, locationB, Role2, null, null, Y) Same user Different Role

(msjdhcdu, levelA, locationB, Role3, null, null, Y) Same user Different Role

USer would be kept on the same page (save VIEW) after the Save button click and showing the recent changes in the database.

ViewModel:

public class UserViewModel
{
    public string UserName { get; set; }
    public string Level { get; set; }
    public string Location { get; set; }
    public List<RoleViewModel> Roles { get; set; }
}

public class RoleViewModel
{
    public string RoleName{ get; set; }
    public bool IsSelected { get; set; }
}

View: I might be doing something very wrong in the VIEW

@model List<Project.ViewModels.UserViewModel>

@using (@Html.BeginForm("Create", "User", FormMethod.Post ,new { id = "UserPermissionForm" }))


{
<table class="table">
    <tr>
        <th>User</th>
        @for (int i = 0; i < Model[0].Roles.Count; i++)
        {
            <th>
                @Model[0].Roles[i].RoleName
            </th>
        }
    </tr>
    @for (int i = 0; i < Model.Count; i++)
    {
        <tr>
            <td>
                @Html.HiddenFor(m => m[i].UserName)
                @Model[i].UserName
            </td>
            @for (int j = 0; j < Model[i].Roles.Count; j++)
            {
                <td>
                    @Html.CheckBoxFor(m => m[i].Roles[j].IsSelected)
                </td>
            }
        </tr>
    }
</table>

<div class="form-actions">
        <button type="submit" class="btn btn-success submit" value="Save">Save changes</button>
    </div>

<script>
    $('#Submit').click(function () {
        let url = '@Url.Action("Create", "Users")'
        $.post(url, $("#UserPermissionForm"))
        });
</script>

Controller:

[HttpPost]
    public ActionResult Create(List<UserViewModel> viewModelList)
    {
        for (int i= 0; i > viewModelList.Count; i++) {
            for (int j = 0; j > viewModelList[i].Roles.Count; j++) {

                    db.UserDetails.Add(new User 
                    {
                        username = viewModelList[i].UserName,
                        level = viewModelList[i].Level,
                        location = viewModelList[i].Location,
                        role = viewModelList[i].Roles[j].RoleName,
                        Approval = "",
                        Request = "",
                        Active = "Y"
                    });
            }

            db.SaveChanges();
            return RedirectToAction("Index","Users");
        }          
        return View(viewModelList); // not right
    }

Error:

The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[Project.ViewModels.UserViewModel]', but this dictionary requires a model item of type 'Project.Models.User'.

Any guidance/help is appreciated.

EDIT1: View has been added EDIT2: Bsuiness logic added

PineCone
  • 2,193
  • 12
  • 37
  • 78
  • Please show your view page. where you define`@model` – Divyang Desai Oct 19 '16 at 12:13
  • VIEW page details added now. – PineCone Oct 19 '16 at 12:21
  • @sharlene: This no longer makes sense, given the view which was added to the question. The error states that the view expects a model of type `Project.Models.User`. Where specifically is this error happening? Are you sure this is the right view? – David Oct 19 '16 at 12:26
  • @David Yes this is the VIEW what I am suing. I presume my `javaScript` code in the view is incorrect. When I submit through post. – PineCone Oct 19 '16 at 12:48
  • @sharlene: Why would you presume that? What indication do you have in your debugging that the JavaScript is incorrect? This error happens when a controller passes an incorrect model to a view. – David Oct 19 '16 at 12:51
  • @David Most likely because I am new to it and I really don't understand where things are going wrong. Only two places I am passing the Model back to the View are my Index() method and Create() method. I will update the post with my Index() method if that gives an idea what could I probably be doing it wrong. – PineCone Oct 19 '16 at 12:55
  • @David You are absolutely right. I happen to have auto generated VIEW for Create.cshtml where `@model Project.Models.User`. Since I don't want to use that autogenerated VIEW, should I be redirecting it to the VIEW I have shown here/Remove that autogenerate VIEW? – PineCone Oct 19 '16 at 13:10
  • @sharlene: Well, in general you'd direct the user to whichever view you want them to see, and you'd provide that view with the model it needs. The options you have remain somewhat similar. You can direct the user to a different view, or you can modify the view to which you are directing the user so that it meets the needs of the system. – David Oct 19 '16 at 13:13
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/126133/discussion-between-sharlene-and-david). – PineCone Oct 19 '16 at 13:43

1 Answers1

4

Here you're sending a model to the view:

return View(viewModelList);

And that model is of type List<UserViewModel>. According to the error, however, the view is expecting a model of type User. Something like this declaration on the view itself:

@model Project.Models.User

So you have two options:

  1. Change the view to accept a model of type List<UserViewModel>, making any changes as necessary within the view itself to use that new model type.
  2. Send the view a model of type Project.Models.User (which would presumably be a transformation of one element from the List<UserViewModel>?).

On a technical level either option is perfectly valid. (Though the two are mutually exclusive, it's one or the other.) It's up to you which option makes more sense for the system you're building, whether or not that view should be operating on an instance or on a list.


Edit: Your edit to the question (to include a view) seems to indicate that you may be looking at the wrong view. The view you're showing us doesn't match the error message you're showing us. Keep in mind that the two exit paths for your controller method (a redirect and a return of a view) probably invoke different views.

In any event, the core of the issue still remains. Somewhere you're providing a view with a type which is different from what it expects. You may have to do some additional debugging to determine where in your code that's happening, but ultimately the problem and potential solution remain the same as described above.

David
  • 208,112
  • 36
  • 198
  • 279
  • Could you please, include all the questions of OP. – Divyang Desai Oct 19 '16 at 12:20
  • @Div: Can you be more specific? Much of what's being asked in the original question seems to come from the error encountered, and indicates some misunderstandings by the OP regarding how controllers and views interact. This answer attempts to explain and simplify a bit. It's not clear what the system *should* do at a business logic level, since we don't really know anything about that. But at a technical level, this answer explains what the error means and how it can be corrected. – David Oct 19 '16 at 12:23
  • Yes, but see the updated question, OP has defined `ViewModel` not an `Entity` class, on viewpage – Divyang Desai Oct 19 '16 at 12:27
  • @Div: At this point I suspect the OP is looking at the wrong view. The type specified in the error message and the type specified in the view we're seeing are not the same. – David Oct 19 '16 at 12:29
  • Yes, this view will not generate any error which OP stated in the question. – Divyang Desai Oct 19 '16 at 12:31
  • @David I understand that I am passing wrong Model. But The model in my view is List but in the controller I am taking the list and trying to persist data for each item in that list. I am not sure which code is wrong. View or Controller. Because I am already doing what you have suggested. – PineCone Oct 19 '16 at 12:44
  • @sharlene: According to the error message you're seeing, the view is *not* expecting a model of type `List`. So it seems highly likely that you're looking at the wrong view here. Or perhaps you're running a version of the code that hasn't been updated. Either way, the error message is clear. The wrong type is being passed to a view somewhere. If it's not here, then it's somewhere else. – David Oct 19 '16 at 12:47