Ivan Porto Carrero

IO(thoughts) flatMap (_.propagandize)

05

Feb
2008

Using Ruby to Generate LightSpeed Models - Part 2

This is the second post in the series on generating LightSpeed entities with the help from ruby.

In the previous post we connected successfully to the database and were able to execute some sql.

At the end of the series I’ll make the code downloadable.

Today I’d like to talk about the metadata we’ll be needing from the database. We’re going to need a list of tables, we’re going to need to know about the columns of each table. Furthermore we want to exclude the primary keys in the case of LightSpeed. And we also want to know about relationships whether they are has many, belongs to or has many and belongs to many.

I put all this in a separate module because I’ll probably need that meta data for another thing later :). The above requirements translate in the following spec:

LightSpeedRepository DB::MetaData  

- should have meta data  

- should resolve the table name from a string  

- should resolve the table name from a hash  

- should identify a given column as not being a foreign key  

- should identify a foreign key given a valid column info  

- should not identify a given column as being a primary key  

- should identify a given column as being a primary key  

- should not identify a table as a join table under the correct conditions  

- should identify a table as a join table under the correct conditions  

- should return an empty array of has many relations when there are none  

- should return the has many relations given a table  

- should return the end point tables for a given through association  

- should return the through associations  

The first thing we’re going to need are the sql statements. At this point I only need the statements for sql 2005 so and these are the ones I used.

def self.sql_statements
  {
    :tables => "SELECT table_name as name FROM information_schema.Tables Where table_type='Base Table' ORDER BY table_name",
    :column_info => "select object_name(c.object_id) as table_name, c.column_id, c.name, type_name(system_type_id) as sql_type, max_length, is_nullable, precision, scale, 
          convert(bit,(Select COUNT(*) from sys.indexes as i 
            inner join sys.index_columns as ic
              on ic.index_id = i.index_id and ic.object_id = i.object_id 
            inner join sys.columns as c2 on ic.column_id = c2.column_id and i.object_id = c2.object_id
          WHERE i.is_primary_key = 0 
            and i.is_unique_constraint = 0 and ic.column_id = c.column_id and i.object_id=c.object_id)) as is_index,
          is_identity, 
          is_computed, 
          convert(bit,(Select Count(*) from sys.indexes as i inner join sys.index_columns as ic
              on ic.index_id = i.index_id and ic.object_id = i.object_id 
            inner join sys.columns as c2 on ic.column_id = c2.column_id and i.object_id = c2.object_id
          WHERE (i.is_unique_constraint = 1) and ic.column_id = c.column_id and i.object_id=c.object_id)) as is_unique
          from sys.columns as c
          WHERE object_name(c.object_id)  in (select table_name    FROM information_schema.Tables WHERE table_type = 'Base Table') 
          order by table_name",
    :primary_keys => "SELECT i.name AS index_name,ic.index_column_id,key_ordinal,c.name AS column_name,TYPE_NAME(c.user_type_id)AS column_type 
                ,is_identity,OBJECT_NAME(i.object_id) as table_name FROM sys.indexes AS i INNER JOIN sys.index_columns AS ic ON 
                i.object_id = ic.object_id AND i.index_id = ic.index_id INNER JOIN sys.columns AS c ON ic.object_id = c.object_id
                AND c.column_id = ic.column_id WHERE i.is_primary_key = 1 order by table_name",
    :foreign_keys => "SELECT f.name AS foreign_key_name, object_name(f.parent_object_id) AS table_name , col_name(fc.parent_object_id, fc.parent_column_id) AS child_id
                ,object_name (f.referenced_object_id) AS parent_table ,col_name(fc.referenced_object_id, fc.referenced_column_id) AS parent_id FROM sys.foreign_keys AS f
                INNER JOIN sys.foreign_key_columns AS fc ON f.object_id = fc.constraint_object_id where OBJECT_NAME(f.parent_object_id) not in ('sysdiagrams')  order by table_name"
  }
end

