1

我正在使用 servicestack 编写用户服务,当请求用户资源时,我想以各自 URL 的形式返回实体集合。资源是被请求用户的朋友。

我当前的用户实体

[RestService("/users")]
[RestService("/users/{Id}")]
[Alias("Users")]
public class User
{
    [AutoIncrement]
    public int      Id              { get; set; }
    public string   Name            { get; set; }
    public string   CellPhoneNumber { get; set; }
    public string   EmailAddress    { get; set; }
    public DateTime CreatedDate     { get; set; }
}

本质上,在我的 SQLite 数据库中,我会有一个名为“Friendships”的表来表达关系。桌子应该像这样工作得很好

ID       PK INT NOT NULL
UserID1  FK INT NOT NULL
UserID2  FK INT NOT NULL

我需要一个“友谊”实体来表达这一点吗?我应该向我的用户实体添加/更改什么以让 ServiceStack 将其与友谊集合一起返回?

我认为可能是这样的友谊实体

[RestService("/users/{Id}/friends")]
[Alias("Friendships")]
public class Friendship
{
    [AutoIncrement]
    public int Id      { get; set; }
    public int UserId1 { get; set; }
    public int UserId2 { get; set; }
}

并且在处理请求时,返回用户朋友的资源 URL 集合。我不确定如何继续前进,并且可以使用一些提示/建议。


结果实施

我最终将我的 Friendships 表保存在数据库中,但没有实现 FriendshipService 类,而是使用了类似于 myz 建议的东西。

[RestService("/users/{Id}/friends")]
public class UserFriends
{
    public int Id { get; set; }
}

如您所见,我一直使用mythz 的UserFriends 实体。尽管符合我的需要,但我的服务等级有所不同。他/她提出的解决方案没有解决的问题是有问题的用户实体可以是 UserId1 或 UserId2。所以这就是用我的服务类来解决问题的。

public class UserFriendsService : RestServiceBase<UserFriends>
{
    public IDbConnectionFactory DbFactory { get; set; }

    public override object OnGet(UserFriends request)
    {
        if (request.Id != default(int))
        {
            User user = DbFactory.Exec(dbCmd => dbCmd.GetById<User>(request.Id));
            var friendIds = DbFactory
                .Exec(dbCmd => dbCmd.Where<Friendship>("UserId1", request.Id).Select(fs => fs.UserId2))
                .Union(DbFactory.Exec(dbCmd => dbCmd.Where<Friendship>("UserId2", request.Id)).Select(fs => fs.UserId1));

            List<User> friends = DbFactory.Exec(dbCmd => dbCmd.GetByIds<User>(friendIds));
            return friends;
        }
        else
        {
            return DbFactory.Exec(dbCmd => dbCmd.Select<User>());
        }
    }
}

我可以通过使用选定的 Id 并dbCmd.GetById<User>()在初始查询中使用它们来选择用户实体而不是朋友 Id,但我认为这会使代码更加复杂且难以阅读。

4

1 回答 1

1

您应该尝试对外部 API 隐藏您的服务的实现细节。在这种情况下,我将为每个朋友返回一个用户 DTO 列表,例如:

[RestService("/users/{Id}/friends")]
public class UserFriends {
    public Id { get; set; }
}

public class UserFriendsService : ServiceBase<UserFriends> {
    public IDbConnectionFactory DbFactory { get; set; }
    public object Run(UserFriends request) {
        return DbFactory.Exec(dbCmd => dbCmd.Select<User>(
         @"SELECT u.* FROM Users u INNER JOIN Friendships f ON (u.Id = f.UserId2)
           WHERE f.UserId1 = {0}", request.Id));
    }
}

注意:它并不适合所有人,但我的偏好是将所有 ServiceStack 响应包装在 Typed Response DTO 中。这为客户端提供了一致的约定/类型化 API(即,它们可以预测每个服务的响应)并包括对类型化 Validation的支持。

public class UserFriendsResponse {
    public List<User> Results { get; set; }
    public ResponseStatus ResponseStatus { get; set; }
}
于 2012-07-15T19:15:24.693 回答