Taoffi's blog

prisonniers du temps

Invoking methods by name. A first approach

As many developers know, one of the interesting namespaces in .NET is System.Reflection.

Here I try to use Reflection to invoke loaded assemblies’ methods by their names. This can be quite useful for applications like storing menu or button actions in a database and bringing them to life during runtime (I will post something about this subject shortly).

The attached test application demonstrates this in a very simple manner. Each dialog button invokes a method defined somewhere in the loaded assembly using its name. The search is done by few methods in MethodFinder static class.

 

The dialog displays 3 buttons. The OnClick handler for each button invokes a method by its name:

private void button1_Click(object sender, EventArgs e)

{

       invoke_method("Method1");

}

 

private void button2_Click(object sender, EventArgs e)

{

       invoke_method("Method2");

}

 

The invoke_method function, calls a method finder to retrieve the method and, if found, invokes it:

 

private void invoke_method(string method_name)

{

       MethodInfo          method       = MethodFinder.FindMethod(method_name);

       string              str_result   = "";

 

       if (method != null)

       {

             str_result = method.Invoke(this, null).ToString();

             MessageBox.Show(str_result);

       }

       else

             MessageBox.Show("Couldn't find method in loaded assemblies");

}

 

What you should know

You probably know that a .NET assembly is composed of modules (generally just one module), each module is composed of classes (Types) and each class is composed of fields, properties and methods.

The MethodFinder.FindMethod function loops through runtime loaded assemblies, modules and classes to search for the method and return the found method if any.

 

FindMethod function:

public static MethodInfo FindMethod( string method_name)

{

       foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())

       {

             method = FindAssemblyMethod( asm, method_name);

             if( method != null)

                    return method;

       }

       return null;

}

 

FindAssemblyMethod function loops through assembly’s classes:

public static MethodInfo FindAssemblyMethod(Assembly asm, string method_name)

{

       MethodInfo   method;

 

       foreach (Module module in asm.GetModules())

       {

             method = FindModuleMethod( module, method_name);

             if( method != null)

                    return method;

       }

       return null;

}

 

As you have already guessed, FindModuleMethod function loops through module’s classes to search the method:

public static MethodInfo FindModuleMethod(Module module, string method_name)

{

       Type[]       module_types;

       MethodInfo   method;

 

       module_types = module.GetTypes();

 

       foreach (Type module_class in module_types)

       {

             method = FindClassMethod( module_class, method_name);

             if( method != null)

                    return method;

       }

 

       return null;

}

 

Finally FindClassMethod searches for the method inside the class’s available methods:

public static MethodInfo FindClassMethod( Type module_class, string method_name)

{

       MethodInfo[] methods;

 

       methods = module_class.GetMethods();

       foreach (MethodInfo method in methods)

       {

             if( string.Compare( method.Name, method_name, true) == 0)

                    return method;

       }

 

       return null;

}

 

This is a first approach that I believe that many useful applications can be built on.

I will expose some ideas on the subject in a next post.

 

Download the sample code (with some more details inside) InvokeMethodByName.zip (30.31 kb)

 

Revisiting the Database design foundations

Software is about defining objects structures, relationships and behavioral contour. This is probably a too short sentence to define what software is. But it can be a good commencement for what I would like to expose here.

Years passed for the software industry to mature and be able to express the complexity of this task. And, it seems that there still will be some more years to come for this maturity to gain some sort of stability and normalization.

Remember the lessons about the ‘Date object’ which would never display a ‘February 30’ day. Although that seems so far away, we still encounter things of the same kind (remember, for example, the ‘page 1 of 0’ syndrome!). Those are simple indications about how complex it can be to mimic the ‘real world’ system, its large span of objects, relationships and behaviors.

 

One of the early, and interesting, questions that the Software had to solve was: data storage.

Historically speaking, we can summarize the maturity of the provided solution into three stages:

§ The folder/file structure

§ The data table

§ The ‘relational’ database

 

The ‘data table’ solution seems particularly interesting:

