8

我想在 RavenDB 文档数据库中存储的两个实体之间进行引用。由于这不是关系数据库,我知道我应该使用 RavenDBs 文档中描述的非规范化引用技术。虽然一开始这似乎很好,但一旦我开始创建一个包含双向引用的真实域“层次结构”,那么让所有这些引用保持最新的努力感觉不成比例。我觉得我可能在某个地方出错了。

您能否解释使用 RavenDB 对相当复杂的域层次结构建模的最佳/最简单方法?

谢谢

4

1 回答 1

6

我不确定这是否足以回答您的问题,但这是我在 RavenDB 中创建非规范化引用的方法(这取自真实代码,为清楚起见删除了非必需项)

领域

public class User : IUserIdentity
{
    public string UserName { get; set; }
    public IEnumerable<string> Claims { get; set; }
    public string Id { get; set; }
    public Guid FormsAuthenticationGuid { get; set; }
}

public class Assessment
{ 
    public string Id { get; set; }
    public UserReference User { get; set; }
    public AssessmentState State { get; set; }
}

您可以看到我有一个Assessment引用User. 此用户参考使用以下UserReference类进行管理。

非规范化参考

public class UserReference
{
    public string Id { get; set; }
    public string UserName { get; set; }

    public static implicit operator UserReference(User user)
    {
        return new UserReference
                {
                        Id = user.Id,
                        UserName = user.UserName
                };
    }
}

请注意参考类如何也带有UserName. 这个值不会经常改变,但它可能会改变,所以我们需要一种方法来更新类中保存的UserName属性中的UserReference属性Assessment。要进行更改,我们必须首先Assessment从 RavenDB 中找到正确的实例,为此我们需要一个索引。

乌鸦指数

public class Assessment_ByUserId : AbstractIndexCreationTask<Assessment>
{
    public Assessment_ByUserId()
    {
        Map = assessments => from assessment in assessments
                                select new
                                    {
                                            User_Id = assessment.User.Id
                                    };
    }
}

User每当更新 a的UserName值时,都需要调用此索引。我有一个UserService类可以帮助我协调所有与用户相关的功能,所以这就是我放置此代码的地方。

我将此代码重用于其他参考,因此它被抽象了一点。这可以帮助您创建您想要的更复杂的层次结构(或者“域图”可能是更好的描述)。

用户服务

public static void SetUserName(IDocumentSession db, string userId, string userName)
{
    var user = db.Load<User>(userId);
    user.UserName = userName;
    db.Save(user);
    UpdateDenormalizedReferences(db, user, userName);
}

private static void UpdateDenormalizedReferences(IDocumentSession db, User user, string userName)
{
    db.Advanced.DatabaseCommands.UpdateByIndex(
            RavenIndexes.IndexAssessmentByUserId,
            GetQuery(user.Id),
            GetUserNamePatch(userName),
            allowStale: true);

}

private static IndexQuery GetQuery(string propertyValue, string propertyName = "User_Id")
{
    return new IndexQuery {Query = string.Format("{0}:{1}", propertyName, propertyValue)};
}

private static PatchRequest[] GetUserNamePatch(string referenceValue, string referenceName = "User")
{
    return new[]
            {
                    new PatchRequest
                    {
                            Type = PatchCommandType.Modify,
                            Name = referenceName,
                            Nested = new[]
                                    {
                                            new PatchRequest
                                            {
                                                    Type = PatchCommandType.Set,
                                                    Name = "UserName",
                                                    Value = referenceValue
                                            }
                                    }
                    }
            };
}

这就对了。而且你知道,现在我把它全部列出来了,我明白你的意思了。更新参考就需要做很多工作。也许服务代码可以变得更加 DRY 并重用于不同的关系类型,但我不知道如何避免编写大量索引,每个引用类型一个。

于 2012-05-08T10:23:14.440 回答