2012-05-08 18 views
8

Mi piacerebbe avere un riferimento tra due entità memorizzate nel database di documenti RavenDB. Dal momento che questo non è un db relazionale, so che dovrei usare la tecnica Denormalized Reference descritta sulla documentazione di RavenDBs. Anche se all'inizio questo sembra andare bene, quando comincio a creare una "gerarchia" di domini del mondo reale compresi i riferimenti bidirezionali, lo sforzo di mantenere aggiornati tutti questi riferimenti sembra sproporzionato. Sento che potrei sbagliare da qualche parte.Come implementare riferimenti denormalizzati in RavenDB

Puoi spiegare il modo migliore/più semplice per modellare una gerarchia di domini ragionevolmente complessa usando RavenDB?

Grazie

+0

Questo post può aiutare a decidere: http://daniellang.net/how-to-handle-relations-in-ravendb/ – dasheddot

+0

Grazie dasheddot, è davvero utile, grazie. –

+0

Questo sembra interessante. Multi Maps/Riduci gli indici [qui] (http: // ayende.com/blog/89089/ravendb-multi-maps-reduce-indexes) – biofractal

risposta

6

Non sono sicuro se questo va abbastanza lontano per rispondere alla tua domanda, ma ecco come posso fare per creare un riferimento denormalizzato in RavenDB (questo è preso dal codice vero e proprio con i non-essenziali rimosse per chiarezza)

Domain

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

Si può vedere che ho una classe Assessment che fa riferimento a un User. Questo riferimento utente è gestito utilizzando la classe UserReference di seguito.

denormalizzato Riferimento

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

nota come la classe di riferimento porta anche il UserName. Questo valore non cambierà molto spesso ma potrebbe cambiare, pertanto è necessario un modo per aggiornare la proprietà UserName nella proprietà UserReference detenuta nella classe Assessment. Per effettuare la modifica dobbiamo prima trovare le istanze corrette Assessment da RavenDB e per questo abbiamo bisogno di un indice.

Raven Indice

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

Questo indice deve essere invocato ogni volta che UserName valore s' un User viene aggiornato. Ho una classe UserService che mi aiuta a coordinare tutte le mie funzioni relative all'utente, quindi è qui che inserisco questo codice.

Riutilizzo questo codice per altri riferimenti, quindi è stato un po 'astratto. Questo può aiutarti a creare le gerarchie più complesse (o forse "il grafico del dominio" è una descrizione migliore) che desideri.

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

che è. E sai, ora che metto tutto fuori posso vedere cosa intendi. È è un sacco di lavoro solo per aggiornare un riferimento. Forse il codice di servizio può essere reso più DRY e riutilizzato per diversi tipi di relazione, ma non vedo come uscire dalla scrittura di molti indici, uno per tipo di riferimento.

+0

Grande, grazie biofractal, stavo cercando un percorso molto più complesso, questo sembra ideale. –