If you have a look at a data table (even within any of the most elaborate of today’s database engines) you can easily state how the simplicity of the structure looks embarrassing:

§ A meta-data space which describes the table’s columns.

§ And, a storage space where the columns’ data is stored into rows.

 

 

 

 

Basically, the column definition describes its data type and storage size. This allows the storage system to organize the data rows according to theses definitions.

 

Of course, with the time going, so many other attributes and artifacts had been added to the data table solution. Column definitions, for example, got more elaborate (ex: default value, auto-increment, indexes, data validation constraints… etc.), Columns’ relationships (foreign keys…), Operations like row insertions, row deletions or row updates can be triggered by procedures that execute particular ‘business’ logic… etc. But the foundations of the solution remain strictly the same as the early reflection and design…

One of the reasons of this design success seems to be the readability of the solution.

 

Anyway, the fact is, today, nearly no one single software solution can live without a database! And that itself is a profound demonstration of the efficiency of the initial design.

 

The Software ‘Code / Database’ schism!

Strangely, databases still live almost apart from the software development cycle. When you talk software, you seem to be supposed talking about classes, inheritance, behaviors, user interface… etc. and databases almost seem so simplistic compared to this ‘alive’ jungle!

The reasons for this schism seem to be cultural and social (within the development community) more than being a verdict against the database solution efficiency.

 

Industrially speaking, this schism seems often to be one of the most common explanations of software projects failures. It is also, probably, one of the profound reasons for some of the software industry’s inefficiencies.

 

What I think is that databases efficiency can be beneficially used for leveraging software objects properties and behaviors. And that database organization and readability bring interesting applications and solutions for controlling objects behaviors in order to create more versatile and scalable software solutions.

At a very simple level, think about application configuration files (which are great solutions but can sometimes hardly be altered without producing inconsistencies).

Think about a truly structured configuration file, one that guaranties no typing errors, no inconsistencies according to application’s specific logic.

This can be, relatively easily, achieved using a Database!

 

Database-stored menus… extending and redirecting actions

Let’s go a step forward and use the database to store, for instance, application menus.

A menu is basically a displayed text that, when clicked, invokes an application’s functionality.

By storing menus in the database, we may be wishing to expose only some of our application’s functionalities (according to specific application version for example). We may also be wishing to let our customer’s IT team to expose different functionalities to user groups according to a predefined business role hierarchy.

We can also use this structure to localize our application menus without having to recompile specific localized versions.

Another advantage: to be able to add a new menu after a new functionality is added to the software, or remove one that no more exist etc.

 

Menus are good examples for the ‘action’ concept. In fact, clicking a menu, a button or other button-like item in the user interface, results in invoking an action in the software. The action to be invoked has an entry point (a function) which is, normally, part of the handled object’s code.

 

Thanks to .NET System.Reflection (or any Reflection-like library) we can retrieve a method by its name. This can allow us to store object’s method names in the database, and, in the action properties, tell the software which method to be called. The software can then search the defined method, and, if found, invokes it when the action is activated.

 

Of course there are some other considerations to be taken into account: invoked function parameters, return value... etc. But the main concept remains correct and feasible.

 

Database-based solutions advantages?

Well, just to name a few:

§  Runtime object properties modification: Imagine a (dynamic) ‘web page section’ object which is initially created as 600/800 pixels dimensions. A database-based solution can allow the user to easily change these properties to obtain, say, a 1027/800 pixels object on runtime… without having to recompile the application’s code;

§  Runtime object properties extension: Software (compiled) objects’ structures NEVER satisfy at 100% real world structures or requirements (at least during a reasonable period of time). With a database-based solution, using, for example, an EAV (Entity Attribute Value) implementation, you can envisage extending compiled software objects… without having to recompile the application’s code;

§  Action processing redirection: Imagine a software functionality that may require authentication only in some contexts and none in other contexts, depending on the customer’s environment. Thanks to database readability and simplicity, the customer can easily choose which entry point to assign to the action according to his or her environment (without having to recompile the software code)

§ 

§ 

 

SQL recursive schemas