Those statements contain all the data we need an probably a little bit more too, if we add a little metaprogramming we can have ruby generate that metadata data for us :)

def populate
  DB::MetaData.sql_statements.each do |key, value|
    instance_variable_set("@"+key.to_s, @db.fetch_all(value))                
  end
end

sql_statements.each_key do |key|
   define_method("#{key}_for") do |table|
     send(key, table).select { |item| item[:table_name] == table_name(table) }
   end unless key == :tables
 end

So now we’ve satisfied our first spec the module now contains all the meta data we need. The rest of the specs require far less code than what we wrote here. Below you’ll find the code needed to satisfy all of the specs. It are just a couple of methods that check some conditions and a couple of predicates we’re going to need later on. The get_endpoint_tables method is the only one that doesn’t explain itself easily. That method returns the table names from tables that are the second level in a has many and belongs to many scenario.

module DB
  module MetaData
    attr_accessor :tables, :primary_keys, :foreign_keys, :column_info

    def initialize
      @db = DB::DbiSqlServer.new
      populate
    end

    def populate
      DB::MetaData.sql_statements.each do |key, value|
        instance_variable_set("@"+key.to_s, @db.fetch_all(value))                
      end
    end

    def collect_has_many_relations(table)
      fks = foreign_keys.select { |fk| fk[:parent_table] ==  table_name(table)  }

      fks.collect  do |fk|
        unless fk[:table_name].nil?
          { :table_name => fk[:table_name].underscore, :class_name => fk[:table_name].singularize.underscore.camelize }
        end
       end.compact
    end

    def collect_through_associations(table)
      fks = foreign_keys.select { |fk| fk[:parent_table] ==  table_name(table)  }

      fks.collect do |fk|
          { :through_table => fk[:table_name].underscore, :end_tables => get_endpoint_tables(table, fk[:table_name]) } if join_table?(fk[:table_name])
      end.compact
    end

    def get_endpoint_tables(table, through_table)
      fks = foreign_keys.select { |fk| fk[:table_name] == table_name(through_table) and fk[:parent_table] != table_name(table)  }
      fks.collect { |fk| fk[:parent_table].underscore unless fk[:parent_table].nil?  }.compact
    end

    def get_belongs_to_table(table, column_name)
      fks = foreign_keys.select { |fk|  fk[:table_name] == table_name(table) and fk[:child_id] = column_name }
      return fks[0][:parent_table] if fks.size > 0
      nil
    end

    def join_table?(table)
      fks = foreign_keys_for table_name(table)
      fks.size > 1
    end

    def primary_key?(column_info)
      pks = primary_keys.select { |pk| pk[:table_name] == column_info[:table_name] and pk[:column_name] == column_info[:name]   }

      pks.size > 0
    end

    def foreign_key?(column_info)
      fks = foreign_keys.select { |fk| fk[:table_name] == column_info[:table_name] and fk[:child_id] == column_info[:name]  }
      fks.size > 0
    end

    def table_name(table)
      table.is_a?(Hash) ? table[:name] : table
    end

    def self.sql_statements
      {
        :tables => "SELECT table_name as name FROM information_schema.Tables Where table_type='Base Table' ORDER BY table_name",
        :column_info => "select object_name(c.object_id) as table_name, c.column_id, c.name, type_name(system_type_id) as sql_type, max_length, is_nullable, precision, scale, 
              convert(bit,(Select COUNT(*) from sys.indexes as i 
                inner join sys.index_columns as ic
                  on ic.index_id = i.index_id and ic.object_id = i.object_id 
                inner join sys.columns as c2 on ic.column_id = c2.column_id and i.object_id = c2.object_id
              WHERE i.is_primary_key = 0 
                and i.is_unique_constraint = 0 and ic.column_id = c.column_id and i.object_id=c.object_id)) as is_index,
              is_identity, 
              is_computed, 
              convert(bit,(Select Count(*) from sys.indexes as i inner join sys.index_columns as ic
                  on ic.index_id = i.index_id and ic.object_id = i.object_id 
                inner join sys.columns as c2 on ic.column_id = c2.column_id and i.object_id = c2.object_id
              WHERE (i.is_unique_constraint = 1) and ic.column_id = c.column_id and i.object_id=c.object_id)) as is_unique
              from sys.columns as c
              WHERE object_name(c.object_id)  in (select table_name    FROM information_schema.Tables WHERE table_type = 'Base Table') 
              order by table_name",
        :primary_keys => "SELECT i.name AS index_name,ic.index_column_id,key_ordinal,c.name AS column_name,TYPE_NAME(c.user_type_id)AS column_type 
                    ,is_identity,OBJECT_NAME(i.object_id) as table_name FROM sys.indexes AS i INNER JOIN sys.index_columns AS ic ON 
                    i.object_id = ic.object_id AND i.index_id = ic.index_id INNER JOIN sys.columns AS c ON ic.object_id = c.object_id
                    AND c.column_id = ic.column_id WHERE i.is_primary_key = 1 order by table_name",
        :foreign_keys => "SELECT f.name AS foreign_key_name, object_name(f.parent_object_id) AS table_name , col_name(fc.parent_object_id, fc.parent_column_id) AS child_id
                    ,object_name (f.referenced_object_id) AS parent_table ,col_name(fc.referenced_object_id, fc.referenced_column_id) AS parent_id FROM sys.foreign_keys AS f
                    INNER JOIN sys.foreign_key_columns AS fc ON f.object_id = fc.constraint_object_id where OBJECT_NAME(f.parent_object_id) not in ('sysdiagrams')  order by table_name"
       }
     end

     sql_statements.each_key do |key|
       define_method("#{key}_for") do |table|
         send(key, table).select { |item| item[:table_name] == table_name(table) }
       end unless key == :tables
     end

  end

