Oren Eini

CEO of RavenDB

a NoSQL Open Source Document Database

Get in touch with me:

oren@ravendb.net +972 52-548-6969

Posts: 7,593
|
Comments: 51,224
Privacy Policy · Terms
filter by tags archive
time to read 5 min | 853 words

David Hayden is talking about AOP, and he mentioned Caching declaratively, which sturk a chord, and I had to write a post about it. I want to talk about a completely different aspect of declerative coding, how far you should go with it. Just to be clear, this is fake code, and it has little to do with David's post other than using the scenario to make a point.

Let us take the code example that David has:

public class NewsController { private INewsService _service; public NewsController(INewsService service) { _service = service; } [CachingCallHandler] public NewsHeadlineCollection GetLatestNewsHeadlines() { return _service.GetLatestNews(); } }

 

Now, assume that you want to specify an expiration time? No problem, just put it in the attribute:

[CachingCallHandler(Expiration="10:00")]
public NewsHeadlineCollection GetLatestNewsHeadlines()
{
       
return _service.GetLatestNews();
}

What if I want to add a bit of smarts to the caching and only cache costly calls?

[CachingCallHandler(Expiration="0:15", CacheIfCallOver="0:30")]
public NewsHeadlineCollection GetLatestNewsHeadlines()
{
       
return _service.GetLatestNews();
}

Now I want to handle dependencies:

[CachingCallHandler(Expiration="10:00", CacheIfCallOver="0:30", DependencyConnectionStringName="MyDatabase"
                         DependencyStatement="SELECT * FROM HeadLines Where datePublished > getdate()-1"
)
]
public NewsHeadlineCollection GetLatestNewsHeadlines()
{
       
return _service.GetLatestNews();
}

But wait, I have a bit of business logic here as well, if there is a developing story, I want to skip caching altogether:

[CachingCallHandler(Expiration="10:00", CacheIfCallOver="0:30", DependencyConnectionStringName="MyDatabase"
                         DependencyStatement="SELECT * FROM HeadLines Where datePublished > getdate()-1"
)
]
public NewsHeadlineCollection GetLatestNewsHeadlines()
{
        if( _service.BigStoryNowDeveloping)
              CachingCallHandler.Current.SkipCaching();

       
return _service.GetLatestNews();
}

And now I have a different case, I want to call the GetLatestNewsHeadlines() and get the uncached results, maybe I am building a ticker, and it really need to be up to date. Now I need:

CachingCallHandler.DisableCachingForNextMethodOn(newsController);
return newsController.GetLatestNews();

This is a fake scenario, obviously, but just note how each additional business requirements make declerative more complex. I am not showing the code for the CachingCallHanlder, but you can be that somewhere its developer would be groaning for each such new requirement.

For myself, the minute that I put the connection string name hard coded SQL, that went way over the line. Even if I could skip it and use just the table name directly, that would be too much. And what happens if I need expiration from sources that do not support cache dependecies natively?

In short, I really love declerative coding, and I tend to overuse it at times, but it is really important to understand that you probably shouldn't try declerative coding for everything. If the scenario is simple and straightforward, that is a good candidate for ocde reduction and simplification. If it isn't, imperative coding (you know, if and else, as well as a while or two) are going to be more intent revealing, and would be easier to maintain.

time to read 6 min | 1121 words


Okay, this is a fairly wacky scenario, I admit.
I am using IRepository<T> for my data access, and I wanted to be able to specify attributes on the T (that is, on the entity, not the repository) and have the behavior of the class change. For instance, if I have IRepository<OrderStatus>, that is something that changes very rarely, so I can safely cache that. But I want to put [Cacheable] on OrderStatus, not on OrderStatusRepository.  To make matter worse, I never explicitly register IRepository<OrderStatus>, I only register IRepository<T> and let Windsor figure the rest of it out.
I thought that it would be a major hurdle, but it turn out to be fairly easy.

Windsor has the concept of Component Model, basically everything that Windsor knows about a component, and you can plug your own contributers, which can add additional knowledge to Windsor.

