【问题标题】:Cannot create an instance of an interface (PagedList)无法创建接口实例 (PagedList)
【发布时间】:2013-10-14 18:24:47
【问题描述】:

我有一个视图模型对象,其中包含另一个模型对象类型的 List。当用户在页面上进行查询时,如果返回的List 包含超过 300 条记录,我们希望使用分页来降低加载时​​间(某些搜索结果可以返回超过 14k 条记录)。我们使用的分页插件can be found here.

在页面上显示结果后,用户可以单击特定结果旁边的复选框,在输入文本框中输入一些信息,点击提交,然后使用该信息编辑所选记录从文本框中。

由于我们需要使用IPagedList<> 来启用分页,但是,当您点击提交时(甚至在页面点击控制器之前),我们会收到以下错误:

                 Cannot create an instance of an interface.

查看模型

这是我们用于分页的两个列表对象。 zipCodeTerritory 对象保存查询结果。 pagedTerritoryList 用于仅在用户所在的特定页面上显示结果。

    //Paging List objects
    public IPagedList<ZipCodeTerritory> pagedTerritoryList { get; set; }
    public List<ZipCodeTerritory> zipCodeTerritory { get; set; }
    public IPagedList PagingMetaData { get; set; }

控制器

这是我们的基本搜索。 .ToPagedList 方法用于指定我们想要显示的结果范围,并将它们放在pagedTerritoryList 对象中。

                //set Paged List counter variables
                int pageNumber = page ?? 1;
                int pageSize = 300;

                //Determine if Territory present?
                if (string.IsNullOrWhiteSpace(search.searchTerritory))
                {
                    //State Code ONLY search
                    search.zipCodeTerritory = (from z in db.ZipCodeTerritory
                                               where z.StateCode.Equals(search.searchState)
                                               select z).ToList();
                }
                else if(string.IsNullOrWhiteSpace(search.searchState))
                {
                    //Territory ONLY search
                    search.zipCodeTerritory = (from z in db.ZipCodeTerritory
                                               where z.IndDistrnId.Equals(search.searchTerritory)
                                               select z).ToList();
                }
                else
                {
                    //Territory AND state search
                    search.zipCodeTerritory = (from z in db.ZipCodeTerritory
                                               where z.IndDistrnId.Equals(search.searchTerritory) &&
                                                     z.StateCode.Equals(search.searchState)
                                               select z).ToList();
                }

                //Convert list to IPagedList for pagining on Index
                search.pagedTerritoryList = search.zipCodeTerritory.ToPagedList(pageNumber, pageSize);

                //Set Paged List objects
                search.PagingMetaData = new StaticPagedList<ZipCodeTerritory>(search.zipCodeTerritory, pageNumber, pageSize,
                                                                      search.zipCodeTerritory.Count).GetMetaData();

                return View(search);

查看

这是显示搜索结果的表单。如果用户选中复选框,然后点击clonedelete 按钮,则结果应该被发送回控制器的Update 方法并执行适当的编辑或删除。用户想要在编辑中覆盖的信息被输入到表单中的newTerritory/Description/etc 字段中(table 上方)。

关于@Html.PagedListPager,我发现我必须将相同的搜索条件从页面传回索引方法,因此RouteValueDictionary 中的参数数量过多。