end

05

Feb
2008

Compiling Mono and IronRuby on OSX Leopard

I tried to compile IronRuby on OS X (leopard) with the dmg I downloaded from the mono website, and that didn’t work.

I then uninstalled that mono version by running monoUninstall.sh and proceeded to get mono from subversion. I’m putting these steps on my blog more for future reference when I decide to reinstall my box for some reason.

Download gettext, pkgconfig and glib2.0

extract the archives and build them in the following order gettext, pkgconfig, glib2.0

./configure --prefix=/opt/local
make
sudo make install

At this point it would be wise to set the PKG_CONFIG_PATH environment variable. I added the following line to ~/.bash_profile

export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:/opt/local/lib/pkgconfig"

next it’s time to check out the mono sources from their repositories

cd ~/
mkdir tools
mkdir mono
svn co svn://anonsvn.mono-project.com/source/trunk/mono
svn co svn://anonsvn.mono-project.com/source/trunk/mcs
svn co svn://anonsvn.mono-project.com/source/trunk/libgdiplus
svn co svn://anonsvn.mono-project.com/source/trunk/moon
svn co svn://anonsvn.mono-project.com/source/trunk/olive
svn co svn://anonsvn.mono-project.com/source/trunk/gtk-sharp

checking those out will take a while

now go into the mono directory and build mono

cd mono
./autogen.sh --prefix=/opt/local/mono --with-preview=yes --with-moonlight=yes

–with-preview enables the .NET 3.5 features that have been implemented so far –with-moonlight enables support for moonlight

make
sudo make install

This will also take some time and when it completes you can check if mono is installed by typing mono -V

That’s it for mono, now onto IronRuby

cd ~/tools
svn co http://ironruby.rubyforge.org/svn/trunk ironruby
sudo gem install pathname2
rake compile mono=1

And that should be all :)

UPDATE: I’ve got new instructions for building IronRuby

04

Feb
2008

Using Ruby to Generate LightSpeed Models - Part 1

This is the first in a multi-part post on a little ruby application I wrote to generate models for LightSpeed.

The ultimate goal is to consume the entities we generate in this series with IronRuby and perform some data access.

Today I’ll post the code I wrote for creating the database connection. At this moment there is only code there to connect tho sql server. But I may want to add providers later if I decide to keep using this code. That’s why some bits are in a separate module.