/// <summary>
///
Inspect the model and add a caching interceptor if appropriate
/// </summary>
public class CachingInterceptorContributer : IContributeComponentModelConstruction
{
    /// <summary>
    /// Inspect the model and add a caching interceptor if appropriate
    /// </summary>
    public void ProcessModel(IKernel kernel, ComponentModel model)
    {
        bool isRepository = model.Service.IsGenericType &&
            model.Service.GetGenericTypeDefinition() == typeof(IRepository<>);
        if (isRepository == false)
            return;
        Type entityType = model.Service.GetGenericArguments()[0];
        bool cacheable = entityType
              .GetCustomAttributes(typeof(CacheableAttribute),true).Length != 0;

        if(cacheable==false)
            return;
        model.Interceptors.Add(new InterceptorReference(typeof(CachingInterceptor)));
    }

}

This checks that the component is an IRepository<T>, and that the T has [Cacheable] attribute. The real nice thing here is that it will be called when Windsor decides that it needs to create an IRepository<OrderStatus>, thereby giving me the chance to add the interceptor to this (and only this) repository.
The interceptor is very simple, and builds on  already existing code:

/// <summary>
///
Add query caching capabilities
/// </summary>
public class CachingInterceptor : IInterceptor, IOnBehalfAware
{
    private string typeName;
 

    /// <summary>
    /// Intercepts the specified invocation and adds query caching capabilities
    /// </summary>
    /// <param name="invocation">The invocation.</param>
    public void Intercept(IInvocation invocation)
    {
        using(With.QueryCache(typeName))
        {
            invocation.Proceed();
        }
    }
 

    /// <summary>
    /// Sets the intercepted component model.
    /// </summary>
    /// <param name="target">The target.</param>
    public void SetInterceptedComponentModel(ComponentModel target)
    {
        typeName = target.Service.GetGenericArguments()[0].FullName;
    }

}


I am using this just to tell NHibernate that it should cache the query, since NHibernate would already be caching the entities in its second level cache (I might show later how I handled that). Now to register the contributer:


/// <summary>
///
Registers various add-ons to the container
/// </summary>
public class AdditionalFunctionalityFacility : AbstractFacility
{

      /// <summary>
      /// The actual addition of resolvers
      /// </summary>
      protected override void Init()
      {
        Kernel.AddComponent("caching.interceptor", typeof(CachingInterceptor));
        Kernel.ComponentModelBuilder.AddContributor(new CachingInterceptorContributer()); 
      }

}

And that is it. I register a facility, which takes care of configuring the interceptor and the contributer, and I am set. I can now simply put [Cacheable] on an entity, and it will cache that in all levels.
This technique is a powerful one, and I do believe that I will use it more in the future.
time to read 1 min | 198 words

I am searching for a tool to help with code reviews. This is something that is done after the fact, not before commits, by the way. Specifically, what I am thinking about is a tool that let me browse checkins into the source control, select one for review, add comments to it, etc.

I wrote the above sentence, and then went to Trac, and took a look at the Timeline view. It seems to be that this may be a way to do it, while handling the code reviews as tickets in Trac as well.

At any rate, I would like to be able to get a tool that says what has changed and needs reviews, or able to give me a diff over more than a single commit. I just thought of a new thing that I would like, something along the line of getting all the changes in this directory from last time that I reviewed it. I can get a diff from subversion, but attempting to understand something with just a diff is non trivial.

Any recommendatiosn about tools for doing this?

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. RavenDB 7.1 (7):
    11 Jul 2025 - The Gen AI release
  2. Production postmorterm (2):
    11 Jun 2025 - The rookie server's untimely promotion
  3. Webinar (7):
    05 Jun 2025 - Think inside the database
  4. Recording (16):
    29 May 2025 - RavenDB's Upcoming Optimizations Deep Dive
  5. RavenDB News (2):
    02 May 2025 - May 2025
View all series

Syndication

Main feed ... ...
Comments feed   ... ...
}