使用oData忽略POST中的JSON属性

时间:2018-02-27 17:43:02

标签: c# entity-framework api odata

我正在尝试使用EntityFramework和OData v4构建API。

问题: 我需要一些额外的数据Item,这些数据不在我的数据库中来创建新的Item,但是oData赢了如果我在POST调用中向我的JSON对象添加一些数据,则不会将其识别为[NotMapped]

我使用EntityFrameWork,因此,根据this question,我尝试在模型中使用数据注释.Ignore(t => t.extraProperty);extraProperty。但oData似乎忽略了它。

我从这个POST获得的所有内容{ "name": "John Doe", "extraProperty": "Random string" } 是:

  

不支持非开放类型的非类型化值。

代码

JSON我发来的POST电话:

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
    <edmx:DataServices>
        <Schema Namespace="MyApi.Models" xmlns="http://docs.oasis-open.org/odata/ns/edm">
            <EntityType Name="Items">
                <Key>
                    <PropertyRef Name="id" />
                </Key>
                <Property Name="id" Type="Edm.Int32" Nullable="false" />
                <Property Name="name" Type="Edm.String" Nullable="false" />                
            </EntityType>           
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

$ metadata:

namespace MyApi.App_Start
{
    public class OdataConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Items>("Items");
            config.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
            config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
        }
    }
}

ODataConfig.cs

[Table("Item.Items")]
public partial class Items
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Items(){}

    public int id { get; set; }

    public string name { get; set; }

    [NotMapped] // I already tried this, it's not working
    public string extraProperty{ get; set; }
 }

Items.cs

public partial class MyModel: DbContext
{
    public MyModel()
        : base("name=MyModel")
    {

        Database.SetInitializer<MyModel>(null);
    }

    public virtual DbSet<Items> Items{ get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // I also tried this but not working
        modelBuilder.Entity<Items>()
            .Ignore(e => e.extraProperty);
    }
}

MyModel.cs

public class ItemsController : ODataController
{
    private MyModeldb = new MyModel();

    // POST: odata/Items 
    public async Task<IHttpActionResult> Post(Items items)
    {
        // items is always null when enterring here
        // and this condition is always triggered
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        // Do some stuff with extraProperty here

        db.Items.Add(items);
        await db.SaveChangesAsync();

        return Created(items);
    }
}

MyController.cs

<package id="EntityFramework" version="6.2.0" targetFramework="net461" />
<package id="Microsoft.Data.Edm" version="5.8.3" targetFramework="net461" />
<package id="Microsoft.AspNet.OData" version="6.1.0" targetFramework="net45" />
<package id="Microsoft.Data.OData" version="5.8.3" targetFramework="net461" />
<package id="Microsoft.OData.Core" version="7.4.1" targetFramework="net45" />
<package id="Microsoft.OData.Edm" version="7.4.1" targetFramework="net45" />

部分package.config

extraProperty

我还想过制作一个拦截器,在调用post之前清除我的json,但根据this question,Web API OData不支持查询拦截器......

如何处理此错误并避免错误?我真的需要在POST方法中处理ColA ColB AA1 Want1 AA2 Want2 AA3 Want3 ,或者至少在此之前处理。{/ p>

4 个答案:

答案 0 :(得分:1)

取决于您想要使用的&#34;额外数据&#34;因为,对于post方法使用输入模型更简单,用数据做你想做的事情,然后填写正确的EF Model属性。如果Annotations和FluentAPI不适合你,这将是最简单的解决方案

&#13;
&#13;
public partial class ItemsInput
{
    public int id { get; set; }
    public string name { get; set; }
    public string extraProperty{ get; set; }
 }
 
 public async Task<IHttpActionResult> Post(ItemsInput itemsInput)
    {
        // This shouldn't be triggered anymore unless it's a valid error
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        // Do some stuff with extraProperty here
        
        //Convert the input object to json string
        var itemsInputJson = JsonConvert.SerializeObject(itemsInput);
        //Load json string to the EF Model, this will fill up all compatible
        //properties and ignore non-matching ones
        Items items = JsonConvert.DeserializeObject<Items>(itemsInputJson);
        db.Items.Add(items);
        await db.SaveChangesAsync();

        return Created(items);
    }
&#13;
&#13;
&#13;

答案 1 :(得分:1)

Items课程中,删除

[NotMapped]属性
public string extraProperty{ get; set; }

并在MyModel

中保留以下代码
modelBuilder.Entity<Items>()
            .Ignore(e => e.extraProperty);

[NotMapped]属性告诉OData在序列化和反序列化Items类时忽略extraProperty。但是,由于您要在POST的{​​{1}}请求中使用它,因此您无法在此方案中使用ItemsController属性,因此模型绑定会根据您的需要进行

答案 2 :(得分:0)

FluentAPI方式正常工作(多次测试)。 你能提供$元数据吗? 再次尝试删除NotMapped属性并在模型构建器上添加Ignore。

或者,您可以在GetEdmModel方法中将此属性添加到IEdmModel:

NSView

答案 3 :(得分:0)

您可以使用AutoMapper将所有内容映射到DTO,然后在控制器中手动应用QueryOptions。

注意:请记住包括

  

使用AutoMapper;

     

使用AutoMapper.QueryableExtensions;

public class ItemDTO 
{
     public int Id { get; set;}
     public string Name { get; set;}
     public string CustomProperty { get; set; }
}

public class ItemsController : ApiController
{
    MyCustomContext _context;
    public ItemsController(MyCustomContext context)
    {
        _context = context;
    }

    public IEnumerable<ItemDTO> Get(ODataQueryOptions<Item> q)
    {
       var itemsQuery = _context.Items.AsQueryable();
       itemsQuery = q.ApplyTo(itemsQuery , new ODataQuerySettings()) as IQueryable<Item>;

       var mapperConfiguration = this.GetMapperConfiguration();
       return itemsQuery.ProjectTo<ItemDTO>(mapperConfiguration);
    }

    public IConfigurationProvider GetMapperConfiguration()
    {
        return new MapperConfiguration(x => x.CreateMap<Item, ItemDTO>().ForMember(m => m.CustomProperty, o => o.MapFrom(d => d.Id + "Custom")));
    }
}

注意:您必须使用无法使用MapFrom

ResolveUsing方法进行地图制作