This are the specs I wrote for the connection manager. The connection manager is the class that reads the database config, gets a connection and executes sql statements. I think this code is pretty simple so I won’t put a line-by-line explanation.

It uses DBI to connect to the database and reads out the results of the executed sql statement. In the next post I’ll talk about getting the metadata that is required from sql server.

DB::DbiSqlServer  
- should return a connection  
- should say it's an ODBC connection when a dsn is provided  
- should return the correct connection string for an ODBC connection
module DB

    module SqlConnectionManager

        DEFAULT_CONFIG_PATH = File.dirname(__FILE__) + '/../config/database.yml'

        attr_reader :connection_string, :connection

        def initialize(config=DEFAULT_CONFIG_PATH)
          if config.is_a? Hash
            initialize_config config
          else
            read_config config
        end
        end

        def read_config(config_path, config_name = 'sqlserver')
            initialize_config(YAML::load(File.open(config_path || DEFAULT_CONFIG_PATH))[config_name])
        end

        def initialize_config(config)
          @config = config
            @connection=nil
      end

        def odbc?
          return true unless @config.nil? || @config['dsn'].nil?
          false
        end


    end


    class DbiSqlServer
        include SqlConnectionManager


        def connection
            if @connection.nil?
              @connection = DBI.connect(connection_string, @config['username'], @config['password'])
            end
            @connection
        end

        def connection_string
          if odbc?
            "DBI:ODBC:#{@config['dsn']}"
          else
            "DBI:ADO:Provider=SQLOLEDB;Data Source=#{@config['host']};Initial Catalog=#{@config['database']};User ID=#{@config['username']};Password=#{@config['password']};"
        end
      end

        def fetch_all(sql_statement)
            result = []
            connection.execute sql_statement do |statement|
                while row = statement.fetch do
                    r = {}
                    row.each_with_name do |val, name|
                        r[name.to_sym] = val
                    end
                    result << r
                end
            end
            result
        end

        def execute_non_query(sql_statement)
          connection.do sql_statement
        end

    end

end

04

Feb
2008

OSX and Resharper Get in Eachothers Way

And today I discovered that using unity severly interferes with my flow in visual studio.

I have ctrl-space mapped to quicksilver on mac osx which would mean I have to rebind either autocompletion or quicksilver, and it sure as hell won’t be my autocompletion. I think it will be too hard to unset that binding in my brain I’ve been using ctrl-space for about 10 years now, without even thinking about it. I even try to use it in Word but of course that doesn’t work ;)

The typing speed is also a little bit too sluggish for me to actually enjoy working in visual studio in unity mode. I decided to get back to a ruby project in textmate :)

I’ll try later on if it’s doable through full screen mode in fusion; and if that doesn’t work, I’ll have no other option than to boot into vista if I have to do .NET work.

There is another key missing from the mac keyboard, the insert key (and I have to admit about this one I’m definitely not that happy - what were they thinking??). So far the keyboard is the hardest to get used to. I mean those would probably be my most used shortcuts : ctrl - arrow, home, end, alt-insert and i now have to learn how to use different combinations and different ones in every program.

In other news, today i found myself using alt-C and alt-V on my windows keyboard when I wanted to copy paste. I think all the shortcuts have no right or wrong combination but it won’t be long before I master the ones on the mac that’s for sure.

I feel a lot better having that off my chest.

03

Feb
2008

Got a Macbook Pro Earlier This Week

Last week I ordered a macbook pro (2.6Ghz - 4GB RAM - 200GB/7200RPM HD) through the shop.

It took about 10 days to arrive. I found that the process was ok but there is a little bit less communication than when I ordered from Dell. It will come as no suprise that the packaging was very well done :)

I ‘ve been using it for the last couple of days. It took me some time to get used to the different keyboard shortcuts that’s for sure.

