sgjsakura / AspNetCore

ASP.NET Core Extension Library
Apache License 2.0
162 stars 26 forks source link

An item with the same key has already been added #15

Closed detilium closed 7 years ago

detilium commented 7 years ago

Getting error message

ArgumentException: An item with the same key has already been added. Key: Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata

HAPPENS WHEN I have a a typical forum structure, and hereby have threads. I'm using your pager to only show 20 comments per thread per page. Imagine being on the first page (showing 20 comments for the thread). In the bottom I have a textarea allowing logged in users to add a new comment to the thread. Whenever I press "Add comment" and submit my form, I receive this error.

SPECS Core version: 1.0.0 Sakura.AspNetCore.PagedList version: 2.0.1 Sakura.AspNetCore.Mvc.PagedList version: 2.0.9

If you need further information please tell me.

sgjsakura commented 7 years ago

@ChristianHaase Hi, It seems that this problem is not caused by the pager, but by your model structure of your pager. If possible, could you please provide all code of this cshtml file?

Thank you for your feedback and waiting for your reply :-)

detilium commented 7 years ago

Here's the part of my cshtml that is relevant.

@if (Model.Comments.TotalPage > 1)
{
    <div class="col-sm-12 pager-container">
        <pager source="Model.Comments" />
    </div>
}
@foreach (var comment in Model.Comments)
{
    <div class="comment-container col-sm-12">
        <div class="col-sm-3 thread-container-left">
            <p>@Html.ActionLink(comment.UserProfile.Username, "UserProfile", "Account", new { username = comment.UserProfile.Username }, new { @class = "user-link" })</p>
            <p>@General.Joined @Html.DisplayFor(model => comment.UserProfile.JoinDate, "ShortDate")</p>
            <p class="role-text">@Html.DisplayFor(model => comment.UserProfile.Role.Name)</p>
        </div>
        <div class="col-sm-9 thread-container-right">
            <div>
                @Html.Raw(comment.Content)
            </div>
            <span>@General.Created @Html.DisplayFor(model => comment.CreateDate, "ShortDateTime")</span>
        </div>
    </div>
}
@if (Model.Comments.TotalPage > 1)
{
    <div class="col-sm-12 pager-container">
        <pager source="Model.Comments" />
    </div>
}

using (Html.BeginForm("Comment", "Forums", FormMethod.Post))
{
    @Html.HiddenFor(model => model.Thread.Id)
    <div class="comment-container col-sm-12">
        <div class="col-sm-3 thread-container-left">

        </div>
        <div class="col-sm-9 thread-container-right">
            @Html.TextAreaFor(model => model.NewComment.Content, new { @class = "editor" })
            <button type="submit" class="btn col-sm-offset-10 col-sm-2">@General.Comment</button>
        </div>
    </div>
}

Please tell me if you need further information.

sgjsakura commented 7 years ago

@ChristianHaase Hi, thank you for your response. Unfortunately, these is no valid information in you CSHTML file. In order to find the exact problem, could you please show the final HTML content for this page when you browse it using a browser? And also, could you please test if this problem wil disappear when you remove the pager tag from the source?

Thank you for your cooperation :-)

detilium commented 7 years ago

Hello. I've tried removing the pager from the page and everything now works. I've attached an image of the html content.

I appreciate the time you put into this issue. Thank you.

Message me if you need further information.

pager

sgjsakura commented 7 years ago

@ChristianHaase Hi, thank you for your feedback, could you show me the HTML code (not the display result) for the generated page? You may send it via email if it is not convenient to put it on the Internet. :-)

detilium commented 7 years ago

Hello.

I'm sorry, I thought you meant the actual rendered content of the page :) Here's the HTML created:

html-content

Here's the HTML for the form that causes the error when submitted:

form-content

I hope this is enough? Please ask for more if needed.

sgjsakura commented 7 years ago

@ChristianHaase HI, sorry but could please provide the entire HTML code? In your snapshot a lot of code has been cascaded. If the code is too long to post in reply, you may send it via email.

detilium commented 7 years ago

Hey, sorry. I'm just making this more complicated for you ^^

