RavenDb :Обновление значения свойства денормализованной ссылки

Я реализовал шаблон 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 содержит идентификатор и имя пользователя указанного пользователя. Итак, теперь, если я обновлю имя пользователя для данного экземпляра пользователя, я хочу, чтобы указанное значение имени пользователя во всех ссылках пользователя также обновлялось. Для этого я написал статический индекс и запрос на исправление следующим образом:

public class Relationships_ByMentorId : AbstractIndexCreationTask
{
    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.Id);

        relationship.ShouldNotBe(null);
        relationship.Mentor.ShouldNotBe(null);
        relationship.Mentor.Id.ShouldBe(mentor.Id);
        relationship.Mentor.UserName.ShouldBe(userName);

        mentor = db.Load(mentor.Id);
        mentor.ShouldNotBe(null);
        mentor.UserName.ShouldBe(userName);
    }
}

Все работает нормально, индекс есть, но я подозреваю, что он не возвращает отношения, требуемые запросом на исправление, но, честно говоря, у меня закончился талант. Можете ли вы помочь, пожалуйста?

Редактировать 1

@MattWarren allowStale=trueне помогло. Однако я заметил потенциальную подсказку.

Поскольку это модульный тест, я использую InMemory, встроенный IDocumentSession -Fake.Db()в приведенном выше коде. Тем не менее, когда вызывается статический индекс, то есть при выполнении UpdateByIndex(...), он использует общий IDocumentStore, а не конкретный поддельный IDocumentSession.

Когда я изменяю свой класс определения индекса, а затем запускаю свой модульный -тест, индекс обновляется в «реальной» базе данных, и изменения можно увидеть через Raven Studio. Однако поддельные экземпляры домена (mentor, menteeи т. д. ), которые «сохраняются» в базе данных InMemory, не хранятся в фактической базе данных (, как ожидалось ), и поэтому их нельзя увидеть через Raven Studio.

Могло ли быть так, что мой вызов UpdateByIndex(...)выполняется против неправильного IDocumentSession, «настоящего» (без сохраненных экземпляров домена ), а не поддельного?

Редактировать 2-@Simon

Я реализовал ваше решение проблемы, описанной в Редактировании 1 выше, и я думаю, что мы добились прогресса. Вы были правы, я использовал статическую ссылку на IDocumentStoreчерез RavenSessionProvder. Сейчас это не так. Приведенный ниже код был обновлен для использования вместо него 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 не станет пустой и индекс не будет считаться «свежим»?

Редактировать 3

Принимая во внимание предложение по предотвращению устаревания индекса, я обновил код следующим образом:

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

Из пронумерованных комментариев выше:

  1. Добавление фиктивного запроса, чтобы заставить индекс ждать, пока он не не -устаревшие работы. Устранена ошибка относительно устаревшего индекса.

  2. Это тестовая строка, чтобы убедиться, что мой индекс работает правильно. Кажется, все в порядке. Возвращаемый результат является правильным экземпляром отношения для предоставленного Mentor.Id ('users -1' ).

    { "Наставник" :{ "Идентификатор" :"пользователи -1", "Имя пользователя" :"Мистер Ментор" }, "Подопечный" :{ "Идентификатор" :"пользователи -2", "Имя пользователя" :"Мистер Подопечный" }... }

  3. Несмотря на то, что индекс -не устарел и, по-видимому, работает правильно, фактический запрос на исправление, по-видимому, ничего не делает. Имя пользователя в денормализованной ссылке для наставника остается неизменным.

Таким образом, теперь подозрение падает на сам запрос на исправление. Почему это не работает?Может быть, я устанавливаю значение свойства UserName для обновления?

...
new PatchRequest
{
        Type = PatchCommandType.Set,
        Name = "UserName",
        Value = userName
}
...

Обратите внимание, что я просто присваиваю строковое значение параметра userNameнепосредственно свойству 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(relationshipId);

        relationship.ShouldNotBe(null);
        relationship.Mentor.ShouldNotBe(null);
        relationship.Mentor.Id.ShouldBe(mentorId);
        relationship.Mentor.UserName.ShouldBe(userName);

        var mentor = db.Load(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", "mentor@email.com", "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", "mentee@email.com", "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(mentorId), db.Load(menteeId));
    db.Store(relationship);
    db.SaveChanges();
    return relationship;
}

Raven Session Provider для модульных тестов

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
    {
        public Relationships_ByMentorId()
        {
            Map = relationships => from relationship in relationships
                                    select new { Mentor_Id = relationship.Mentor.Id };
        }
    }

    public class AllDocuments : AbstractIndexCreationTask
    {
        public AllDocuments()
        {
            Map = documents => documents.Select(entity => new {});
        }
    }
}

Обновление денормализованной ссылки

public static void SetUserName(IDocumentSession db, string mentorId, string userName)
{
    var mentor = db.Load(mentorId);
    mentor.UserName = userName;
    db.Store(mentor);
    db.SaveChanges();

    //Don't want this is production code
    db.Query(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
                                            },
                                    }
                    }
            };
}

5
задан biofractal 25 April 2012 в 15:11
поделиться