Ayende @ Rahien

Oren Eini aka Ayende Rahien CEO of Hibernating Rhinos LTD, which develops RavenDB, a NoSQL Open Source Document Database.

Get in touch with me:

oren@ravendb.net

+972 52-548-6969

Posts: 7,211 | Comments: 50,323

Privacy Policy Terms
filter by tags archive
time to read 2 min | 246 words

Okay, my dislike for internal is well known at this stage, I believe. What I want to talk about today is the process of stripping (ignoring) the internal keyword. As I have recently discussed, I have recently run into several situations where certain parts of the framework, marked as internal, would have been so very useful to me.

A while ago I stripped internal out of SqlCommandSet, and I have used the same technique since. It is a hack, mind you, but a very useful one.

Now, we can make use of internals in the framework if we use Reflection, but that has a perf cost to it. I found that by caching the results of the Reflection, and a bit of delegates, we can fairly easily expose the required functionality out, without paying a high price for performance. SqlCommandSet is a good example of this technique in action, and it has been wildly successful.

Another, a more advance version, but one that I don't know the ROI for, is to use Lightweight Code Generation to add accessors to the internal methods already there. I am pretty sure it is not worth the trouble.

Of course, if it is internal, you have to make sure that there are tests around it, because otherwise it could change without warning and you'll not know. There are other issues with the method, mostly around support, robustness (the internal code may make assumptions about its use), etc.

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.

IL Wierdness

time to read 2 min | 251 words

I am in deep dive mode into Dynamic Proxy 2, and I am once again confronted with IL Madness. I can understand IL well enough to say that this piece of could is a very long way to say: "Console.WriteLine('a');". The problem is that it doesn't do that.

    IL_0000:  nop
    IL_0001:  ldc.i4.s   97
    IL_0003:  box        [mscorlib]System.Char
    IL_0008:  stloc.0
    IL_0009:  ldloc.0
    IL_000a:  unbox   [mscorlib]System.Char
    IL_000f:  stloc.1
    IL_0010:  ldloc.1
    IL_0011:  call       void [mscorlib]System.Console::WriteLine(char)
    IL_0016:  nop
    IL_0017:  ret

Instead, it just prints a random character. The issue is with IL_000a, where the unbox reside. The C# Compiler output an unbox.any in this scenario, and then it works. Pursuing the documenation for those instructions doesn't reveal much. unbox.any is basically an unbox followed with ldobj in this scenario, and unbox short circuit itself to just copy the address of the boxed value.

What this looks to me is that I'm actually storing the reference to the char variable, but I have no idea why. Any explanation is welcome.

FUTURE POSTS

  1. When the error is byzantine - 10 hours from now
  2. Negative feature response: Automatic attachment compression in RavenDB - about one day from now

There are posts all the way to Oct 20, 2021

RECENT SERIES

  1. A PKI-less secure communication channel (6):
    12 Oct 2021 - Using TLS
  2. Postmortem (3):
    27 Sep 2021 - Partial RavenDB Cloud outage
  3. Production postmortem (31):
    17 Sep 2021 - The Guinness record for page faults & high CPU
  4. RavenDB 5.2 (2):
    06 Aug 2021 - Simplifying atomic cluster wide transactions
  5. re (28):
    23 Jun 2021 - The performance regression odyssey
View all series

RECENT COMMENTS

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats