我已经实现了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