2012-04-24 14 views
6

अद्यतन करें मैंने RavenDB Denormalized Reference पैटर्न लागू किया है। मैं स्थिर सूचकांक और पैच अपडेट अनुरोध को एक साथ तार करने के लिए संघर्ष कर रहा हूं ताकि यह सुनिश्चित किया जा सके कि मेरे संदर्भित संदर्भ गुण मान अपडेट किए जाते हैं जब एक संदर्भित उदाहरण मान बदल जाता है।रावेनडीबी: एक डेमोर्मलाइज्ड संदर्भ संपत्ति मान

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

और अंत में एक unittest कि विफल रहता है क्योंकि अद्यतन अपेक्षा के अनुरूप काम नहीं कर रहा।

[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 मदद नहीं की। हालांकि मैंने एक संभावित सुराग देखा है।

क्योंकि यह एक यूनिट परीक्षण है, मैं उपरोक्त कोड में एक इनमेमरी, एम्बेडेड IDocumentSession - Fake.Db() का उपयोग कर रहा हूं। फिर भी जब एक स्थिर सूचकांक लागू किया जाता है यानी UpdateByIndex(...) करते समय, यह सामान्य IDocumentStore का उपयोग करता है, विशिष्ट नकली IDocumentSession नहीं।

जब मैं अपनी अनुक्रमणिका परिभाषा कक्षा बदलता हूं और फिर अपना यूनिट-टेस्ट चलाता हूं तो इंडेक्स को 'असली' डेटाबेस में अपडेट किया जाता है और परिवर्तन रावेन स्टूडियो के माध्यम से देखा जा सकता है। हालांकि, नकली डोमेन उदाहरण (mentor, mentee आदि) जो इनमेमरी डीबी में 'सहेजे गए' हैं, वास्तविक डेटाबेस (अपेक्षित के रूप में) में संग्रहीत नहीं हैं और इसलिए रावेन स्टूडियो के माध्यम से नहीं देखा जा सकता है।

क्या यह हो सकता है कि UpdateByIndex(...) पर मेरा कॉल गलत आईडी दस्तावेज़ सत्र, 'असली' (नकली डोमेन उदाहरणों के साथ) नकली के बजाय चल रहा है?

संपादित 2 - @Simon

मैं संपादित करें 1 ऊपर में उल्लिखित समस्या के लिए अपना समाधान को लागू किया है और मुझे लगता है कि हम प्रगति कर रहे हैं। आप सही थे, मैं के 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(..) विधि का इंतजार कैसे कर सकता हूं कमांड-क्यू खाली है और सूचकांक को 'ताजा' माना जाता है?

  1. :

    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

    खाते में एक बासी सूचकांक को रोकने के लिए सुझाव ले रहा है, मैं कोड के रूप में इस अद्यतन किया है

    एक डमी क्वेरी में डालकर इंडेक्स को तब तक प्रतीक्षा करने के लिए मजबूर करने के लिए जब तक यह गैर-बालों वाले काम न हो। पुरानी अनुक्रमणिका से संबंधित त्रुटि समाप्त हो गई है।

  2. यह सुनिश्चित करने के लिए एक परीक्षण रेखा है कि मेरी अनुक्रमणिका सही तरीके से काम कर रही है। यह ठीक लग रहा है। परिणाम लौटाया गया है आपूर्ति किए गए Mentor.Id ('उपयोगकर्ता -1') के लिए सही रिलेशनशिप उदाहरण है।

    { "मेंटर": { "आईडी": "उन -1", "उपयोगकर्ता नाम": "श्री मेंटर" }, "Mentee": { "आईडी": "उन -2 ", " उपयोगकर्ता नाम ":" श्री Mentee " } ... }

  3. सूचकांक गैर बासी और उचित रूप में सही ढंग से कार्य होने के बावजूद, वास्तविक पैच अनुरोध उचित रूप में कुछ नहीं करता है। सलाहकार के लिए denormalized संदर्भ में उपयोगकर्ता नाम अपरिवर्तित बनी हुई है।

तो संदेह अब पैच अनुरोध पर पड़ता है। यह क्यों काम नहीं कर रहा है? क्या यह तरीका हो सकता है कि मैं UserName प्रॉपर्टी वैल्यू को अपडेट करने के लिए सेट कर रहा हूं?

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

आप देखेंगे कि मैं सिर्फ Value संपत्ति को userName परम सीधे, प्रकार 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; 
} 