I’m happy with my purchase but I do wonder what the harm is in providing a separate home and end key. If somebody knows how I can get a consistent word selection going ( I mean like ctrl - arrow or shift - ctrl - arrow) that would be of great help because that’s what annoys me most. I set up a full copy of vista on my bootcamp partitition, it had to be the 32-bit so I can’t take full advantage of my 4GB of ram, but that’s the way it has to be because otherwise fusion won’t load my boot camp partitition.

I first tried with a copy of vista I generated with the vlite tool I blogged about earlier but it appears that the full version actually runs faster than the lite version I generated. This will probably be mostly because of my own stupidity than the tool. With the full version I could install all the software from the bootcamp cd which may be why it works faster.

The next couple of days were spent bonding with my new macbook. Being pleasantly surprised that visual studio 2008 with resharper runs at a really acceptable speed for day to day development through fusion with unity turned on. I’ll probably boot into windows when I have to do purely .NET development.

And now I did want to know of textmate is as good as they say it is or if it’s just hype for nothing. I’ve been using textmate the last couple of days for doing some ruby development and i have to admit that while it’s no visual studio for c# it’s definitely an enjoyable experience. I still have to memorise a lot of shortcuts but I’m well on my way :)

Simone’s post on 18 almost free applications for Mac OSX got me everything I needed. I downloaded 2 or 3 extra programs. Like simone I’m trying to go as much native mac apps as possible. So far I have everything I need and need my windows box only for windows related development tools like LightSpeed, LLBLGen, SQL Server and Visual Studio etc.

7zX or something along those lines for zipping and unzipping 7z archives.
MacFUSE and NTFS-3G for mac OSX so that I can mount read/writeable NTFS volumes.

Another thing I wonder is if there maybe is a tree for navigating in finder. I’m having a hard time compiling IronRuby on OSX but I also haven’t really that hard yet.

Thanks to Simone I wrote this blog post with Ecto. I couldn’t believe how easy it was to connect to a windows vpn and remote desktop in there to fix up some servers.

My first week as a mac user was a success :D

16

Jan
2008

Back to Contracting

Yesterday I gave notice at Xero, my last day will be mid February (15/02).

It’s been fun working with the team of people at Xero, but I just want my own time back. I won’t go into more detail around the reasons for my leaving Xero but we part ways on friendly terms.

I’ve been getting more and more into Ruby again and I really like that language. So I’m mostly interested in jobs that will allow me to use those dynamic languages. I can now take some time off to finish the book I’m writing and to do some coding on a couple of things I have in mind.

Ideal places for me to work would be a place that practices Agile development et al. I’ll be going back to contracting probably unless somebody makes me a really great offer :)

26

Dec
2007

New View Engines in the MvcContrib Project for asp.net Mvc

When I got back from visiting my parents over the christmas holiday I checked the MvcContrib project to find out that both Andrew Peter’s NHAML view engine and my XSLT view engine have been accepted in the trunk of those projects.

What’s left to do for me is provide a sample site that shows usage of the view engine.  This has been a very rewarding experience so far :). I found out about IViewSourceLoader to get the path to the current view file for example.

I also learnt a new trick to increase my test coverage and be able to test the content of the rendered view.

To get the mvc contrib project you can either download it from the releases section on the codeplex site.

Or you can check out the source code and build it yourself from the subversion repository

19

Dec
2007

And Another View Engine for ASP.NET MVC Has Been Born

Andrew Peters wrote a view engine for HAML

NHaml (pronounced enamel) is a pure .NET implementation of the popular RailsHaml view engine. From the Haml website:

“Haml is a markup language that‘s used to cleanly and simply describe the XHTML of any web document, without the use of inline code. Haml functions as a replacement for inline page templating systems such as PHP, ERB, and ASP. However, Haml avoids the need for explicitly coding XHTML into the template, because it is actually an abstract description of the XHTML, with some code to generate dynamic content.”

Andrew and I share the same dislike for the xml family. XML is not fun to type, and I think his haml engine eases a lot of that pain. It results in far more readable views too.

2 view engines in one week and both from Wellington. Nice :)

