首页 文章

JSON补丁和“聚合”DTO

提问于
浏览
1

一个有点人为但却很重要的例子 .

假设以下情况 UserDetails 是一个聚合DTO(不确定正确的术语,请教育我,但基本上是来自不同商店/服务的收集信息的模型),它由RESTful Web服务使用 . 它不一定具有与它们一起收集的对象相同的属性名称 .

public class UserDetails
{
    public int UserId { get;set; }
    public string GivenName { get; set; }
    public string Surname { get; set; }
    public int? UserGroupId { get;set; } // FK in a different database
}

让我们的商店坚持以下型号:

public class User
{
    public int Id { get; set; }
    public string GivenName { get; set; }
    public string Surname { get; set; }
}


public class UserGroup
{
    public int UserId { get; set; }
    public int GroupId { get; set; }
}

这样就可以填充UserDetails对象:

User user = _userService.GetUser(userId) ?? throw new Exception();
UserGroup userGroup = _userGroupService.GetUserGroup(user.Id);

UserDetails userDetails = new UserDetails {
    UserId = user.Id,
    GivenName = user.GivenName,
    Surname = user.Surname,
    UserGroupId = userGroup?.GroupId
};

也就是说,设置 FirstNameSurname 应该委托给 UserService ,而 UserGroupId 委托给 GroupService .

这个 UserDetails 对象用于GET和PUT,这里的逻辑非常简单,但是为PATCH请求发送了该对象的JSON补丁文档 . 这显然要复杂得多 .

我们怎样才能改变用户的群体?我想出的最好的(“最好的”使用非常松散)是这样的:

int userId;
JsonPatchDocument<UserDetails> patch;

// This likely works fine, because the properties in `UserDetails`
// are named the same as those in `User`
IEnumerable<string> userPaths = new List<string> {"/givenName", "/surname"};
if (patch.Operations.Any(x => userPaths.Contains(x.path))) {
    User user = _userService.GetUserByUserId(userId);
    patch.ApplyTo(user);
    _userService.SetUser(userId, user);
}

// Do specialised stuff for UserGroup
// Can't do ApplyTo() because `UserDetails.UserGroupId` is not named the same as `UserGroup.GroupId`
IEnumerable<Operation<UserDetails>> groupOps = patch.Operations.Where(x => x.path == "/userGroupId");
foreach (Operation<UserDetails> op in groupOps)
{
    switch (op.OperationType)
    {
        case OperationType.Add:
        case OperationType.Replace:
            _groupService.SetOrUpdateUserGroup(userId, (int?)(op.value));
            break;

        case OperationType.Remove:
            _groupService.RemoveUserGroup(userId);
            break;
    }
}

这非常糟糕 . 这是一个很大的样板,并依赖于一个神奇的字符串 .

无需在 Microsoft.AspNetCore.JsonPatch API中请求更改,例如

JsonPatchDocument<UserDetails> tmpPatch = new JsonPatchDocument<UserDetails>();
tmpPatch.Add(x => x.GivenName, String.Empty);
tmpPatch.Add(x => x.Surname, String.Empty);
IEnumerable<string> userPaths = tmpPatch.Operations.Select(x => x.path);

至少会摆脱魔法弦,但是,imo,这只是感觉不对劲!

JsonPatch在这方面看起来非常有限,似乎更适合于DAO(实体)和DTO(模型)之间存在1:1映射的系统 .

有人有什么好主意吗?不能难以击败我提出的肚子!!

1 回答

  • 0

    Json Merge补丁 - RFC7396将更适合这一点 .

相关问题