यूनिट टेस्ट के लिए रेवेन सत्र प्रदाता

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

अद्यतन Denormalized संदर्भ

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 
              }, 
            } 
        } 
      }; 
} 
+0

इससे पहले कि आप UpdateByIndex, इस 'db.Query (indexName) .Customize (x => x.WaitForNonStaleResultsAsOfNow())। ToList (जैसे कुछ कोड में डाल) –

+0

फोन यह पीछा करेगा कि सूचकांक से पहले गैर-बासी है आप इसका उपयोग करके पैचिंग करने की कोशिश करते हैं और करते हैं। –

+0

@MattWarren कृपया देखें 3 – biofractal

उत्तर

4

अपनी लाइन को बदलने का प्रयास करें:

RavenSessionProvider.UpdateByIndex(indexName, //etc 

को

db.Advanced.DatabaseCommands.UpdateByIndex(indexName, //etc 

यह सुनिश्चित होगा कि अद्यतन आदेश एक ही (नकली) दस्तावेज़ की दुकान है कि आप अपने इकाई परीक्षण में उपयोग कर रहे हैं पर जारी किया जाता है।

उत्तर 2 संपादित करने के लिए:

जब UpdateByIndex का उपयोग कर गैर बासी परिणामों के लिए प्रतीक्षा करने का कोई स्वचालित मार्ग नहीं है। आप अपने SetUserName विधि में विकल्पों की एक जोड़ी है:

1 - अपने डेटासंग्रह बदलें हमेशा तुरंत इस तरह अनुक्रमित अद्यतन करने के लिए (नोट: इस पर प्रतिकूल प्रदर्शन को प्रभावित कर सकते हैं):

store.Conventions.DefaultQueryingConsistency = ConsistencyOptions.MonotonicRead; 

2 - के खिलाफ एक क्वेरी चलाएं अपनी अनुक्रमणिका, बस UpdateByIndex कॉल करने से पहले, WaitForNonStaleResults विकल्प को निर्दिष्ट:

var dummy = session.Query<Relationship>("Relationships_ByMentorId") 
.Customize(x => x.WaitForNonStaleResultsAsOfLastWrite()) 
.ToArray(); 

3 - अपवाद फेंक पकड़ो जब सूचकांक बासी, एक Thread.Sleep(100) करते हैं और पुनः प्रयास करें।

उत्तर 3 संपादित करने के लिए:

मैं अंत में यह समझ गए होंगे बाहर, और एक गुजर परीक्षण ... यह विश्वास नहीं कर सकता है, लेकिन यह सिर्फ एक कैशिंग मुद्दा रहा है लगता है। जब आप अपने दस्तावेज़ों को फिर से लोड करने के लिए पुनः लोड करते हैं, तो आपको एक अलग सत्र का उपयोग करने की आवश्यकता होती है ... उदा।

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

using (var db = Fake.Db()) 
{ 
    relationship = db 
     .Include("Mentor.Id") 
     .Load<Relationship>(relationship.Id); 
    //etc... 
} 

विश्वास नहीं कर सकता कि मैंने इसे जल्द ही नहीं बताया, क्षमा करें।

+1

लोड हमेशा अद्यतित है, यह इंडेक्स का उपयोग नहीं करता है। हालांकि 'WaitForNonStaleResults (..) 'के अंदर' MentorService.SetUserName (डीबी, सलाहकार, उपयोगकर्ता नाम) 'के अंदर' मदद कर सकता है, क्योंकि यह एक इंडेक्स का उपयोग करता है। –

+0

असल में मुझे नहीं लगता कि यह मुद्दा है, क्योंकि आप जिस इंडेक्स को अपडेट कर रहे हैं उससे डेटा लोड नहीं कर रहे हैं। अगर आप सहमत हैं, तो हटाने के लिए वोट दें और मैं इस जवाब को हटा दूंगा। – Simon

+0

@ मैटवार्रेन हाँ, बस देखा कि धन्यवाद मैट - मुझे लगता है कि आपके पास सही जवाब है। पैच को इंडेक्स में नए सहेजे गए उपयोगकर्ता नहीं मिलेगा, इसलिए पैच करने के लिए कुछ भी नहीं है। कम से कम यह मेरे लिए सही लगता है! – Simon

संबंधित मुद्दे