del.icio.us Tags: aspnetmvc

16

Dec
2007

Implementing Filters in asp.net Mvc

One of the things I wanted for the framework I’m building for Xero was to implement filters.

I have a first pass of those ready.. didn’t take me to long to implement either.  I only tested the before filters I haven’t gotten round to testing the After filter. I have to do a demo today that demonstrates databinding (like the castle project solved it.)  What I’m putting on my blog here is very much a proof of concept implementation and you should not use this in a production environment.

Here’s how I went about it. In this post I’ll show how I implemented a filter that directs anonymous users to the login page.

  1. Define a couple enumerations
public enum Execute
{
    Before,
    After,
    BeforeAndAfter
}

public enum SecureFor
{
    None,
    Anonymous,
    PerUser
}
  1. Create an IFilter interface
using System.Web;

namespace Xero.Mvc.Extensions.Filters
{
    public interface IFilter
    {
        Execute WhenToExecute { get; }
        IHttpContext HttpContext { get; set; }
        void Execute();
    }
}

  1. Create an AbstractFilter base class
using System;
using System.Web;

namespace Xero.Mvc.Extensions.Filters
{
    [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    public abstract class AbstractFilter : Attribute, IFilter
    {
        private readonly Xero.Mvc.Extensions.Execute whenToExecute;
        private IHttpContext httpContext;
        public AbstractFilter(Xero.Mvc.Extensions.Execute whenToExecute)
        {
            this.whenToExecute = whenToExecute;
        }

        public IHttpContext HttpContext
        {
            get { return httpContext; }
            set { httpContext = value; }
        }

        public Xero.Mvc.Extensions.Execute WhenToExecute
        {
            get { return whenToExecute; }
        }

        public abstract void Execute();
    }
}
  1. Create a SecureFilter base class
using System;
using System.Web;
using System.Collections.Generic;

namespace Xero.Mvc.Extensions.Filters
{
    [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    public abstract class AbstractSecureFilter : AbstractFilter, IFilter
    {
        private readonly SecureFor secureFor;

        public AbstractSecureFilter(Xero.Mvc.Extensions.Execute whenToExecute, SecureFor secureFor)
            : base(whenToExecute)
        {
            this.secureFor = secureFor;
        }

        public SecureFor SecureFor
        {
            get { return secureFor; }
        }

        protected void RedirectToLogin()
        {
            HttpContext.Response.Redirect("~/", true);
        }
    }
}
  1. Implement the concrete AnonymousUsersFilter
using Xero.Mvc.Extensions.Filters;
using Xero.Mvc.Tasklist.Model.EntityClasses;
using Xero.Mvc.LLBLGenIntegration.Services;
using System;
using System.Collections.Generic;
using Xero.Mvc.Extensions;

namespace Xero.Mvc.Tasklist.Filters
{
    [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    public class AnonymousUsersFilter : AbstractSecureFilter
    {
        private User[] users;
        private DataService<User> userService;
        public AnonymousUsersFilter(Xero.Mvc.Extensions.Execute whenToExecute, SecureFor secureFor) : this(whenToExecute, secureFor, null) { }

        private AnonymousUsersFilter(Xero.Mvc.Extensions.Execute whenToExecute, SecureFor secureFor, User[] users)
            : base(whenToExecute, secureFor)
        {
            this.users = users;
            this.userService = new DataService<User>();
        }
        public AnonymousUsersFilter(Xero.Mvc.Extensions.Execute whenToExecute, User[] users) : this(whenToExecute, SecureFor.PerUser, users) { }

        public User[] Users
        {
            get { return users; }
        }

        public override void Execute()
        {
            if (SecureFor == Xero.Mvc.Extensions.SecureFor.Anonymous && HttpContext.Session["userId"] == null)
            {
                RedirectToLogin();
            }

            if (SecureFor == Xero.Mvc.Extensions.SecureFor.PerUser && Users == null)
                RedirectToLogin();

            if (SecureFor == Xero.Mvc.Extensions.SecureFor.PerUser)
            {
                User currentUser = userService.FindOneById((Guid)HttpContext.Session["userId"]);
                List<User> allowedUsers = new List<User>(Users);
                if (allowedUsers.Find(usr => usr.Name.ToUpperInvariant() == currentUser.Name.ToUpperInvariant()) == null)
                RedirectToLogin();
            }
        }
    }
}
  1. Implement the filter on a controller
[AnonymousUsersFilter(Xero.Mvc.Extensions.Execute.Before, SecureFor.Anonymous)]
public abstract class SecureControllerBase : SmartXeroController
  1. There are a couple of places where you can implement the execution of the filter logic. I chose to do it before the actual controller class is being loaded. To do that I had to create a handler and a routehandler

7.a The MvcHandler

using System.Web.Mvc;
using System;
using Xero.Mvc.Core.Exceptions;
using Xero.Mvc.Extensions.Filters;
using System.Linq;

namespace Xero.Mvc.Extensions
{
    public class XeroMvcHandler : MvcHandler
    {

