我已經實現了RavenDB Denormalized Reference模式。我正努力將靜態索引和所需的修補程序更新請求連接在一起,以確保在更改引用的實例值時更新非規範化的引用屬性值。RavenDb:更新非規範化的引用屬性值
這裏是我的域名:
public class User
{
public string UserName { get; set; }
public string Id { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
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
};
}
}
public class Relationship
{
public string Id { get; set; }
public UserReference Mentor { get; set; }
public UserReference Mentee { get; set; }
}
你可以看到UserReference包含ID和引用用戶的用戶名。所以,現在,如果我更新給定用戶實例的用戶名,那麼我希望所有用戶引用中的引用用戶名值也要更新。爲了實現這一點,我寫了一個靜態指標和修補程序要求如下:
public class Relationships_ByMentorId : AbstractIndexCreationTask<Relationship>
{
public Relationships_ByMentorId()
{
Map = relationships => from relationship in relationships
select new {MentorId = relationship.Mentor.Id};
}
}
public static void SetUserName(IDocumentSession db, User mentor, string userName)
{
mentor.UserName = userName;
db.Store(mentor);
db.SaveChanges();
const string indexName = "Relationships/ByMentorId";
RavenSessionProvider.UpdateByIndex(indexName,
new IndexQuery
{
Query = string.Format("MentorId:{0}", mentor.Id)
},
new[]
{
new PatchRequest
{
Type = PatchCommandType.Modify,
Name = "Mentor",
Nested = new[]
{
new PatchRequest
{
Type = PatchCommandType.Set,
Name = "UserName",
Value = userName
},
}
}
},
allowStale: false);
}
最後一個單元測試,因爲預期的更新不工作失敗。
[Fact]
public void Should_update_denormalized_reference_when_mentor_username_is_changed()
{
using (var db = Fake.Db())
{
const string userName = "updated-mentor-username";
var mentor = Fake.Mentor(db);
var mentee = Fake.Mentee(db);
var relationship = Fake.Relationship(mentor, mentee, db);
db.Store(mentor);
db.Store(mentee);
db.Store(relationship);
db.SaveChanges();
MentorService.SetUserName(db, mentor, userName);
relationship = db
.Include("Mentor.Id")
.Load<Relationship>(relationship.Id);
relationship.ShouldNotBe(null);
relationship.Mentor.ShouldNotBe(null);
relationship.Mentor.Id.ShouldBe(mentor.Id);
relationship.Mentor.UserName.ShouldBe(userName);
mentor = db.Load<User>(mentor.Id);
mentor.ShouldNotBe(null);
mentor.UserName.ShouldBe(userName);
}
}
一切都正常運行,該指數是有,但我懷疑這是不是返回由補丁請求所需要的關係,但說實話,我已經江郎才盡了。你能幫忙嗎?
編輯1個
@MattWarren allowStale=true
沒有幫助。但是我注意到了一個潛在的線索。
因爲這是一個單元測試,所以我使用了InMemory,嵌入了IDocumentSession - 上面代碼中的Fake.Db()
。然而,當調用靜態索引時,即在執行UpdateByIndex(...)
時,它使用通用IDocumentStore,而不是特定的假IDocumentSession。
當我更改我的索引定義類,然後運行我的單元測試時,索引在「真實」數據庫中更新,可以通過Raven Studio查看更改。但是,「保存」到InMemory數據庫中的虛假域實例(mentor
,mentee
等)並未存儲在實際數據庫中(如預期的那樣),因此無法通過Raven Studio查看。
難道是我對UpdateByIndex(...)
的調用針對不正確的IDocumentSession,'真正'的一個(沒有保存的域實例),而不是假的?
編輯2 - @Simon
我已經實現了你的修爲在編輯上述1中列出的問題,我認爲我們正在取得進展。你是對的,我通過RavenSessionProvder
使用了對IDocumentStore
的靜態引用。現在情況並非如此。下面的代碼已更新爲使用Fake.Db()
。
public static void SetUserName(IDocumentSession db, User mentor, string userName)
{
mentor.UserName = userName;
db.Store(mentor);
db.SaveChanges();
const string indexName = "Relationships/ByMentorId";
db.Advanced.DatabaseCommands.UpdateByIndex(indexName,
new IndexQuery
{
Query = string.Format("MentorId:{0}", mentor.Id)
},
new[]
{
new PatchRequest
{
Type = PatchCommandType.Modify,
Name = "Mentor",
Nested = new[]
{
new PatchRequest
{
Type = PatchCommandType.Set,
Name = "UserName",
Value = userName
},
}
}
},
allowStale: false);
}
}
你會注意到我還重置了allowStale=false
。現在,當我運行此我得到以下錯誤:
Bulk operation cancelled because the index is stale and allowStale is false
我想我們已經解決了第一個問題,現在我使用的是正確的Fake.Db,我們遇到的問題首先強調,該指數是陳舊,因爲我們在單元測試中運行速度超快。
現在的問題是:如何讓UpdateByIndex(..)
方法等待,直到命令-Q爲空並且索引被視爲「新鮮」?
public static void SetUserName(IDocumentSession db, User mentor, string userName)
{
mentor.UserName = userName;
db.Store(mentor);
db.SaveChanges();
const string indexName = "Relationships/ByMentorId";
// 1. This forces the index to be non-stale
var dummy = db.Query<Relationship>(indexName)
.Customize(x => x.WaitForNonStaleResultsAsOfLastWrite())
.ToArray();
//2. This tests the index to ensure it is returning the correct instance
var query = new IndexQuery {Query = "MentorId:" + mentor.Id};
var queryResults = db.Advanced.DatabaseCommands.Query(indexName, query, null).Results.ToArray();
//3. This appears to do nothing
db.Advanced.DatabaseCommands.UpdateByIndex(indexName, query,
new[]
{
new PatchRequest
{
Type = PatchCommandType.Modify,
Name = "Mentor",
Nested = new[]
{
new PatchRequest
{
Type = PatchCommandType.Set,
Name = "UserName",
Value = userName
},
}
}
},
allowStale: false);
}
從編號的註釋以上:
-
編輯3
考慮到用於防止陳舊索引的建議,如下我已經更新了代碼
加入一個虛擬查詢強制索引等待,直到它是非陳舊的作品。有關陳舊索引的錯誤被消除。
這是一個測試線,以確保我的索引正常工作。它似乎很好。返回的結果是提供的Mentor.Id('users-1')的正確關係實例。
{ 「學長」:{ 「ID」: 「用戶-1」, 「用戶名」: 「導師先生」 }, 「受指導者」:{ 「ID」:「用戶-2 」 ‘用戶名’:‘受導先生’ } ... }
儘管指數爲未失效的,看似正常,實際修補請求看似什麼都不做。非導師非規範化參考中的UserName保持不變。
因此,懷疑現在落在補丁請求本身。爲什麼這不起作用?這可能是我設置UserName屬性值進行更新的方式嗎?
...
new PatchRequest
{
Type = PatchCommandType.Set,
Name = "UserName",
Value = userName
}
...
你會注意到,我只是分配userName
PARAM直奔Value
屬性,這是RavenJToken
類型的字符串值。這可能是一個問題嗎?
編輯4
太棒了!我們有一個解決方案。我重寫了我的代碼,以允許您提供的所有新信息(謝謝)。萬一有人實際上已經讀到這裏,我更好地投入工作代碼給他們關閉:
單元測試
[Fact]
public void Should_update_denormalized_reference_when_mentor_username_is_changed()
{
const string userName = "updated-mentor-username";
string mentorId;
string menteeId;
string relationshipId;
using (var db = Fake.Db())
{
mentorId = Fake.Mentor(db).Id;
menteeId = Fake.Mentee(db).Id;
relationshipId = Fake.Relationship(db, mentorId, menteeId).Id;
MentorService.SetUserName(db, mentorId, userName);
}
using (var db = Fake.Db(deleteAllDocuments:false))
{
var relationship = db
.Include("Mentor.Id")
.Load<Relationship>(relationshipId);
relationship.ShouldNotBe(null);
relationship.Mentor.ShouldNotBe(null);
relationship.Mentor.Id.ShouldBe(mentorId);
relationship.Mentor.UserName.ShouldBe(userName);
var mentor = db.Load<User>(mentorId);
mentor.ShouldNotBe(null);
mentor.UserName.ShouldBe(userName);
}
}
假貨
public static IDocumentSession Db(bool deleteAllDocuments = true)
{
var db = InMemoryRavenSessionProvider.GetSession();
if (deleteAllDocuments)
{
db.Advanced.DatabaseCommands.DeleteByIndex("AllDocuments", new IndexQuery(), true);
}
return db;
}
public static User Mentor(IDocumentSession db = null)
{
var mentor = MentorService.NewMentor("Mr. Mentor", "[email protected]", "pwd-mentor");
if (db != null)
{
db.Store(mentor);
db.SaveChanges();
}
return mentor;
}
public static User Mentee(IDocumentSession db = null)
{
var mentee = MenteeService.NewMentee("Mr. Mentee", "[email protected]", "pwd-mentee");
if (db != null)
{
db.Store(mentee);
db.SaveChanges();
}
return mentee;
}
public static Relationship Relationship(IDocumentSession db, string mentorId, string menteeId)
{
var relationship = RelationshipService.CreateRelationship(db.Load<User>(mentorId), db.Load<User>(menteeId));
db.Store(relationship);
db.SaveChanges();
return relationship;
}
用於單元測試的Raven會話提供程序
public class InMemoryRavenSessionProvider : IRavenSessionProvider
{
private static IDocumentStore documentStore;
public static IDocumentStore DocumentStore { get { return (documentStore ?? (documentStore = CreateDocumentStore())); } }
private static IDocumentStore CreateDocumentStore()
{
var store = new EmbeddableDocumentStore
{
RunInMemory = true,
Conventions = new DocumentConvention
{
DefaultQueryingConsistency = ConsistencyOptions.QueryYourWrites,
IdentityPartsSeparator = "-"
}
};
store.Initialize();
IndexCreation.CreateIndexes(typeof (RavenIndexes).Assembly, store);
return store;
}
public IDocumentSession GetSession()
{
return DocumentStore.OpenSession();
}
}
個
的指標
public class RavenIndexes
{
public class Relationships_ByMentorId : AbstractIndexCreationTask<Relationship>
{
public Relationships_ByMentorId()
{
Map = relationships => from relationship in relationships
select new { Mentor_Id = relationship.Mentor.Id };
}
}
public class AllDocuments : AbstractIndexCreationTask<Relationship>
{
public AllDocuments()
{
Map = documents => documents.Select(entity => new {});
}
}
}
更新規格化參考
public static void SetUserName(IDocumentSession db, string mentorId, string userName)
{
var mentor = db.Load<User>(mentorId);
mentor.UserName = userName;
db.Store(mentor);
db.SaveChanges();
//Don't want this is production code
db.Query<Relationship>(indexGetRelationshipsByMentorId)
.Customize(x => x.WaitForNonStaleResultsAsOfLastWrite())
.ToArray();
db.Advanced.DatabaseCommands.UpdateByIndex(
indexGetRelationshipsByMentorId,
GetQuery(mentorId),
GetPatch(userName),
allowStale: false
);
}
private static IndexQuery GetQuery(string mentorId)
{
return new IndexQuery {Query = "Mentor_Id:" + mentorId};
}
private static PatchRequest[] GetPatch(string userName)
{
return new[]
{
new PatchRequest
{
Type = PatchCommandType.Modify,
Name = "Mentor",
Nested = new[]
{
new PatchRequest
{
Type = PatchCommandType.Set,
Name = "UserName",
Value = userName
},
}
}
};
}
在打電話之前,UpdateByIndex,把一些像這樣的代碼'db.Query(INDEXNAME).Customize(X => x.WaitForNonStaleResultsAsOfNow())。ToList() –
2012-04-24 20:42:30
這將接踵而至,該指數是之前未失效您嘗試使用它進行修補。 – 2012-04-24 20:44:06
@MattWarren請參閱編輯3 – biofractal 2012-04-25 08:54:29