无法创建接口实例(PagedList)

时间:2013-10-14 18:24:47

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

我有一个视图模型对象,其中包含另一个模型对象类型的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尝试了该方法,但仍然收到原始错误消息(“无法创建接口实例”)。

2 个答案:

答案 0 :(得分:5)

我能完全破解我的方式,但我不认为这是最好的解决方案。如果有人可以提供更好的答案,我会喜欢它,但我会在此期间将其发布到此处。

由于IPagedList对象是为了保存List<>的特定范围而构建的,所以我在视图模型上创建了一个显示属性,并在我的视图中使用了它。此列表(而不是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>
     .
     .

答案 1 :(得分:0)

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

ASP.Net MVC Custom Model Binding explanation

基本上,您的活页夹会为您解析您的模型,确定哪种类型的IPagedList&lt;&gt;实施。