SQL recursive schemas

 

One of the interesting features of relational databases is the possibility for a table to reference itself.

Suppose we want to represent the file folder system in a database. We can define a table having, for example, 3 fields: id, parent_id and name:

 

We can then create a self referencing relationship between the id and parent_id field.

Simple (and elegant) solution!

Yes, but that also let us inherit all specific constraints of recursive patterns.

 

The figure above shows the simple (and somehow elegant) of the table schema or pattern.

What it doesn’t show is how the ‘real world’ records will look like when stored in the table:

 

 

Two important constraints of such recursive schema are:

·         Path readability: i.e. as the schema allows an infinite nest levels, reading an item path may cause a stack overflow;

·         Cyclic references: i.e. a folder that references itself (or one of its children) as a parent (which may end up by creating infinite path readability loop;

 

The Nest level problem

A function or stored procedure for reading an item path may look like this:

 

CREATE FUNCTION dbo.Read_path

(

       @item_id int

)

RETURNS nvarchar(max)

AS

BEGIN

       declare             @parent_id   int,

                    @path        nvarchar(max),

                    @name        nvarchar(50)

      

       select @parent_id   = parent_id,

             @name        = name

       from dbo.sample_folders

       where(id = @item_id)

      

       if @parent_id is null

             return @name

 

       -- recursive call  

       return dbo.Read_path ( @parent_id) + N'\' + @name

END

GO

 

 

Such a function will work nicely as long as the folder’s nest level remains in a ‘reasonable’ value. For example, in MS SQL Server, nest level (recursive call stack whose value can be returned at any time by the @@nestlevel system function) has a maximum of 32.

 

So, it is more precautious to test current @@nestlevel value before proceeding:

 

if @@nestlevel >= 32

       return N'Path nest level exceeds allowed value'

 

However, doing so breaks the efficiency and usability of our pattern. The best solution would be to get rid of the recursive call inside our function. Here is an example using a goto label:

 

       DECLARE @folder_name       nvarchar(255),

              @parent_id   int,

              @cur_item_id int,

              @path        nvarchar(max)

 

       SELECT @path        = N'',

              @cur_item_id = @item_id

 

-- recursion label

get_nested_path:

       SELECT @folder_name = f.name,

             @parent_id   = f.parent_id

       FROM   dbo.sample_folders AS f

       WHERE( f.id = @cur_item_id)

 

 

       if @parent_id is null

       begin

              -- We are on the root

              return @folder_name + @path

       end

 

       select @cur_item_id = @parent_id,

              @path        = N’\’ + @folder_name + @path

 

       -- goto recursion label

       goto get_nested_path

 

 

Cyclic references problem

Our read path function can still be trapped into an infinite loop that can be caused by cyclic references… i.e. a folder referencing itself or one of its children as a parent:

 

 

One way to prevent cycling references is to create a constraint on the folders table that tests for cycling references. The constraint may call a function that returns true (1) if the item is not valid:

 

CREATE FUNCTION [dbo].[Is_cyclic_folder]

(

       @item_id            int,

       @parent_id          int

)

RETURNS bit

AS

BEGIN

       -- if this is a root item: OK

       if @parent_id is null

             return 0

      

       -- self referencing?

       if @parent_id = @item_id

             return 1

      

       declare             @parent_folder_id   int,

                    @folder_id          int

      

       -- initial parent folder

       select @parent_folder_id = @parent_id

      

       while NOT @parent_folder_id is null

       begin

             -- if the current parent folder is one of children folders:

             -- then this item is cyclic (NOT VALID)

             if @parent_folder_id = @item_id

                    return 1

            

             select @folder_id   = @parent_folder_id

            

             select @parent_folder_id   = parent_id

             from dbo.sample_folders

             where( id = @folder_id)

       end

      

       return 0

END

 

The constraint may look like this:

 

ALTER TABLE dbo.sample_folders ADD CONSTRAINT

       CK_sample_folders_cyclic CHECK ([dbo].[Is_cyclic_folder](id, parent_id) = 0)