Here's the full HTML content of the page, as requested.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta content="width=device-width, initial-scale=1.0" name="viewport">
    <title>Thread</title>
    <link href="/lib/bootstrap/dist/css/bootstrap.css" rel="stylesheet">
    <link href="/css/site.css" rel="stylesheet"><!-- jQuery and Bootstrap -->

    <script src="/lib/jquery/dist/jquery.js">
    </script>
    <script src="/lib/bootstrap/dist/js/bootstrap.js">
    </script>
    <script src="/js/site.js?v=EWaMeWsJBYWmL2g_KkgXZQ5nPe-a3Ichp0LEgzXczKo">
    </script><!-- Third party libraries -->
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button class="navbar-toggle" data-target=".navbar-collapse" data-toggle="collapse" type="button"><span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span></button> <a class="navbar-brand" href="/">Home</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>
                        <a href="/Gameplay">Gameplay</a>
                    </li>
                    <li>
                        <a href="/News">News</a>
                    </li>
                    <li>
                        <a href="/Media">Media</a>
                    </li>
                    <li>
                        <a href="/Forums">Forums</a>
                    </li>
                    <li>
                        <a href="/Blog">Development blog</a>
                    </li>
                    <li>
                        <a href="/Donate">Donate</a>
                    </li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    <!-- If the user is logged in, show user name + Log out function instead of Log in + Sign up -->
                    <li>
                        <a href="/Account/MyProfile">Detilium</a>
                    </li>
                    <li>
                        <a href="/Account/SignOut">Sign out</a>
                    </li>
                </ul>
            </div>
        </div>
    </div>
    <div>
        <script src="//cdn.tinymce.com/4/tinymce.min.js">
        </script> 
        <script src="/js/tinymce-options.js">
        </script>
        <div class="container body-content">
            <div class="row">
                <div class="breadcrumb-container">
                    <a href="/Forums">Forums Home</a> &gt; <a href="/Forums/Category/1">News and Announcements</a>
                </div>
                <h3>Test thread</h3>
                <div class="thread-container col-sm-12">
                    <div class="col-sm-3 thread-container-left">
                        <p><a class="user-link" href="/Account/UserProfile/Detilium">Detilium</a></p>
                        <p>Joined: 01-09-2016</p>
                        <p class="role-text">Administrator</p>
                    </div>
                    <div class="col-sm-9 thread-container-right">
                        <div>
                            <p>Here's&nbsp;<strong>some</strong>&nbsp;<em>content</em></p>
                            <p><em>Test: 21:25 (19:25)</em></p>
                        </div>
                        <hr>
                        <span>Created: 04-10-2016 19:17 | Last edited: 06-10-2016 19:25</span> | <a class="thread-admin-action" href="/Forums/RemoveSticky/3">Remove as sticky</a> | <a class="thread-admin-action" href="/Forums/SetClosed/3">Set as closed</a> | <a class="thread-admin-action" href="/Forums/EditThread/3">Edit thread</a>
                    </div>
                </div>
                <div class="col-sm-12 pager-container">
                    <ul class="pagination">
                        <li class="disabled"><span>&laquo;</span></li>
                        <li class="disabled"><span>&lsaquo;</span></li>
                        <li class="active"><span>1</span></li>
                        <li>
                            <a href="/Forums/Thread/3?page=2">2</a>
                        </li>
                        <li>
                            <a href="/Forums/Thread/3?page=2">&rsaquo;</a>
                        </li>
                        <li>
                            <a href="/Forums/Thread/3?page=2">&raquo;</a>
                        </li>
                    </ul>
                </div>
                <div class="comment-container col-sm-12">
                    <div class="col-sm-3 thread-container-left">
                        <p><a class="user-link" href="/Account/UserProfile/Detilium">Detilium</a></p>
                        <p>Joined: 01-09-2016</p>
                        <p class="role-text">Administrator</p>
                    </div>
                    <div class="col-sm-9 thread-container-right">
                        <div>
                            <p>test</p>
                        </div><span>Created: 01-01-0001 00:00</span>
                    </div>
                </div>
                <div class="comment-container col-sm-12">
                    <div class="col-sm-3 thread-container-left">
                        <p><a class="user-link" href="/Account/UserProfile/Detilium">Detilium</a></p>
                        <p>Joined: 01-09-2016</p>
                        <p class="role-text">Administrator</p>
                    </div>
                    <div class="col-sm-9 thread-container-right">
                        <div>
                            <p>test2</p>
                        </div><span>Created: 01-01-0001 00:00</span>
                    </div>
                </div>
                <div class="col-sm-12 pager-container">
                    <ul class="pagination">
                        <li class="disabled"><span>&laquo;</span></li>
                        <li class="disabled"><span>&lsaquo;</span></li>
                        <li class="active"><span>1</span></li>
                        <li>
                            <a href="/Forums/Thread/3?page=2">2</a>
                        </li>
                        <li>
                            <a href="/Forums/Thread/3?page=2">&rsaquo;</a>
                        </li>
                        <li>
                            <a href="/Forums/Thread/3?page=2">&raquo;</a>
                        </li>
                    </ul>
                </div>
                <form action="/Forums/Comment" method="post">
                    <input data-val="true" data-val-required="The Id field is required." id="Thread_Id" name="Thread.Id" type="hidden" value="3">
                    <div class="comment-container col-sm-12">
                        <div class="col-sm-3 thread-container-left"></div>
                        <div class="col-sm-9 thread-container-right">
                            <textarea class="editor" id="NewComment_Content" name="NewComment.Content"></textarea> <button class="btn col-sm-offset-10 col-sm-2" type="submit">Comment</button>
                        </div>
                    </div><input name="__RequestVerificationToken" type="hidden" value="CfDJ8EBlfs7l3ChAh0O8JGmF8k87BAU7wZuSKzRIHMii6k_vUL5vW59wwRZOblmXVok8nU5QEzPe3PHyASiKCVdpjVMa77lgsLPaX9Wq3w_QfiLuVncpWAHvV8qZx1ktlB8F28VDJJxfW2lFUcT1U_aWTGA">
                </form>
            </div>
        </div>
        <script type="text/javascript">
          $(function () {
              tinymce.init(tinyMceOptionsSmall);
          });
        </script>
        <div class="container body-content">
            <hr>
            <footer>
                <p>&copy; 2016 - Blabla</p>
            </footer>
        </div>
    </div>