        protected override void ProcessRequest(System.Web.IHttpContext httpContext)
        {
            if (this.RequestContext == null)
            {
                throw new NoRequestContextException();
            }

            string controllerName = this.RequestContext.RouteData.Values["controller"].ToString();

            Type controllerType = this.GetControllerType(controllerName);
            if (controllerType == null)
            {
                throw new NoControllerFoundException(this.RequestContext.HttpContext.Request.Path);
            }

            IFilter[] filters = controllerType.GetCustomAttributes(typeof(IFilter), true) as IFilter[];
            filters
                .Where(attr => attr.WhenToExecute == Xero.Mvc.Extensions.Execute.Before || attr.WhenToExecute == Xero.Mvc.Extensions.Execute.BeforeAndAfter)
                .ToList()
                .ForEach(attr =>
                {
                    if (attr != null)
                    {
                        attr.HttpContext = httpContext;
                        attr.Execute();
                    }
                });

            IController controllerInstance = this.GetControllerInstance(controllerType);
            ControllerContext controllerContext = new ControllerContext(this.RequestContext, controllerInstance);
            controllerInstance.Execute(controllerContext);
            filters
                .Where(attr => attr.WhenToExecute == Xero.Mvc.Extensions.Execute.After || attr.WhenToExecute == Xero.Mvc.Extensions.Execute.BeforeAndAfter)
                .ToList()
                .ForEach(attr =>
                {
                    if (attr != null)
                    {
                        attr.HttpContext = httpContext;
                        attr.Execute();
                    }
                });
        }
    }
}

7b. The Route Handler

using System.Web.Mvc;
using System.Web;

namespace Xero.Mvc.Extensions
{
    public class XeroMvcRouteHandler : IRouteHandler
    {

        #region IRouteHandler Members
        public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new XeroMvcHandler { RequestContext = requestContext };
        }

        #endregion
    }
}
  1. And the last step is to tell your application that it needs to use the new route handler. You can do that in the global.asax.cs
protected override void SetupRoutes()
{
  // Note: Change Url= to Url="[controller].mvc/[action]/[id]" to enable 
  //       automatic support on IIS6 

  RouteTable.Routes.Add(new Route
  {

      Url = "Login/Default.aspx",
      Defaults = new { controller = "Login", action = "Index", id = (string)null },
      RouteHandler = typeof(XeroMvcRouteHandler) // Our custom route handler
  });

  RouteTable.Routes.Add(new Route
  {
      Url = "[controller]/[action]/[id]",
      Defaults = new { action = "Index", id = (string)null },
      RouteHandler = typeof(XeroMvcRouteHandler)
  });

  RouteTable.Routes.Add(new Route
  {
      Url = "Default.aspx",
      Defaults = new { controller = "Login", action = "Index", id = (string)null },
      RouteHandler = typeof(XeroMvcRouteHandler)
  });
}

kick it on DotNetKicks.com

14

Dec
2007

ASP.NET MVC XsltViewEngine, Patch Submitted to the Contrib Project

I just submitted a patch to the mvc contrib project that contains the xslt view engine I wrote for Xero without any of the dependencies from our own libraries or commercial components.

The bulk of the work is done in a class called XmlResponseBuilder which builds the xml document that is going to be transformed by the xsl stylesheet. Almost all the rest of the code is there just to build up this document.

The implementation of IView and IViewFactory were the easy bits :)

To use the View engine you need to take the following steps:

