【问题标题】:WebApi create and EF InheritanceWebApi 创建和 EF 继承
【发布时间】:2018-09-12 21:06:23
【问题描述】:

我有以下实体:

abstract class User
{
    string Id 
    string Name 
}

class UserA: User
{
    string PropA
}

class UserB : User
{
    string PropB
}

有一个唯一的带有动态参数的create(post)并根据属性实例化子类是一个很好的解决方案吗?

[HttpPost]
public IActionResult Create([FromBody]dynamic data)
{
    if (data.PROP == null)
    {
        _context.Users.Add(new UserA(data.PropA));
    }
    else
    {
        _context.Users.Add(new UserB(data.PropB));
    }

 ...

【问题讨论】:

  • 不,你最好使用某种工厂模式(在我看来)。这不是很容易维护。

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


【解决方案1】:

不要使用dynamic。我真的有点惊讶,它的工作原理。尽管没有迹象表明您已经实际测试过此代码,但可能还没有。 modelbinder 需要知道要绑定的具体类型,以便它可以确定如何将值映射到目标实例。如果没有强类型,它只能将所有内容都变成字符串,因为这就是它在请求正文中的方式。

无论如何,对于这样的事情,正确的方法是使用视图模型。您的视图模型应该包含所有各种可能的派生类型的所有属性。同样,modelbinder 需要这些来确定如何将请求正文中的数据映射过来,因此如果属性不存在,它将简单地丢弃关联的数据。

这也是为什么不能简单地使用基类的原因。如果这是一个正常的方法,你可以这样做:

public IActionResult Create([FromBody]User data)

然后,在内部,您可以使用模式匹配或类似的方法来转换为正确的派生类型。这是可行的,因为最终,内存中的对象实际上是UserA 之类的实例,而您只是将其向上转换为User。因此,您始终可以将其转换回 UserA。但是,动作不同。来自请求的内容是不是一个对象实例。 modelbinder 用于通过检查它需要绑定到的参数来从中创建一个对象实例。如果该参数的类型为User,那么它将填充User 上的属性,并丢弃其他所有内容。结果,内存中的对象只是User,并且没有办法转换为UserA 之类的东西——至少就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.
 }

或者:

if (user is UserA userA)
{
     // do something with `userA`
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-22
    • 2021-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-14
    相关资源
    最近更新 更多