如何使用 MongoDB 将动态 JSON 属性发布到 C# ASP.Net Core Web API?

And*_*ndy 7 c# asp.net json mongodb asp.net-core-webapi

如何使用 MongoDB 将动态 JSON 属性发布到 C# ASP.Net Core Web API?

似乎 MongoDB 的优势之一是能够将您需要的任何内容存储在 Object 类型属性中,但似乎不清楚如何通过 C# Web API 从客户端 ajax post 获取动态属性信息到 MongoDB .

我们希望允许管理员创建一个带有标题和开始日期/时间的事件,但我们也希望允许用户使用响应式表单为他们想要的任何内容添加自定义表单字段,例如 T 恤尺寸或膳食偏好......无论如何用户可能会在未来提出。然后,当有人注册该活动时,他们会将 EventID 和自定义字段发布到 Web API。

我们可以拥有一个带有 _id、event_id、reg_time 和 form_fields 的 Event MongoDB 集合,其中 form_fields 是存储动态数据的对象类型。

所以我们想用自定义的 FormsFields POST 这个 JSON 的变体:

变化1:

{
    "EventId": "595106234fccfc5fc88c40c2",
    "RegTime":"2017-07-21T22:00:00Z",
    "FormFields": {
        "FirstName": "John",
        "LastName": "Public",
        "TShirtSize": "XL"
    }
}
Run Code Online (Sandbox Code Playgroud)

变体2:

{
    "EventId": "d34f46234fccfc5fc88c40c2",
    "RegTime":"2017-07-21T22:00:00Z",
    "FormFields": {
        "Email": "John.Public@email.com",
        "MealPref": "Vegan"
    }
}
Run Code Online (Sandbox Code Playgroud)

我想要一个带有 Post 操作的 EventController,它采用映射到上面 JSON 的自定义 C# EventReg 对象。

事件控制器:

[HttpPost]
public void Post([FromBody]EventReg value)
{
    eventService.AddEventRegistration(value);
}
Run Code Online (Sandbox Code Playgroud)

EventReg 类:

public class EventReg
{
    public EventReg()
    {
        FormFields = new BsonDocument();
    }
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string EventRegId { get; set; }

    [BsonElement("EventId")]
    [BsonRepresentation(BsonType.ObjectId)]
    public string EventId { get; set; }

    [BsonElement("reg_time")]
    public DateTime RegTime
    {
        set; get;
    }

    [BsonElement("form_fields")]
    public MongoDB.Bson.BsonDocument FormFields { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

事件服务

public string AddEventRegistration(EventReg eventReg)
{
    this.database.GetCollection<EventReg>("event_regs").InsertOne(eventReg);
    return eventReg.EventRegId;
} 
Run Code Online (Sandbox Code Playgroud)

现在,如果我发布到控制器,我的 EventReg 为空,因为它一定不知道如何将我的 JSON FormFields 属性映射到 BsonDocument。

  • 我可以为 FormFields 使用什么类型?
  • 我可以让 FormFields 属性成为 BsonDocument 并且有一种简单的方法可以将 Web API 参数映射到它吗?
  • 是否有一些自定义序列化程序在这种情况下如何工作的示例?

我们也许可以使用动态类型并遍历已发布的属性,但这看起来很丑陋。我还从这里的帖子中看到了 JToken 解决方案,但这看起来也很丑陋。

如果 MongoDB 打算像这样动态使用,难道不应该有一个干净的解决方案来将动态数据传递给 MongoDB?有什么想法吗?

bea*_*oss 7

在 ASP.NET Core 3.0+ 中,Newtonsoft.Json 不再是默认的 JSON 序列化器。因此我会使用 JsonElement:

[HttpPost("general")]
public IActionResult Post([FromBody] JsonElement elem)
{        
    var title = elem.GetProperty("title").GetString();
    ...
Run Code Online (Sandbox Code Playgroud)


And*_*ndy 5

JToken 示例可用于获取数据,但在检索时会导致浏览器和 Postman 抛出错误并显示警告,表明内容已作为文档读取,但采用 application/json 格式。我看到 FormFields 属性被返回为 {{"TShirtSize":"XL"}} 所以也许双大括号在序列化过程中是一个问题。

我最终在 System.Dynamic 命名空间中使用了 .NET ExpandoObject。ExpandoObject 与 MongoDB BsonDocument 兼容,因此序列化会像您期望的那样自动完成。所以不需要奇怪的代码来手动处理问题中的 JToken 示例等属性。

我仍然认为应该尽可能使用更强类型的 C# 表示,但是如果您必须允许任何 JSON 内容通过 Web API 以自定义 C# 类作为输入发送到 MongoDB,那么 ExpandoObject 应该可以解决问题。

看看下面的 EventReg 类的 FormFields 属性现在是 ExpandoObject 并且没有代码来手动处理保存到 MongoDB 的对象的属性。

这是原始有问题且过于复杂的 JToken 代码,用于手动填充具有标准类型属性和动态 FormFields 属性的对象:

[HttpPost]
public void Post([FromBody]JToken token)
{
    if (token != null)
    {
        EventReg eventReg = new EventReg();
        if (token.Type == Newtonsoft.Json.Linq.JTokenType.Object)
        {
            eventReg.RegTime = DateTime.Now;
            foreach (var pair in token as Newtonsoft.Json.Linq.JObject)
            {
                if (pair.Key == "EventID")
                {
                    eventReg.EventId = pair.Value.ToString();
                }
                else if (pair.Key == "UserEmail")
                {
                    eventReg.UserEmail = pair.Value.ToString();
                }
                else
                {
                    eventReg.FormFields.Add(new BsonElement(pair.Key.ToString(), pair.Value.ToString()));
                 }
            }
        }
        //Add Registration:
        eventService.AddEventRegistration(eventReg);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用 ExpandoObject 不需要所有这些代码。请参阅下面的最终代码。Web API 控制器现在是 1 行而不是 30 行代码。此代码现在可以从上面的问题中插入和返回 JSON,而不会出现问题。

事件控制器:

[HttpPost]
public void Post([FromBody]EventReg value)
{
    eventService.AddEventRegistration(value);
}
Run Code Online (Sandbox Code Playgroud)

EventReg 类:

public class EventReg
{
    public EventReg()
    {
       FormFields = new ExpandoObject();
    }
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string EventRegId { get; set; }

    [BsonElement("event_id")]
    [BsonRepresentation(BsonType.ObjectId)]
    public string EventId { get; set; }

    [BsonElement("user_email")]
    public string UserEmail { get; set; }

    [BsonElement("reg_time")]
    public DateTime RegTime{ get; set; }

    [BsonElement("form_fields")]
    public ExpandoObject FormFields { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

事件服务:

public string AddEventRegistration(EventReg eventReg)
{
    this.database.GetCollection<EventReg>("event_regs").InsertOne(eventReg);
    return eventReg.EventRegId;
}
Run Code Online (Sandbox Code Playgroud)