</body>
</html>
detilium commented 7 years ago

Quick update. Notice the if statement before the pager. If the page count is greater than 1? I just tried increasing the page size to 20, meaning the pager is NOT rendered. I would believe the error should disappear, but no, same error happens.

Because of this, I tried removing the pager completely from my cshtml page, but the error still occurs. Perhaps this can be used to something? It seems it not related to your pager though (didn't really expect that either but I wasn't sure)

sgjsakura commented 7 years ago

@ChristianHaase Thank you for your feedback, Usually this problem is caused by the form contains multiple inputs with same name. Please make a double check for it. If possible, you may provide a minimal project archive which can reproduce this problem in order to find the exact reason more effiiciently.

sgjsakura commented 7 years ago

Because the original poster has no reply in days, I'm closing this issue now. Please feel free to reopen it of submit new issues if you have any new question.

detilium commented 7 years ago

Hey. I'm sorry I've just been so busy with everything. I'll reopen if needed. Thank you for your help.

sgjsakura commented 7 years ago

@ChristianHaase you're very welcome~

detilium commented 7 years ago

Hello again. I'm sorry, but I need to reopen this issue. I recently returned to fix this error, and removed everything besides the PagedList property. Whenever I'm posting (even without any post content) I'm getting this error. If I remove the PagedList property, I get no error.

Is there anything you might be able to do?

My form does not contain multiple values, hence my confusion, and I'm not even trying to bind any data to the PagedList property. I do suspect it has something to do with your library, and I'm quite confused.

sgjsakura commented 7 years ago

@ChristianHaase Hi, thank you for your feedback, and could you provide a minimal project which can reproduce your problem, in order to find the exact reason?

Waiting for your kind reply :-)

detilium commented 7 years ago

Here you go. I've attached the project. This is the most minimal solution I could come up with. Eberything is located in the HomeController, and Home/Index.cshtml.

Hope this helps you. (and this it's not just me having some minor issue with my understanding of MVC)

Sakura.FaultyModelBinding.zip

sgjsakura commented 7 years ago

@ChristianHaase Hi, unfortunately, I cannot reproduce your problem using provided code. Can you tell me the detailed step to reproduce the error?

Thank you again for your help :-)

detilium commented 7 years ago

Hey @sgjsakura. Sorry for the late reply, I forgot that you answered.

To reproduce do the following:

  1. Run the solution and navigate to Home/Index.html (should be the first page that loads)
  2. You can see the pager where the Comments are listed
  3. Test the pager (everything works as expected)
  4. Using the input field below the comments, add a new comment
  5. The error I'm experiencing should occur

I hope this helps you further.

sgjsakura commented 7 years ago

@ChristianHaase Hi, thank you for your detailed description. In my enviroment (lastest package with .net CORE 1.1) the post actions does success, however only the "NewComment" property of the model is correctly assigned.

I think you may want to post the IPagedList object back into the controller using a form. Unforetunatelly, the default MVC model binding system can only handle primitive value, POCO (plain object) and arrays of them. Internally, MVC implicitly create an instance of each model type, reads the input data, and set the property to its corresponding value. Form submission is done by browser with plain text, and all server side information (including strong type name and relations of objects) and lost. In the above case, the IPagedList is an interface type, thus MVC cannot create an instance of it, neither set property values. Besides, You must use index accessors (e.g. item[i].Text) so that MVC can properly re-fill the array, otherwise, all elements in collection will generate same input fields and they may overwrites each other.

I hope the above explaination can be helpful, and it is welcome to reply if you have any further quesion. :-)

detilium commented 7 years ago

Thank you for responding. Since you can get yours to work with the latest package, I'll start out by updating the project to use 1.1. I do not need to return the IPagedList property, as I only need the comment.

I will try to update my project, and return to you with the results. Again, thank you! 👍

sgjsakura commented 7 years ago

@ChristianHaase You may also try to change the model used as the post controller's argument. The model used to display can be different with the model used to submission. In your case, you just need the new comment data, thus your model for new comment action can be defined just contains them without any other parts.

detilium commented 7 years ago

Oh wow beautiful. Upgrading the project to 1.1.0 actually did it.

Thank you so much for the time you put into this. Your help is much appreciated!