Native Queries (under the hood)
[
bamboo
]
11:14, Thursday, 17 November 2005
We are having a
discussion about
NativeQueries on castleproject-devel and I decided to wrap up the discussion here
by describing some of the details and shared goodies of our implementation.
> Can I assume that you're using the new GetMethodIL (or something like that)?
We are using the Mono.Cecil API not the reflection API.
Simply because Cecil's version of GetMethodBody is so much better and it already worked on most
platforms we support. One of our first contributions was to get Cecil running on the Compact Framework.
After some conditional compilation fun (among other things) Cecil now works on mono, ms.net 1.1, ms.net 2.0, cf 1.0 and cf 2.0.
Pretty cool.
> This is just freaking cool. And the idea is awesome.
Yeah, we think so too! (:
> If you cache the
> execution plans, I suppose you can get a very good performance out of it.
>
Currently we are not even caching the execution plans and the performance it's pretty
close to the raw query execution performance one would get from the underlying query api.
But, yes, caching the execution plans (or transformed expression trees in our case) is
the next logical step.
> This is a whole different way of thinking about how to mess with the
> code...!
> I've only heard about Cecil until now, wasn't aware that it was operational
> on this level. Are there are docs yet, or is it read the code to find out?
>
The Cecil object model is pretty self documenting.
The code we are contributing to cecil is a little more involved but still easy
to follow if you stick to the public API.
It basically allows you to get high level control flow graphs out of method
definitions. These high level cfgs can also contain decompiled expression trees
(an expression tree is a structure pretty similar to a bound boo ast).
Here's a simple but complete example that uses Cecil and our contributed code (Cecil.FlowAnalysis) to
dump the body of a method:
import System
import Mono.Cecil
import Cecil.FlowAnalysis
import Cecil.FlowAnalysis.ActionFlow
import Cecil.FlowAnalysis.CodeStructure
class Address:
[property(State)]
_state as string
class Customer:
[property(Address)]
_address as Address
class Predicate:
def Match(c as Customer):
return c.Address.State == "SP"
def GetMatchMethod():
asm = AssemblyFactory.GetAssembly(typeof(Predicate).Module.FullyQualifiedName)
return asm.MainModule.Types["Predicate"].Methods.GetMethod("Match")[0]
cfg = FlowGraphFactory.CreateControlFlowGraph(GetMatchMethod())
afg = FlowGraphFactory.CreateActionFlowGraph(cfg)
for block in afg.Blocks:
expression as IExpression = null
if block isa IReturnActionBlock:
expression = (block as IReturnActionBlock).Expression
Console.Write("return ")
elif block isa IAssignActionBlock:
expression = (block as IAssignActionBlock).AssignExpression
continue if expression is null
print ExpressionPrinter.ToString(expression)
The code above will print:
local0 = string.op_Equality(c.get_Address().get_State(), "SP")
return local0
Going from that to a high performant query native to the underlying engine is a fun ride!
TrackBack