我有以下实体:
insertion_sort
使用动态参数进行唯一的创建(发布)并根据属性实例化子类是一个很好的解决方案吗?
abstract class User
{
string Id
string Name
}
class UserA: User
{
string PropA
}
class UserB : User
{
string PropB
}
答案 0 :(得分:2)
请勿使用dynamic
。实际上,我真的很惊讶。尽管没有迹象表明您已经实际测试过此代码,所以也许没有。模型绑定器需要知道要绑定的具体类型,以便可以确定如何将值映射到目标实例上。没有强类型,它就什么也做不了,只能将所有内容都变成字符串,因为这就是它在请求正文中出现的方式。
无论如何,对于这样的事情,正确的方法是使用视图模型。您的视图模型应包含所有各种可能的派生类型的所有属性。同样,modelbinder需要这些来确定如何映射请求主体中的数据,因此,如果属性不存在,它将简单地丢弃关联的数据。
这也是为什么您不能简单地使用基类的原因。如果这是正常方法,则可以执行以下操作:
public IActionResult Create([FromBody]User data)
然后,在内部,您可以使用模式匹配或类似方式将其强制转换为正确的派生类型。之所以可行,是因为最终,内存中的对象实际上是诸如UserA
之类的实例,而您只是将其向上投射到User
。因此,您始终可以将其投射回UserA
。但是,动作是不同的。请求发出的是不是对象实例。 Modelbinder用于通过检查需要绑定到的参数来从中创建对象实例。如果该参数的类型为User
,则它将填充User
上的属性,并丢弃其他所有内容。结果,内存中的对象只是User
,并且无法强制转换为UserA
之类的东西-至少就让{{}实例实际发布的所有值而言1}}在物体上。
这使我们回到了视图模型:
UserA
然后,让您的操作将其接受为参数:
public class UserViewModel
{
public string Id { get; set; }
public string Name { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
}
然后,在里面:
public IActionResult Create([FromBody]UserViewModel data)
与if (!string.IsNullOrWhiteSpace(data.PropA))
{
// UserA was posted, map data to an instance of UserA
}
类似。如果愿意,您还可以在数据中张贴一个明确的“类型”,然后打开它以实例化正确的类型。由你决定。为了减少代码重复,您可以实例化正确的类型,但将其存储在类型为UserB
的变量中。然后,如果您需要返回正确的类型,则可以使用模式匹配:
User
然后再来
:User user;
switch (data.Type)
{
case "UserA":
user = new UserA
{
Id = data.Id,
Name = data.Name,
PropA = data.PropA
};
break;
// etc.
default:
user = new User
{
Id = data.Id,
Name = data.Name
};
break;
}
或者:
switch (user)
{
case UserA userA:
// do something specific with `userA`
// etc.
}