@if (Model.zipCodeTerritory.Count > 0)
{
    using (Html.BeginForm("Update", "ZipCodeTerritory", FormMethod.Post))
    {
        @Html.HiddenFor(model => model.searchZip)
        @Html.HiddenFor(model => model.searchDate)
        @Html.HiddenFor(model => model.searchState)

        <div id="cloneBox">
            <div id="rw1">
                @Html.LabelFor(model => model.newTerritory)
                @Html.TextBoxFor(model => model.newTerritory, new { style = "width: 30px;padding-left:10px;", maxLength = 3 })
                @Html.LabelFor(model => model.newDescription)
                @Html.TextBoxFor(model => model.newDescription, new { style = "width: 250px;padding-left:10px;", maxLength = 30 })  
                @Html.LabelFor(model => model.newEffectiveDate)     
                @Html.TextBoxFor(model => model.newEffectiveDate, new { style = "width: 80px;padding-left:10px;" }) 
                <div id="rw2" style="padding-top: 10px;">
                    @Html.LabelFor(model => model.newChannelCode)
                    @Html.DropDownListFor(model => model.newChannelCode, Model.ChannelCodes, " ")
                    @Html.LabelFor(model => model.newStateCode)
                    @Html.DropDownListFor(model => model.newStateCode, Model.StateCodes, "  ")
                </div>                                 
            </div>
        </div>
        <br/>
        <div id="buttonDiv">
            <button type="submit" id="CloneButton" name="button" value="clone">Apply New Data</button>
            <button type="submit" id="deleteButton" name="button" value="delete">Delete Selected Items</button>            
        </div>


        @*Display paging only if necessary*@
        if (Model.pagedTerritoryList.Count >= 300)
        {
           <div id="pagingDiv">
               @Html.PagedListPager(new StaticPagedList<Monet.Models.ZipCodeTerritory>(Model.zipCodeTerritory, Model.PagingMetaData) , 
            Page => Url.Action("Index", new RouteValueDictionary()
               {
                   { "Page", Page},
                   { "searchZip", Model.searchZip },
                   { "searchActiveOnly", Model.searchActiveOnly },
                   { "searchDate", Model.searchDate },
                   { "searchState", Model.searchState },
                   { "searchTerritory", Model.searchTerritory },
                   { "searchChannel" , Model.searchChannelCode }
               }), PagedListRenderOptions.DefaultPlusFirstAndLast)
           </div>            
        }

        <table id="thetable" class="tablesorter" >
            <thead>
                <th>@Html.CheckBox("SelectAll")</th>
                <th>State</th>
                <th>Territory</th>
                <th>Zip</th>
                <th>Description</th>
                <th>Effective</th>
                <th>End Date</th>
                <th>Last Update Date</th>
                <th>Channel</th>
                <th></th>
            </thead>
            <tbody id="tableBody">
                @for (int i = 0; i < Model.pagedTerritoryList.Count; i++)
                {
                    <tr id="@(Model.lastEditedId == Model.pagedTerritoryList[i].Id ? "lastEdit" : "")">
                        <td>
                            @Html.CheckBoxFor(model => model.pagedTerritoryList[i].Update)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].Update)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].StateCode)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].StateCode)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].IndDistrnId)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].IndDistrnId)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].ZipCode)
                            @Html.HiddenFor(model => model.zipCodeTerritory[i].ZipCode)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].DrmTerrDesc)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].DrmTerrDesc)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].EffectiveDate)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].EffectiveDate)
                        </td>
                        <td>
                            @if (Model.pagedTerritoryList[i].EndDate.Date != DateTime.MaxValue.Date)
                            {
                                @Html.DisplayFor(model => model.pagedTerritoryList[i].EndDate)
                                @Html.HiddenFor(model => model.pagedTerritoryList[i].EndDate)                                
                            }
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].LastUpdateDate)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].LastUpdateDate)
                        </td>
                        <td>
                            @Html.DisplayFor(model => model.pagedTerritoryList[i].ChannelCode)
                            @Html.HiddenFor(model => model.pagedTerritoryList[i].ChannelCode)
                        </td>

                        @if (ViewBag.SecurityLevel >= 4)
                        {
                            <td>
                                @Html.ActionLink("Edit", "Edit", new
                                    {
                                        id = Model.zipCodeTerritory[i].Id,
                                        searchZip = Model.searchZip,
                                        searchActiveOnly = Model.searchActiveOnly,
                                        searchDate = Model.searchDate,
                                        searchState = Model.searchState,
                                        searchTerritory = Model.searchTerritory,
                                        searchChannelCode = Model.searchChannelCode
                                    })
                                @Html.HiddenFor(model => model.zipCodeTerritory[i].Id)
                            </td>                            
                        }

                    </tr>
                }
            </tbody>
        </table>
    }
}

编辑

根据下面的评论,这是表单发布到的方法的签名。它包含最初加载到页面上的ZipCodeIndex 的实例,以及来自button 的文本,以确定我们是在执行clone 还是delete

    [HttpPost]
    public ActionResult Update(ZipCodeIndex updateZip, string button)
    {

二次编辑

尝试了this question 中的方法,但仍然收到原始错误消息(“无法创建接口实例”)。

【问题讨论】:

  • 如果错误发生在控制器之前但在提交之后,那么我们可能需要控制器之前的代码。可能是控制器和视图的签名。
  • 好点,刚刚发布在上面的编辑中。谢谢!
  • 您没有将页面传递给签名。
  • 我发现以接口为属性的模型需要创建一个 ModelBinder 来确定使用哪个实现。
  • @DarthVader:不需要将页面 int 传递给 Update 操作。更新完成后页面将重置。

标签: c# .net asp.net-mvc asp.net-mvc-3 pagedlist


【解决方案1】:

我能够彻底解决这个问题,但我认为这不是最好的解决方案。如果有人可以提供更好的答案,我会很高兴,但是我会同时在这里发布。

由于IPagedList 对象是为容纳List&lt;&gt; 的特定范围而构建的,所以我只是在我的视图模型上创建了一个显示属性并在我的视图中使用了它。这个列表,而不是IPagedList 被发回控制器以进行更新,因此不会发生界面异常。

查看模型

    //Paging List objects
    public IPagedList<ZipCodeTerritory> pagedTerritoryList { get; set; }
    public List<ZipCodeTerritory> zipCodeTerritory { get; set; }
    public List<ZipCodeTerritory> displayForPaging { get; set; } 

控制器

    //Convert list to IPagedList for pagining on Index
    search.pagedTerritoryList = search.zipCodeTerritory.ToPagedList(pageNumber, pageSize);
    search.displayForPaging = search.pagedTerritoryList.ToList();

查看

    <td>
         @Html.CheckBoxFor(model => model.displayForPaging[i].Update)
         @Html.HiddenFor(model => model.displayForPaging[i].Update)
    </td>
    <td>
     .
     .

【讨论】:

  • 谢谢 - 正是我所需要的
  • 你节省了我的时间!
【解决方案2】:

我发现将接口作为属性的模型需要创建一个 ModelBinder 以确定要使用的实现。

ASP.Net MVC Custom Model Binding explanation

基本上,您的 binder 会为您解析模型,确定要实现哪种类型的 IPagedList。

【讨论】:

  • 那是public IPagedList PagingMetaData { get; set; }吗?
  • @SH-我认为是迂回的方式。
猜你喜欢
  • 2012-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多