【问题标题】:How to define anonymous object property dynamically?如何动态定义匿名对象属性?
【发布时间】:2018-04-14 13:21:17
【问题描述】:

我正在我的网站中制作一个小型应用程序,以使用 API 请求获取用户信息。

[HttpGet("GetUserInfo/{user_id}/{fields?}")]
public IActionResult GetUserInfo(string user_id, params string[] fields)
{
    var userProfile = _userManager.GetUserProfile(user_id);

    if (userProfile == null)
    {
        return Ok(null);
    }

    var userInfo = new
    {
        id = userProfile.UserId,
        email = userProfile.Email,
        name = userProfile.Name,
        // I don't want to define a null property here:
        picture_url = fields.Contains("picture_url") ? "path" : null
    };

    if (fields.Contains("picture_url"))
    {
        userInfo.picture_url = "";
    }

    return Ok(userInfo);
}

当请求有效时,它会返回一个 JSON 对象,默认包含 3 个属性:idemailname

现在,我想检查请求是否要获取有关此用户的更多信息,就像 picture_url。所以,我试过了:

if (fields.Contains("picture_url"))
{
    // error in this line
    userInfo.picture_url = "path";
}

'' 不包含对 'picture_url' 的定义,并且没有扩展方法 'picture_url' 接受类型为 '' 可以找到(您是否缺少 using 指令或程序集引用?)

如何动态地向匿名对象添加一些属性?

【问题讨论】:

  • 匿名类型是不可变的,只能在创建实例时设置属性。
  • 使用dynamic 而不是var
  • @SebastianHofmann dynamic 几乎从来都不是正确的选择,而且几乎总是在裂开的伤口上贴膏药。
  • dynamic 几乎始终是为对象动态添加属性时的正确选择,该对象将由动态语言(如 JavaScript)使用...
  • @StephenKennedy 我不同意,我宁愿选择Dictionary

标签: c# asp.net-core asp.net-core-webapi


【解决方案1】:

匿名类型是不可变的,你只能在创建实例时创建和设置属性。这意味着您需要创建所需的确切对象。所以你可以这样做:

if (fields.Contains("picture_url"))
{
    return Ok(new
    {
        id = userProfile.UserId,
        email = userProfile.Email,
        name = userProfile.Name,
        picture_url = "path"
    });
}

return Ok(new
{
    id = userProfile.UserId,
    email = userProfile.Email,
    name = userProfile.Name
});

另一个选项是使用Dictionary<string, object>。例如:

var userInfo = new Dictionary<string, object>
{
    {"id", userProfile.UserId},
    {"email", userProfile.Email},
    {"name", userProfile.Name}
};

if (fields.Contains("picture_url"))
{
    // error in this line
    userInfo.Add("picture_url", "path");
}

return Ok(userInfo);

此对象将序列化为相同的 JSON 结构:

{"id":1,"email":"email@somewhere.com","name":"Bob","picture_url":"path"}

【讨论】:

  • 对我来说也一样,匿名类型很有用,但仅在某些情况下。
  • 第二个例子可能会忽略任何大小写格式化程序。
  • @WouterHuysentruit 是的,它会使用字典键中字符串的大小写。
【解决方案2】:

您可以将对象强制转换为动态并添加属性或使用ExpandoObject 类,它基本上是一个属性包(实现为IDictionary&lt;string, object&gt;)。

var userInfo = new
{
    id = userProfile.UserId,
    email = userProfile.Email,
    name = userProfile.Name
};

dynamic result = userInfo;

if (fields.Contains("picture_url"))
{
    result.picture_url = "<your url here>";

    // alternatively use expando object
    // var expando =  (IDictionary<String, Object>)result;
    // expando.Add("picture_url", "<your url here>");
    // the first parameter is the name of the property
}

return result;

但请注意,使用 dynamic 关键字或 ExpandoObject 您会失去强类型和 Intellisense 支持。

但它允许您在最终的 json 响应中添加或删除属性。

编辑

另见我对类似问题的回答Filtering Out Properties in an ASP.NET Core API

虽然在使用 dynamic/ExandoObject 或类似 graphapi 的功能时需要小心,但它应该可以正常工作。要删除属性,您只需使用 exanod.Remove("PropertyName") 方法。

过滤也可以很简单地实现

var dictionaryResult = expando.Where(kv => filter.Contains(kv.Key))
    .ToDictionary(kv => kv.Key, kv => kv.Value);

这将删除所有未在filter 字符串数组中定义的属性。它适用于任何类型,annonymous 或强类型。

【讨论】:

  • 我在使用dynamic对象时只有1个问题:Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type.这种情况下,Linq在这里没用。
  • 您将其转换为 Linq lambda 的动态 outside(即将匿名类转换为动态)。Lambda 仅适用于强类型,而匿名类是强类型,它们是只是编译器在编译时给了一个随机的名字。
  • 对!但是,如果我有多个需要强制转换的语句怎么办?例如:我想将 Linq 与 connection_idpicture_urlstatusaddress 一起使用。铸造 1 或 2 次是可以的,我可以这样做,但多 3 或 4 次会生成代码变得难以阅读(只是避免)。
  • 您将所需的所有数据提取到 ONE 对象中,然后对其进行转换并删除您不需要的任何数据。看上面的例子,你把你需要的所有东西都放到userInfo,然后删除用户不想要的东西。如果你真的想减少从数据库中获取的数据,你将不得不通过构建表达式树并将它们传递给 lambda 来实现hard way。但是动态创建这样的表达式树是一项不平凡的任务
  • 如果您想了解表达式树的工作原理,请访问Dixon's post on EF Core 阅读
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-05
  • 2016-12-31
  • 2010-12-20
相关资源
最近更新 更多