8

RavenDB ドキュメント データベースに格納されている 2 つのエンティティ間の参照が必要です。これはリレーショナル データベースではないため、RavenDB のドキュメントに記載されている非正規化参照手法を使用する必要があることはわかっています。最初はこれで問題ないように思えますが、双方向参照を含む実際のドメイン「階層」を作成し始めると、これらすべての参照を最新の状態に保つ努力が不釣り合いに感じられます。私はどこかで間違っているかもしれないと感じています。

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プロパティを更新する方法が必要です。変更を行うには、まずRavenDB から正しいインスタンスを見つける必要があり、そのためにはインデックスが必要です。UserReferenceAssessmentAssessment

レイヴン インデックス

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

Userこのインデックスは、の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
                                            }
                                    }
                    }
            };
}

それだ。そして、あなたが知っているように、私はそれをすべてレイアウトしたので、あなたが何を意味するかがわかります。リファレンスを更新するだけでも大変な作業です。おそらく、Service コードをより DRY にして、さまざまな関係タイプに再利用できますが、参照されるタイプごとに 1 つずつ、多数のインデックスを作成することから逃れる方法がわかりません。

于 2012-05-08T10:23:14.440 に答える