WebApi oData v3 实体数据模型路由

Posted

技术标签:

【中文标题】WebApi oData v3 实体数据模型路由【英文标题】:WebApi oData v3 Entity Data Model routing 【发布时间】:2018-06-29 15:24:48 【问题描述】:

我已经构建了 MVC4 应用程序,并希望在名为 Api 的单独区域中添加带有 OData v3 的 Asp.Net WebApi,但是在使用 EF 集进行路由时,我能够看到 $metadata 但无法从 Get 操作中获得结果。 如果我在浏览器中按此 url (http://localhost:1254/odata/$metadata),我会得到以下输出。

This XML file does not appear to have any style information associated with it. 

The document tree is shown below.
<edmx:Edmx xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx" Version="1.0">
<script id="tinyhippos-injected"/>
<edmx:DataServices xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" m:DataServiceVersion="3.0" m:MaxDataServiceVersion="3.0">
<Schema xmlns="http://schemas.microsoft.com/ado/2009/11/edm" Namespace="SchoolProject.DAL">
<EntityType Name="NewsAnnouncement">
<Key>
<PropertyRef Name="Id"/>
</Key>
<Property Name="Id" Type="Edm.String" Nullable="false"/>
<Property Name="Title" Type="Edm.String"/>
<Property Name="Image" Type="Edm.String"/>
<Property Name="Description" Type="Edm.String"/>
<Property Name="PublishDate" Type="Edm.DateTime" Nullable="false"/>
<Property Name="CreatedDate" Type="Edm.DateTime" Nullable="false"/>
<Property Name="UserName" Type="Edm.String"/>
<Property Name="ShortDescription" Type="Edm.String"/>
</EntityType>
</Schema>
<Schema xmlns="http://schemas.microsoft.com/ado/2009/11/edm" Namespace="Default">
<EntityContainer Name="Container" m:IsDefaultEntityContainer="true">
<EntitySet Name="NewsAnnouncements" EntityType="SchoolProject.DAL.NewsAnnouncement"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

但是当我通过实际 uri http://localhost:1245/odata/NewsAnnouncements 获取操作时,我得到以下输出

"Message":"No HTTP resource was found that matches the request URI 'http://localhost:1245/odata/NewsAnnouncements'.","MessageDetail":"No type was found that matches the controller named 'NewsAnnouncements'."

这是 WebApiConfig.Cs 文件的一部分

public static void Register(HttpConfiguration config)
        
            ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
            modelBuilder.EntitySet<NewsAnnouncement>("NewsAnnouncements");

            config.Routes.MapODataServiceRoute(routeName: "ODataRoute",
                routePrefix: "odata",
                model: modelBuilder.GetEdmModel(),
                pathHandler: new DefaultODataPathHandler());

            config.Formatters.Remove(config.Formatters.XmlFormatter);
            config.EnsureInitialized();
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.LocalOnly;
            config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
        

已编辑:

 public class NewsController : ODataController

    //
    // GET: /News/
    private SchoolEntities entities = new SchoolEntities();

    private bool NewsExists(string key)
    
        return entities.NewsAnnouncements.Any(p => p.Id == key);
    


    //
    //
    // The parameterless version of the Get method returns the entire Products collection. The Get method with a key parameter looks up a product by its key (in this case, the Id property).
    //The [EnableQuery] attribute enables clients to modify the query, by using query options such as $filter, $sort, and $page
    [EnableQuery]
    public IQueryable<NewsAnnouncement> Get()

    
        return entities.NewsAnnouncements;
    
    [EnableQuery]
    public SingleResult<NewsAnnouncement> Get([FromODataUri] string key)
    
        IQueryable<NewsAnnouncement> result = entities.NewsAnnouncements.Where(p => p.Id == key);
        return SingleResult.Create(result);
    

    //To enable clients to add a new product to the database, add the following method to ProductsController.
    public async Task<IHttpActionResult> Post(NewsAnnouncement newsItem)
    
        if (!ModelState.IsValid)
        
            return BadRequest(ModelState);
        
        entities.NewsAnnouncements.Add(newsItem);
        await Task.Delay(10);
        entities.SaveChanges();
        return Created(newsItem);
    

    //PATCH performs a partial update. The client specifies just the properties to update.

    public async Task<IHttpActionResult> Patch([FromODataUri] string key, Delta<NewsAnnouncement> product)
    
        if (!ModelState.IsValid)
        
            return BadRequest(ModelState);
        
        var entity = entities.NewsAnnouncements.Find(key);
        if (entity == null)
        
            return NotFound();
        
        product.Patch(entity);
        try
        
            await Task.Delay(10);
            entities.SaveChanges();
        
        catch (DbUpdateConcurrencyException)
        
            if (!NewsExists(key))
            
                return NotFound();
            
            else
            
                throw;
            
        
        return Updated(entity);
    


    public async Task<IHttpActionResult> Put([FromODataUri] string key, NewsAnnouncement updateNews)
    
        if (!ModelState.IsValid)
        
            return BadRequest(ModelState);
        
        if (key != updateNews.Id)
        
            return BadRequest();
        
        entities.Entry(updateNews).State = EntityState.Modified;
        try
        
            await Task.Delay(10);
            entities.SaveChanges();
        
        catch (DbUpdateConcurrencyException)
        
            if (!NewsExists(key))
            
                return NotFound();
            
            else
            
                throw;
            
        
        return Updated(updateNews);
    

    // Notice that the controller overrides the Dispose method to dispose of the EntitiesContext.
    protected override void Dispose(bool disposing)
    
        entities.Dispose();
        base.Dispose(disposing);
    


任何建议都将受到高度赞赏。 谢谢。

【问题讨论】:

你能告诉我们你的控制器吗? @donMateo 请检查我已发布控制器代码的编辑代码。谢谢。 【参考方案1】:

最后我在手动添加控制器名称时找到了答案,它与复数实体集“NewsAnnouncements”不同,这就是它显示错误的原因。我将控制器名称更改为类似于实体集“NewsAnnouncementsController”而不是“NewsController”。一切都像魅力一样运作。

正确的控制器应该是这样的

 public class NewsAnnouncementsController /*NewsController*/: ODataController
 
        //
        // GET: /News/
        private SchoolEntities entities = new SchoolEntities();

        private bool NewsExists(string key)
        
            return entities.NewsAnnouncements.Any(p => p.Id == key);
        


        //
        //
        // The parameterless version of the Get method returns the entire Products collection. The Get method with a key parameter looks up a product by its key (in this case, the Id property).
        //The [EnableQuery] attribute enables clients to modify the query, by using query options such as $filter, $sort, and $page
        [EnableQuery]
        public IQueryable<NewsAnnouncement> Get()

        
            return entities.NewsAnnouncements;
        
        [EnableQuery]
        public SingleResult<NewsAnnouncement> Get([FromODataUri] string key)
        
            IQueryable<NewsAnnouncement> result = entities.NewsAnnouncements.Where(p => p.Id == key);
            return SingleResult.Create(result);
        

        //To enable clients to add a new product to the database, add the following method to ProductsController.
        public async Task<IHttpActionResult> Post(NewsAnnouncement newsItem)
        
            if (!ModelState.IsValid)
            
                return BadRequest(ModelState);
            
            entities.NewsAnnouncements.Add(newsItem);
            await Task.Delay(10);
            entities.SaveChanges();
            return Created(newsItem);
        

        //PATCH performs a partial update. The client specifies just the properties to update.

        public async Task<IHttpActionResult> Patch([FromODataUri] string key, Delta<NewsAnnouncement> product)
        
            if (!ModelState.IsValid)
            
                return BadRequest(ModelState);
            
            var entity = entities.NewsAnnouncements.Find(key);
            if (entity == null)
            
                return NotFound();
            
            product.Patch(entity);
            try
            
                await Task.Delay(10);
                entities.SaveChanges();
            
            catch (DbUpdateConcurrencyException)
            
                if (!NewsExists(key))
                
                    return NotFound();
                
                else
                
                    throw;
                
            
            return Updated(entity);
        


        public async Task<IHttpActionResult> Put([FromODataUri] string key, NewsAnnouncement updateNews)
        
            if (!ModelState.IsValid)
            
                return BadRequest(ModelState);
            
            if (key != updateNews.Id)
            
                return BadRequest();
            
            entities.Entry(updateNews).State = EntityState.Modified;
            try
            
                await Task.Delay(10);
                entities.SaveChanges();
            
            catch (DbUpdateConcurrencyException)
            
                if (!NewsExists(key))
                
                    return NotFound();
                
                else
                
                    throw;
                
            
            return Updated(updateNews);
        

        // Notice that the controller overrides the Dispose method to dispose of the EntitiesContext.
        protected override void Dispose(bool disposing)
        
            entities.Dispose();
            base.Dispose(disposing);
        

    

【讨论】:

以上是关于WebApi oData v3 实体数据模型路由的主要内容,如果未能解决你的问题,请参考以下文章

您如何将乐观并发与 WebAPI OData 控制器一起使用

OData $expand、DTO 和实体框架

OData WebAPI 2 复杂授权

WebAPI Odata 在无类型实体上应用查询选项

对 DTO 的 ASP.NET WebApi OData 支持

使用 EF 和 WebAPI,如何返回 ViewModel 并支持 IQueryable/OData? [复制]