2012-05-08 48 views
8

我想在存儲在RavenDB文檔數據庫中的兩個實體之間有一個引用。由於這不是關係數據庫,我知道我應該使用RavenDBs文檔中描述的非規範化引用技術。雖然起初這看起來很好,但一旦我開始創建包含雙向引用的真實世界域「層次結構」,保持所有這些引用爲最新的努力會感覺不成比例。我覺得我可能會在某個地方出錯。如何在RavenDB中實現非規範化引用

你能解釋一下使用RavenDB建模一個相當複雜的域結構的最好/最簡單的方法嗎?

感謝

+0

此博客文章可能會幫助您決定:http://daniellang.net/how-to-handle-relations-in-ravendb/ – dasheddot

+0

謝謝dasheddot,這真的很有用,謝謝。 –

+0

這看起來很有趣。 Multi Maps/Reduce索引[here](http:// ayende。com/blog/89089/ravendb-multi-maps-reduce-indexes) – biofractal

回答

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。此值不會經常更改,但可能會更改,因此我們需要更新Assessment類中保存的UserReference屬性中UserName屬性的方法。要做出改變,我們必須首先從RavenDB中找到正確的Assessment實例,爲此我們需要一個索引。

烏鴉指數

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

該指數需要每當UserUserName值更新爲被調用。我有一個UserService類,它可以幫助我協調所有與用戶有關的功能,所以這就是我放置這些代碼的地方。

我重複使用此代碼的其他參考,所以它已被抽象出一點。這可以幫助您創建更復雜的層次結構(或者「域圖」更好地描述)。

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 
              } 
            } 
        } 
      }; 
} 

這就是它。而且你知道,現在我全力以赴瞭解了你的意思。它很多工作只是爲了更新參考。也許服務代碼可以做得更幹,並且可以重用於不同的關係類型,但是我不知道如何避免編寫大量的索引,每個引用類型一個索引。

+0

非常感謝生物分形,我嘗試了更復雜的路線,這看起來很理想。 –