Joe Audette, MCSD, MCDBA, MCSE

Founder and primary developer of mojoPortal, an open source content management system.

Silverlight + Google Gears = Awesome! at least in Firefox

 cross post from mojoPortal.com

I spent most of the week prototyping some things in Silverlight. I figured during the holiday week most people out there goofed off on their jobs a lot this week, so rather than work on my roadmap priorities, I decided to have some fun and play with Silverlight.

Some of you who have followed my blog for a while may remember some posts I made in the past about my plans for Site Office as a second plug in model for mojoPortal more geared to line of business apps that need a consistent look and feel rather than the web site kind of look, they need to look like applications. I originally protyped the UI using Dojo and then later re-did it with ExtJs. You can see the ExtJs version if you login to this site (or http://demo.mojoportal.com using admin@admin.com and password admin), then click the Site Office link at the top. You'll see the drag resizable panes that give the idea of where I wanted to go with Site Office as a UI for LOB applications. This prototype has just been sitting there without much attention because of other priorities and also partly because my enthusiasm for ExtJs disappeared when they changed the license from LGPL to GPL. Anyway, even back then I implemented a google gears query tool. Its really the only functioning app in the old Site Office prototype, you can find it by clicking the My Stuff in the left accordian menu in Site Office and then click SQL. For those who don't know, google gears is a client side SQL database built on SQLite and having this database available opens a lot of possibilities in web development for very rich and responsive applications.

Well, now my plan is to scrap the old ExtJs based Site Office prototype and build a better one with Silverlight. I've already got the Google Gears Query Tool re-implemented in Silverlight as shown below:

silverlight google gears query tool ascreenshot

I wrote a nice managed code wrapper around the javscript calls for gears. The only problem is, it doesn't work well in IE 7 for some reason, it works great in Firefox. I've sent an email off to Scott Guthrie at Microsoft in hopes of some help looking into the problem, but for now you can try it out online at http://demo.mojoportal.com/Index.aspx, you can see that I've got the basic layout of Site Office again implemented in Silverlight with the drag re-sizable panes. I plan to build a plug in model that allows you to plugin your own Silverlight applets and let the framework provide stuff thats common across applications. If I can get google gears working well across browsers with Silverlight its really going to be sweet. The code for this is in my svn sandbox and will probably land in trunk sometime next week.

Now using the managed gears wrapper can be seen in this client side business/data class, it looks very much like a server side class but its a client side object populated from a client side database in a very similar fashion to what it would look like in server side code. Notice the parametrized queries to prevent sql injection attacks. This class represents a saved query but it could represent anything.

using System;


namespace mojoPortal.Silverlight.Helpers.Gears
{
   
    public class SavedQuery
    {
        public SavedQuery()
        {}

        private int id = -1;
        private string name = string.Empty;
        private string query = string.Empty;

        public int Id
        {
            get { return id; }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public string Query
        {
            get { return query; }
            set { query = value; }
        }

        public void Save(GearsDb gearsDb)
        {
            if (id == -1) { Create(gearsDb); return; }

            Update(gearsDb);
        }

        private void Create(GearsDb gearsDb)
        {
            if (gearsDb == null) { return; }

            string sqlCommand = "insert into savedqueries (name, query) values (?, ?)";
            object[] parameters = new object[2];
            parameters.SetValue(name, 0);
            parameters.SetValue(query, 1);
            gearsDb.Execute(sqlCommand, parameters);
            id = gearsDb.LastInsertRowId();

        }

        private bool Update(GearsDb gearsDb)
        {
            if (gearsDb == null) { return false; }

            string sqlCommand = "update savedqueries set name = ?, query = ? where id = ?";
            object[] parameters = new object[3];
            parameters.SetValue(name, 0);
            parameters.SetValue(query, 1);
            parameters.SetValue(id, 2);
            gearsDb.Execute(sqlCommand, parameters);
            int rowsAffected = gearsDb.RowsAffected();
            return (rowsAffected > 0);
        }

        public static SavedQuery GetQuery(GearsDb gearsDb, int id)
        {
            if (gearsDb == null) { return null; }
            string sqlCommand = "select * from savedqueries where id = ?";
            object[] parameters = new object[1];
            parameters.SetValue(id, 0);
            GearsResultSet rs = new GearsResultSet(gearsDb.Execute(sqlCommand, parameters));

            SavedQuery query = null;
            if (rs.IsValidRow())
            {
                query = new SavedQuery();
                query.id = Convert.ToInt32(rs.GetFieldValue("id"));
                query.name = rs.GetFieldValue("name").ToString();
                query.query = rs.GetFieldValue("query").ToString();
            }
            rs.Close();

            return query;
        }

        public static SavedQuery GetQuery(GearsDb gearsDb, string name)
        {
            if (gearsDb == null) { return null; }

            string sqlCommand = "select * from savedqueries where name = ?";
            object[] parameters = new object[1];
            parameters.SetValue(name, 0);
            GearsResultSet rs = new GearsResultSet(gearsDb.Execute(sqlCommand, parameters));

            SavedQuery query = null;
            if (rs.IsValidRow())
            {
                query = new SavedQuery();
                query.id = Convert.ToInt32(rs.GetFieldValue("id"));
                query.name = rs.GetFieldValue("name").ToString();
                query.query = rs.GetFieldValue("query").ToString();
            }
            rs.Close();

            return query;
        }

        public static bool Delete(GearsDb gearsDb, int id)
        {
            if (gearsDb == null) { return false; }
            string sqlCommand = "delete from savedqueries where id = ?";
            object[] parameters = new object[1];
            parameters.SetValue(id, 0);
            gearsDb.Execute(sqlCommand, parameters);
            int rowsAffected = gearsDb.RowsAffected();
            return (rowsAffected > 0);

        }

    }

 

Comments

Reflective Perspective - Chris Alcock » The Morning Brew #234 said:

Pingback from  Reflective Perspective - Chris Alcock  » The Morning Brew #234

# December 1, 2008 5:50 AM

SilverTrader: Resize of Windows « Tales from a Trading Desk said:

Pingback from  SilverTrader: Resize of Windows « Tales from a Trading Desk

# December 1, 2008 7:02 PM

Craig Sutherland said:

I also have the same problem with IE7.0. After some playing around I fixed out a work-around (although it is a hack).

To the HTML page that contains the Silverlight control I added a javascript function as follows:

function convertArgs(){

  return args;

}

The I call this function first from the Silverlight code-behind to convert the arguments from a .Net array to a javascript array.

Not the best, but at least it now works.

# December 4, 2008 3:55 AM

joeaudette said:

Hi Craig,

Thanks for the suggestion. I'm already doing something similar when passing in an array of params:

ScriptObject jsArray = HtmlPage.Window.Invoke("createJsArray", parameters.Length) as ScriptObject;

               for (int i = 0; i < parameters.Length; i++)

               {

                   HtmlPage.Window.Invoke("assignArray", jsArray, i, parameters[i]);

               }

               //HtmlPage.Window.Invoke("passArrayTest", jsArray);

               return gDb.Invoke("execute", sqlCommand, jsArray) as ScriptObject;

I'm able to execute the sql and get a resultSet object, the problem in IE is when trying to get the field name or value from the resultSet. I've tried a lot of different syntax but I get invalid type or out of range errors in IE 7 using these methods (all of which work in FF):

public string GetFieldName(int fieldIndex)

       {

           try

           {

               // these work with Firefox but not IE7

               // IE7 throws an invalid argument exception

               //return gResult.Invoke("fieldName", parameters).ToString();

               return gResult.Invoke("fieldName", fieldIndex).ToString();

               //return gResult.Invoke("fieldName", new object[]{fieldIndex}).ToString();

               //return HtmlPage.Window.Invoke("getFieldName", new object[]{ gResult, fieldIndex}).ToString();

           }

           catch (Exception ex)

           {

               Logger.LogError("GearsResultSet.GetFieldName", ex);

           }

           return string.Empty;

       }

all of these approaches work in FF but not in IE 7. I tried adding your method to the js and invoking it to create a ScriptObject to pass into getFieldName, but the result is the same.

So I can execute, get a resultset , check isValidRow returns true and I can move through the results with moveNext. Its just the fieldName and field methods that break in IE 7. I can even call fieldByName and it works.

Best,

Joe

# December 4, 2008 10:29 AM