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,546
|
Comments: 51,163
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. Partial writes, IO_Uring and safety - one day from now
  2. Configuration values & Escape hatches - 4 days from now
  3. What happens when a sparse file allocation fails? - 6 days from now
  4. NTFS has an emergency stash of disk space - 8 days from now
  5. Challenge: Giving file system developer ulcer - 11 days from now

And 4 more posts are pending...

There are posts all the way to Feb 17, 2025

RECENT SERIES

  1. Challenge (77):
    20 Jan 2025 - What does this code do?
  2. Answer (13):
    22 Jan 2025 - What does this code do?
  3. Production post-mortem (2):
    17 Jan 2025 - Inspecting ourselves to death
  4. Performance discovery (2):
    10 Jan 2025 - IOPS vs. IOPS
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats
}