【问题标题】:Example of Kendo Grid MVC Wrapper with Web API 2 Controller带有 Web API 2 控制器的 Kendo Grid MVC Wrapper 示例
【发布时间】:2014-05-03 08:53:37
【问题描述】:

我正在尝试使用 Web API 2 控制器查找 Kendo Grid MVC 服务器包装器的示例,但运气不佳。有没有办法将这些服务器包装器与 VS 生成的控制器一起使用,使用“添加”->“控制器”->“带有操作的 Web API 2 控制器,使用实体框架?”使用异步控制器操作会影响这一点吗?

我查看了 Telerik (http://docs.telerik.com/kendo-ui/tutorials/asp.net/asp-net-hello-services) 和示例项目 (http://www.telerik.com/support/code-library/binding-to-a-web-apicontroller-6cdc432b8326) 的教程,但它们都没有使用 VS 可以自动生成的新 Web API 2 控制器。

我更希望能够使用 VS 生成的控制器,但我的技能显然无法适应现有的示例(这是我第一个使用 MVC/Web API 的项目)。有没有其他人这样做过,还是我应该像那些两年前的例子那样编写控制器?

预计到达时间:仅包括我目前得到的起点:

我使用数据库中的 EF Code First 创建模型。我用来测试的最简单的就是这个:

namespace TestProject.Models
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;

    [Table("TP.Industry")]
    public partial class Industry
    {
        public Industry()
        {
            Companies = new HashSet<Company>();
        }

        public int IndustryId { get; set; }

        public int IndustryCode { get; set; }

        [Required]
        [StringLength(150)]
        public string IndustryName { get; set; }

        public virtual ICollection<Company> Companies { get; set; }
    }
}

然后,我使用“带有动作的 Web API 2 控制器,使用实体框架”选项创建了控制器,并选中了“使用异步控制器动作”以获得该模型的以下控制器:

using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using TestProject.Models;

namespace TestProject.Controllers
{
    public class IndustryController : ApiController
    {
        private TestProjectContext db = new TestProjectContext();

        // GET api/Industry
        public IQueryable<Industry> GetIndustries()
        {
            return db.Industries;
        }

        // GET api/Industry/5
        [ResponseType(typeof(Industry))]
        public async Task<IHttpActionResult> GetIndustry(int id)
        {
            Industry industry = await db.Industries.FindAsync(id);
            if (industry == null)
            {
                return NotFound();
            }

            return Ok(industry);
        }

        // PUT api/Industry/5
        public async Task<IHttpActionResult> PutIndustry(int id, Industry industry)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != industry.IndustryId)
            {
                return BadRequest();
            }

            db.Entry(industry).State = EntityState.Modified;

            try
            {
                await db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!IndustryExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);
        }

        // POST api/Industry
        [ResponseType(typeof(Industry))]
        public async Task<IHttpActionResult> PostIndustry(Industry industry)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            db.Industries.Add(industry);
            await db.SaveChangesAsync();

            return CreatedAtRoute("DefaultApi", new { id = industry.IndustryId }, industry);
        }

        // DELETE api/Industry/5
        [ResponseType(typeof(Industry))]
        public async Task<IHttpActionResult> DeleteIndustry(int id)
        {
            Industry industry = await db.Industries.FindAsync(id);
            if (industry == null)
            {
                return NotFound();
            }

            db.Industries.Remove(industry);
            await db.SaveChangesAsync();

            return Ok(industry);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }

        private bool IndustryExists(int id)
        {
            return db.Industries.Count(e => e.IndustryId == id) > 0;
        }
    }
}

最后,我将以下 Kendo UI MVC 包装器代码放入我的网格视图中:

@(Html.Kendo().Grid<TestProject.Models.Industry>()
    .Name("Grid")
    .Columns(columns =>
    {
        columns.Bound(c => c.IndustryId);
        columns.Bound(c => c.IndustryCode);
        columns.Bound(c => c.IndustryName);
        columns.Command(c =>
        {
            c.Edit();
            c.Destroy();
        });
    })
    .ToolBar(tools =>
    {
        tools.Create();
    })
    .Sortable()
    .Pageable(pageable => pageable
        .Refresh(true)
        .PageSizes(true)
        .ButtonCount(5))
    .Filterable()
    .DataSource(dataSource => dataSource
        .Ajax()
        .Model(model =>
        {
            model.Id(c => c.IndustryId);
        })
        .Read(read => read.Url("../api/Industry").Type(HttpVerbs.Get))
        .Create(create => create.Url("../api/Industry").Type(HttpVerbs.Post))
        .Update(update => update.Url("../api/Industry").Type(HttpVerbs.Put))
        .Destroy(destroy => destroy.Url("../api/Industry").Type(HttpVerbs.Delete))
    )
)

<script>

    $(function () {
        var grid = $("#Grid").data("kendoGrid");

        // WebAPI needs the ID of the entity to be part of the URL e.g. PUT /api/Product/80
        grid.dataSource.transport.options.update.url = function (data) {
            return "api/Industry/" + data.IndustryId;
        }

        // WebAPI needs the ID of the entity to be part of the URL e.g. DELETE /api/Product/80
        grid.dataSource.transport.options.destroy.url = function (data) {
            return "api/Industry/" + data.IndustryId;
        }
    });

</script>

网格没有返回数据,对 api 的请求返回 500 Internal Server Error:

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
    <Message>An error has occurred.</Message>
    <ExceptionMessage>
        The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
    </ExceptionMessage>
    <ExceptionType>System.InvalidOperationException</ExceptionType>
    <StackTrace/>
    <InnerException>
    <Message>An error has occurred.</Message>
    <ExceptionMessage>
        Type 'System.Data.Entity.DynamicProxies.Industry_5BBD811C8CEC2A7DB96D23BD05DB137D072FDCC62C2E0039D219F269651E59AF' with data contract name 'Industry_5BBD811C8CEC2A7DB96D23BD05DB137D072FDCC62C2E0039D219F269651E59AF:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
    </ExceptionMessage>
    <ExceptionType>
        System.Runtime.Serialization.SerializationException
    </ExceptionType>
    <StackTrace>
        at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeAndVerifyType(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, Boolean verifyKnownType, RuntimeTypeHandle declaredTypeHandle, Type declaredType) at System.Runtime.Serialization.XmlObjectSerializerWriteContext.SerializeWithXsiTypeAtTopLevel(DataContract dataContract, XmlWriterDelegator xmlWriter, Object obj, RuntimeTypeHandle originalDeclaredTypeHandle, Type graphType) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph) at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()
    </StackTrace>
    </InnerException>
</Error>

事实上,如果我只是在那个模型上搭建一个视图,像这样:

@model IEnumerable<TestProject.Models.Industry>

@{
    ViewBag.Title = "Industries";
}

<h2>Industries</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.IndustryCode)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.IndustryName)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.IndustryCode)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.IndustryName)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.IndustryId }) |
            @Html.ActionLink("Details", "Details", new { id=item.IndustryId }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.IndustryId })
        </td>
    </tr>
}

</table>

“@foreach(模型中的变量项){”行生成“NullReferenceException:对象引用未设置为对象的实例。”错误。

似乎即使剑道部分不正确,至少所有 VS 生成的代码都应该可以正常工作,而事实并非如此。

【问题讨论】:

  • 你的困难是什么?错误?不编译代码?
  • 我已经添加了我正在测试的现有代码以及我在原始问题中遇到的错误。
  • 如果您使用客户端(例如 Postman 或 Hacks)测试您的 api,您看到结果了吗?而且,由于您使用异步,我猜您必须考虑到这一点......虽然不确定......

标签: kendo-ui kendo-grid asp.net-mvc-5 kendo-asp.net-mvc asp.net-web-api2


【解决方案1】:

因此,根据我目前的研究,似乎没有任何将 Kendo MVC 包装器与具有异步操作的 Web API 2 控制器一起使用的示例。

但是,现在 Kendo UI 入门文档中似乎有一个 Web API Editing 部分(我发誓以前没有),其中包含一个不依赖添加 DataSourceRequestModelBinder 的更新示例。 cs 文件添加到项目中。

控制器设置与前面的示例基本相同,但网格包装器上的 .DataSource 现在看起来像这样:

.DataSource(dataSource => dataSource
    .WebApi()
    .Model(model =>
    {
        model.Id(i => i.IndustryId);
        model.Field(i => i.IndustryId).Editable(false);
    })
    .Create(create => create.Url(Url.HttpRouteUrl("DefaultApi", new { controller = "Industries" })))
    .Read(read => read.Url(Url.HttpRouteUrl("DefaultApi", new { controller = "Industries" })))
    .Update(update => update.Url(Url.HttpRouteUrl("DefaultApi", new { controller = "Industries", id = "{0}" })))
    .Destroy(destroy => destroy.Url(Url.HttpRouteUrl("DefaultApi", new { controller = "Industries", id = "{0}" })))
)

那个 .WebApi() 方法只是没有出现在我的任何搜索中。不过,这似乎可行,所以我将使用它。谢谢大家!

【讨论】:

  • 它如何知道要运行哪个操作?如果您有自定义命名操作怎么办?
【解决方案2】:

Here 您可以找到一个存储库,其中包含有关 MVC 技术的更多示例。应该有一个您正在搜索的示例。

【讨论】:

  • 除非我忽略了什么,否则这些示例都与我看过的现有示例非常相似。如果我无法让最新的 VS 生成的 Web API 2 控制器工作,那么这些肯定是我作为选项 2 使用的。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多