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!