WebApi创建和EF继承

时间:2018-09-11 21:27:21

标签: entity-framework asp.net-core-webapi

我有以下实体:

insertion_sort

使用动态参数进行唯一的创建(发布)并根据属性实例化子类是一个很好的解决方案吗?

abstract class User
{
    string Id 
    string Name 
}

class UserA: User
{
    string PropA
}

class UserB : User
{
    string PropB
}

1 个答案:

答案 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.
 }