Session: What is a Session and How Does it Work



Unit of Work Pattern

Tracking Changes

  • Using the Session, perform needed operations on your documents.
    e.g. store a new document, modify an existing document, query your database, etc.
    Any such operation 'loads' the documents to the Session for tracking.
  • The Session tracks all changes made to all entities that it has either loaded or stored.
    You don't need to manually track the changes to these entities and decide what needs to be saved and what doesn't.
    The Session will do it for you.
  • All these tracked changes are combined & persisted to the database only when calling SaveChanges().
  • Entity tracking can be disabled if needed. See:

Batching

  • Remote calls to a server over the network are among the most expensive operations an application makes.
    The session optimizes this by batching all write operations it has tracked into the single SaveChanges() call.
  • The SaveChanges() call checks the Session state for all changes made to the tracked entities.
    These changes are sent to the server as a single remote call that will complete transactionally.
    In other words, either all changes are saved as a Single Atomic Transaction or none of them are.
    Once SaveChanges() returns, it is guaranteed that the changes are persisted to the database.
  • The SaveChanges() is the only time when a RavenDB client sends updates to the server,
    so you will experience a reduced number of network calls.

Store New Document Example

  • The Client API, and the Session in particular, is designed to be as straightforward as possible.
    Open the session, do some operations, and apply the changes to the RavenDB server.
  • The example below shows how to store a new document in the database using the Session.

// Obtain a Session from your Document Store.
using (IDocumentSession session = store.OpenSession())
{
    // Create a new entity
    Company entity = new Company { Name = "Company" };
    // Mark the entity for storage in the Session.
    session.Store(entity);
    // Any changes made to the entity will now be tracked by the Session.
    // However, the changes are persisted to the server only when 'SaveChanges()' is called.
    session.SaveChanges();
    // At this point the entity is persisted to the database as a new document.
    // Since no database was specified when opening the Session, the Default Database is used.
}

Load & Edit Document Example

  • The example below loads & edits an existing document and then saves the changes.

// Open a session.
using (IDocumentSession session = store.OpenSession())
{
    // Load an existing document to the Session using its Id.
    Company entity = session.Load<Company>(companyId);
    // Edit the entity. The Session will track this change.
    entity.Name = "Another Company";

    session.SaveChanges();
    // At this point, the change made is persisted in the database.
}

Identity Map Pattern

  • The session implements the Identity Map Pattern.

  • The first document Load() call goes to the server and fetches the document from the database.
    The document is then saved as an entity in the Session's identity map.

  • All subsequent Load() calls to the same document will simply retrieve the entity from the Session -
    no additional calls to the server are made.

//When a document is loaded for a second time, it is retrieved from the identity map.
Assert.Same(session.Load<Company>(companyId), session.Load<Company>(companyId));
//This command will not throw an exception.
  • Note: To override this and update an entity with the latest changes from the server see: Refresh an Entity

Reducing Server Calls (Best Practices) For:

The Select N+1 Problem

  • The Select N+1 problem is common with all ORMs and ORM-like APIs.
    It results in an excessive number of remote calls to the server, which makes a query very expensive.
  • Make use of RavenDB's include() method to include related documents and avoid this issue.
    See: Document Relationships

Large query results

  • When query results are large and you don't want the overhead of keeping all results in memory, you can Stream the Query Results.
    A single server call is executed and the client can handle the results one by one.
  • Note: Paging also avoids getting all query results in one time, but multiple server calls are generated - one per page retrieved.

Retrieving results on demand (Lazy)

  • Query calls to the server can be delayed and executed on-demand as needed using Lazily()
  • See Perform Queries Lazily