  1. Get the MVC Contrib project from google code

repository url: http://mvccontrib.googlecode.com/svn/trunk/

  1. If the patch hasn’t been applied yet you can download it from: http://www.codeplex.com/Project/Download/FileDownload.aspx?ProjectName=MVCContrib&DownloadId;=23748

After downloading the patch you can apply it to the downloaded mvc contrib project.

  1. I use a base controller to ensure that the correct data is passed to the view factory. This is the base controller class:

public abstract class XeroControllerBase : Controller

    {

        private readonly XsltViewData vData = new XsltViewData();

 

        protected IHttpSessionState Session

        {

            get { return ControllerContext.HttpContext.Session; }

        }

 

        public XeroControllerBase()

        {

            ViewFactory = new XsltViewFactory();  

        }

 

        #region Add datasource methods

 

        protected void AddDataSource(IXmlConvertible dataSource)

        {

            vData.DataSources.Add(new XslDataSource(dataSource));

        }

 

        protected void AddDataSource(IXmlConvertible dataSource, string rootName)

        {

            vData.DataSources.Add(new XslDataSource(rootName, dataSource));

        }

 

        #endregion

 

        #region Add variable methods

 

        protected void AddPageVar(string key, string value)

        {

            vData.PageVars.Add(key, value);

        }

 

        #endregion

 

        #region Add message methods

 

 

        protected void AddErrorMessage(string message)

        {

            vData.Messages.Add(new ErrorMessage(message));

        }

 

        protected void AddErrormessage(string message, string controlID)

        {

            vData.Messages.Add(new ErrorMessage(message, controlID));

        }

 

        protected void AddInfoMessage(string message)

        {

            vData.Messages.Add(new InfoMessage(message));

        }

 

        protected void AddInfoMessage(string message, string controlID)

        {

            vData.Messages.Add(new InfoMessage(message, controlID));

        }

 

        protected void AddAlertMessage(string message)

        {

            vData.Messages.Add(new AlertMessage(message));

        }

 

        protected void AddAlertMessage(string message, string controlID)

        {

            vData.Messages.Add(new AlertMessage(message, controlID));

        }

 

        protected void AddInfoHtmlMessage(string message)

        {

            vData.Messages.Add(new InfoHtmlMessage(message));

        }

 

        protected void AddInfoHtmlMessage(string message, string controlID)

        {

            vData.Messages.Add(new InfoHtmlMessage(message, controlID));

        }

 

        protected void AddErrorHtmlMessage(string message)

        {

            vData.Messages.Add(new ErrorHtmlMessage(message));

        }

 

        protected void AddErrorHtmlmessage(string message, string controlID)

        {

            vData.Messages.Add(new ErrorHtmlMessage(message, controlID));

        }

 

        protected void AddAlertHtmlMessage(string message)

        {

            vData.Messages.Add(new AlertHtmlMessage(message));

        }

 

        protected void AddAlertHtmlMessage(string message, string controlID)

        {

            vData.Messages.Add(new AlertHtmlMessage(message, controlID));

        }

 

        #endregion

 

        #region RenderView method hides

 

        protected new virtual void RenderView(string viewName)

        {

            RenderView(viewName, string.Empty, vData);

        }

 

        protected new void RenderView(string viewName, string masterName)

        {

            RenderView(viewName, string.Empty, vData);

        }

 

        protected new void RenderView(string viewName, object viewData)

        {

            RenderView(viewName, string.Empty, vData);

        }

 

        #endregion 

    }

 

del.icio.us Tags: aspnetmvc

 

kick it on DotNetKicks.com

To top