System.Runtime Schema: Resolving references using 'binding context'
In my previous post “Assembly, Type and Method References”, I talked about how AssemblyReferences, TypeReferences and MethodReferences entities can be used to find references across assemblies, types and methods.
Context
Binding Context is a unique concept in SSMoS System.Runtime model and provides a resolution for the types and methods that are referenced between the assemblies AND loaded into the repository. This allows you to navigate the references between assemblies faster than having to use a name-based approach for finding references.
Let me walk you through a scenario:
· FooAssembly.dll depends on BarAssembly.dll, because a type Foo in FooAssembly.dll references a type Bar in BarAssembly.dll
· Both FooAssembly.dll and BarAssembly.dll have been loaded into the repository
How would you write this query – “Show all dependent assemblies for FooAssembly.dll that are also loaded into the repository”? If you just wanted to find out the references, you can easily obtain that by querying the AssemblyReferences entity, but having a reference in that table doesn’t necessarily mean the metadata for that assembly is also available into the repository. To try with some of our internal assemblies, I loaded Microsoft.M.LanguageServices.dll into the repository.
The following T-SQL queries for all assembly references for Microsoft.M.LanguageServices that begin with “Microsoft.*”:
Select AR.* from [Repository].[System_Runtime].[AssemblyReferences] AS AR
INNER JOIN [Repository].[System_Runtime].[Assemblies] AS A ON A.Id = AR.Assembly
WHERE A.Name = 'Microsoft.M.LanguageServices' AND AR.Name LIKE 'Microsoft.%'
The result set looks like this on my machine:
Folder |
Id |
Name |
VersionMajor |
VersionMinor |
VersionRevision |
VersionBuild |
Culture |
PublicKeyToken |
Assembly |
Hash_Name |
117 |
16 |
Microsoft.Intellipad.Core |
1 |
0 |
0 |
0 |
0x31BF3856AD364E35 |
2 |
366780607 |
|
117 |
18 |
Microsoft.M |
1 |
0 |
0 |
0 |
0x31BF3856AD364E35 |
2 |
2078358469 |
|
117 |
19 |
Microsoft.Intellipad.Editor |
1 |
0 |
0 |
0 |
0x31BF3856AD364E35 |
2 |
-1383118891 |
|
117 |
22 |
Microsoft.Oslo.Internal |
1 |
0 |
0 |
0 |
0x31BF3856AD364E35 |
2 |
791105609 |
|
117 |
25 |
Microsoft.Build.Engine |
4 |
0 |
0 |
0 |
0xB03F5F7F11D50A3A |
2 |
1222306620 |
|
117 |
27 |
Microsoft.Intellipad.Framework |
1 |
0 |
0 |
0 |
0x31BF3856AD364E35 |
2 |
988212485 |
|
117 |
33 |
Microsoft.Build.Framework |
4 |
0 |
0 |
0 |
0xB03F5F7F11D50A3A |
2 |
899350863 |
But what this query doesn’t tell me yet is whether or not the dependent assemblies are also loaded into the repository. To find that I need to look into Assemblies table for existence of Microsoft.M.dll assembly that matches the identity of assembly being referenced (i.e. Name + Version + PublicKeyToken + Culture). Here is the revised query (this only matches based on the assembly name for now).
Select AR.* from [Repository].[System_Runtime].[AssemblyReferences] AS AR
INNER JOIN [Repository].[System_Runtime].[Assemblies] AS A ON A.Id = AR.Assembly
INNER JOIN [Repository].[System_Runtime].[Assemblies] AS A2 ON ( A2.Name = AR.Name )
WHERE A.Name = 'Microsoft.M.LanguageServices' AND AR.Name LIKE 'Microsoft.%'
The result set of this query is only one row, because I only loaded Microsoft.M.dll into the database.
Folder |
Id |
Name |
VersionMajor |
VersionMinor |
VersionRevision |
VersionBuild |
Culture |
PublicKeyToken |
Assembly |
Hash_Name |
117 |
18 |
Microsoft.M |
1 |
0 |
0 |
0 |
0x31BF3856AD364E35 |
2 |
2078358469 |
The Binding Context concept lets the system do the assembly name-matching for you and populate a bunch of tables which you can directly query to find answers. The above query can be re-written like this using the BindingContext and yields the same result.
SELECT AR.* FROM [Repository].[System_Runtime].[AssemblyReferences] AS AR
INNER JOIN [Repository].[System_Runtime].[Assemblies] AS A ON A.Id = AR.Assembly
WHERE AR.Id IN
(SELECT BAR.SourceAssemblyReference FROM [Repository].[System_Runtime].[BoundAssemblyReferences] AS BAR)
Did you notice that we didn’t have to specify the name matching in above query? The system takes care of that.
Binding Context Overview
Binding Context makes it easier to write dependency queries to resolve type/method references to the type/method definition in the repository.
The following diagram shows the high level relationship between core entities related to the Binding Context.
n BindingContexts
o Holds individual binding context for a group of assemblies that make a particular application. For example, you may want to create separate binding contexts for each individual tier in your application. 3rd party libraries may themselves be loaded into a binding context of their own.
n BindingContextAssemblies
o Holds links to each assemblies that are part of the binding context
n BoundAssemblyReferences
o Provides resolution between assembly references and assemblies
o System populates this table when assemblies are inserted into BindingContextAssemblies table
n BoundTypeReferences
o Provides resolution between type references (TypeReferences) and type definitions (TypeDefinitions)
o System populates this table when assemblies are inserted into BindingContextAssemblies table
n BoundMethodReferences
o Provides resolution between method references (MethodReferences) and method definitions (MethodDefinitions)
o System populates this table when assemblies are inserted into BindingContextAssemblies table
Before the binding context can be used, you need to:
# |
Description |
T-SQL |
1 |
Create a new binding context |
-- 117 = Folder ID INSERT INTO [Repository].[System_Runtime].BindingContexts (Folder, Name) VALUES (117, 'Test') |
2 |
Insert assemblies into the binding context |
-- 3 = Binding Context ID, 1 = Assembly ID INSERT INTO [Repository].[System_Runtime].BindingContextAssemblies (Folder, Context, Assembly) VALUES (117, 3, 2) |
The insert of assemblies into the binding context invokes a trigger that resolves type and method references between assemblies and populate BoundAssemblyReferences, BoundTypeReferences and BoundMethodReferences entities.
Once the assemblies are loaded into the binding context, you can perform queries like these.
For following queries, I have already oaded m.exe, Microsoft.M.dll and Microsoft.Oslo.Internal.dll into the repository and into a binding context 3.
Show all methods that are bound to and being referenced in m.exe assembly
SELECT A.Name AS SourceAssembly, A2.Name AS ReferencedAssembly, M2.Name AS ReferencedMethod
FROM [Repository].System_Runtime.BoundMethodReferences AS BMR
INNER JOIN [Repository].System_Runtime.Methods AS M ON M.Id = BMR.SourceMethodReference
INNER JOIN [Repository].System_Runtime.TypeReferences AS TR ON TR.Id = M.DeclaringType
INNER JOIN [Repository].System_Runtime.AssemblyReferences AS AR ON TR.AssemblyReference = AR.Id
INNER JOIN [Repository].System_Runtime.Assemblies AS A ON AR.Assembly = A.Id
INNER JOIN [Repository].System_Runtime.Methods AS M2 ON M2.Id = BMR.TargetMethodDefinition
INNER JOIN [Repository].System_Runtime.Types AS T ON M2.DeclaringType = T.Id
INNER JOIN [Repository].System_Runtime.Modules AS MO ON T.Module = MO.Id
INNER JOIN [Repository].System_Runtime.Assemblies AS A2 ON MO.Assembly = A2.Id
WHERE BMR.Context = 3 AND A.Name = 'm'
Here is partial result for abovementioned query.
SourceAssembly |
ReferencedAssembly |
ReferencedMethod |
m |
Microsoft.Oslo.Internal |
.ctor |
m |
Microsoft.Oslo.Internal |
ParseForConsoleApplication |
m |
Microsoft.Oslo.Internal |
AddParameterHelp |
m |
Microsoft.M |
.ctor |
m |
Microsoft.M |
ProcessInputLists |
You can extend above query to see all methods in assembly m.exe that are calling methods defined in other assemblies loaded into the database.
SELECT A.Name AS SourceAssembly, M3.Name AS SourceMethod, A2.Name AS ReferencedAssembly, M2.Name AS ReferencedMethod
FROM [Repository].System_Runtime.BoundMethodReferences AS BMR
INNER JOIN [Repository].System_Runtime.Methods AS M ON M.Id = BMR.SourceMethodReference
INNER JOIN [Repository].System_Runtime.TypeReferences AS TR ON TR.Id = M.DeclaringType
INNER JOIN [Repository].System_Runtime.AssemblyReferences AS AR ON TR.AssemblyReference = AR.Id
INNER JOIN [Repository].System_Runtime.Assemblies AS A ON AR.Assembly = A.Id
INNER JOIN [Repository].System_Runtime.Methods AS M2 ON M2.Id = BMR.TargetMethodDefinition
INNER JOIN [Repository].System_Runtime.Types AS T ON M2.DeclaringType = T.Id
INNER JOIN [Repository].System_Runtime.Modules AS MO ON T.Module = MO.Id
INNER JOIN [Repository].System_Runtime.Assemblies AS A2 ON MO.Assembly = A2.Id
INNER JOIN [Repository].System_Runtime.CalledMethods AS CM ON CM.Callee = BMR.SourceMethodReference
INNER JOIN [Repository].System_Runtime.Methods AS M3 ON M3.Id = CM.Caller
WHERE BMR.Context = 3 AND A.Name = 'm'
SourceAssembly |
SourceMethod |
ReferencedAssembly |
ReferencedMethod |
m |
<ProcessCommandLineArguments>b__0 |
Microsoft.Oslo.Internal |
.ctor |
m |
ProcessCommandLineArguments |
Microsoft.Oslo.Internal |
ParseForConsoleApplication |
m |
<ProcessCommandLineArguments>b__0 |
Microsoft.Oslo.Internal |
AddParameterHelp |
m |
BuildCompilerOptions |
Microsoft.M |
.ctor |
m |
ProcessCommandLineArguments |
Microsoft.M |
ProcessInputLists |
Since this post has already gotten too long, I will write more binding context related queries in a future post. Here is one query that I have been planning on writing and I think the binding context can be useful there.
“Find all methods in your application that are not being called, across all the assemblies in the database (i.e. find all dead methods)”
Summary
Binding Context entities in the System.Runtime schema provide a quicker way to go from a reference to its definition in the repository. What do you think of the Binding Context set of entities in the System.Runtime schema? Do you think they can be useful for your query scenarios? Does the name binding context resonate? We appreciate your feedback!