Things to Remember When Using RavenDB
Brief introduction
RavenDB is a document-oriented database. It is understood that it is flexible, comfortable, fast and still has a lot of goodies in it ...
And this is so.

But with some reservations.

The most important thing
The first thing we encounter when searching for “project architecture with RavenDB” is the phrase - do not work with RavenDb as a relational database. However, this proposition is true for working with any NoSQL database. This phrase means that you need to denormalize data where necessary. RavenDB pushes us to the decision to store all the necessary information in one entity. Consider an example. Suppose we have the following entity.
public class Article
{
public long Id {get;set;}
public string Title {get;set;}
public string Content{get;set;}
}
And we make the assumption that our Article entity may have comments.
public class Comment
{
public long Id {get;set;}
public string Author {get;set;}
public string Content{get;set;}
}
All we need to do to correctly write an article with comments in the database is:
- Add Comments property to Article class
- Remove Id properties from comment.
In other words, we do not need to have a separate table for comments, because they are always inside an entity. This is what denormalization is all about.
I added the second point simply because in this model we do not need to refer to a separate comment. We may want to get all the comments on the article, or all the comments of some author, but we do not need a separate comment.
By the way, there are no tables in RavenDB. There are collections of entities, and the entity belonging to the collection is set very simply. But more about that below.
Metadata
It is worth noting that RavenDB stores entities in the form of JSON objects. As a result, they do not have a specific structure, with the exception of some service properties. The main utility property is @metadata. This object contains all the control data of our document: Id inside RavenDB, type on the server (our Article, for example), and many other properties.
The Raven-Entity-Name property is responsible for the entity belonging to the collection. If you change it, the collection in which the object is located will also change. Id does not automatically change.
By the way, identifiers in RavenDB by default map on the Id property, but you can make any field an identifier and define your own strategy for generating identifiers. Described in more detail here . You just have to scroll down a bit.
By the way, the important thing
Of course, a situation may arise when you need to determine whether one entity belongs to another, but if you have to do this constantly, ask yourself - are you using RavenDB correctly and do you need it on the project?
Entity Search
Consider the trivial task - we need to get a list of all posts.
List blogs = null;
using (var session = store.OpenSession())
{
blogs = session.Query().ToList();
}
What happens in this little piece of code?
First, we create a connection to RavenDB (this is trivial).
Secondly, the session gives us exactly 128 of the first entities that satisfy the condition. Why 128? Because this is the default behavior. In the config, you can increase this value to 1024, but, you see, this is not quite the behavior that is required.
This is because RavenDb strongly recommends using pagination to work with large amounts of data. And it would be cool if this behavior were already written in the API, but it is not! Instead, you have to write your bike for pagination every time. First we need to find out how many total pages there will be, and then pull out a specific one.
Yes, the task is trivial, but annoying.
By the way, here is the code (perhaps, from your point of view, not optimal) that simplifies the work with pagination.
public static int GetPageCount (this IRavenQueryable queryable, int pageSize)
{
if (pageSize < 1)
{
throw new ArgumentException("Page size is less then 1");
}
RavenQueryStatistics stats;
queryable.Statistics(out stats).Take(0).ToArray(); //Без перечисления статистика работать не будет.
var result = stats.TotalResults / pageSize;
if (stats.TotalResults % pageSize > 0) // Округляем вверх
{
result++;
}
return result;
}
public static IEnumerable GetPage(this IRavenQueryable queryable, int page, int pageSize)
{
return queryable
.Skip((page - 1)*pageSize)
.Take(pageSize)
.ToArray();
}
However, this is not all.
In the above example, RavenDB gives us entities sorted by last modified date . This is the Last-Modified property in the @metadata object that I mentioned earlier.
An interesting fact is that you can’t sort by Id. An error crashes, or nothing happens.
The solution is simple - create a Created field and sort by it.
Using RavenDB for Queries
It is worth remembering that the session is limited to 30 queries, after this limit expires, an exception occurs when you try to send a query to the database. Thus, the creators of this wonderful database in every way tell us that we should create a separate session for each request. In principle, this is justified, because the session is UnitOfWork and, as a result, is lightweight. But the constant creation of sessions can lead your code to an unreadable look, so you can do otherwise:
private IDocumentSession Session
{
get
{
if (_session == null)
{
_session = _store.OpenSession();
}
if (_session.Advanced.NumberOfRequests ==
_session.Advanced.MaxNumberOfRequestsPerSession)
{
_session.Dispose();
_session = _store.OpenSession();
}
return _session;
}
}
Using RavenDB in a project
The creator of the aforementioned database, Ayende Rahien, says: "Use RavenDB as high as possible." And gives an example access to the database directly from the controller. Perhaps for small projects this is justified. However, I prefer the good old three-link with unit testing, so this way is not for me.
My solution is to proxy over a RavenDB session that does what I need.
The main reason for creating this component is the difficulty with the session mok. If Load can still be dunked somehow, then Query is almost unrealistic. While the add-in is very simple.
And one more thing to say about tests with RavenDB. Maybe this happens that you need to check the work with a real database. In this case, use the EmbeddableStore.
One of the reasons for using a real database is index testing. But indexes in RavenDB is an extensive topic, which is worth writing a separate article. =)