A Completely Dynamic View Model

by Administrator 29. December 2011 11:43

Recently, I was working on a project where there was a requirement to print certain documents.  These documents were stored, in pieces, in the database and then loaded and assembled at runtime.  This allowed parts of the documents to be reused as needed.

The Xaml for these documents contained bindings to data that originated from the database.  The usual drill would be to statically define all the required properties on the view model and then write code to populate them from the database.  There's nothing wrong with this approach, but the project was in a mode where new documents were being generated quickly and they required new properties to be defined on the view model making the view model code a hot spot.

The data required in the bindings was largely stored as name/value pairs in the database.  Wouldn't it be much nice if new values added to the database just showed up at runtime without having to create new properties and the code to populate them?

Using dynamic objects would be a great way to go, if you could bind to them.  Unfortunately, they don't support reflection which is required for binding.  A second problem is that adding properties to a dynamic object requires a syntax like this:

Connect to XMPP
  1. dynamic vm = new object();
  2.             vm.PropertyName = 5;

Which, of course, requires you to know all property names to be added, ahead of time.  Since I'd like to be able to add properties that I find in the database, this is unsuitable.

I did some research and found this blog post by Lester Lobo.  The solution accompanying the post contained this class.

Connect to XMPP
  1. using System.Collections.Generic;
  2. using System.Collections.ObjectModel;
  3. using System.ComponentModel;
  4. using System.Dynamic;
  5. using System.Windows.Data;
  6. using System;
  7.  
  8. namespace DynamicVM
  9. {
  10.     public class DynamicObjectClass : DynamicObject, INotifyPropertyChanged
  11.     {
  12.         #region DynamicObject overrides
  13.  
  14.         public DynamicObjectClass()
  15.         {
  16.         }
  17.  
  18.         public override bool TryGetMember(GetMemberBinder binder, out object result)
  19.         {
  20.             return members.TryGetValue(binder.Name, out result);
  21.         }
  22.  
  23.         public override bool TrySetMember(SetMemberBinder binder, object value)
  24.         {
  25.             members[binder.Name] = value;
  26.             OnPropertyChanged(binder.Name);
  27.             return true;
  28.         }
  29.  
  30.         public override IEnumerable<string> GetDynamicMemberNames()
  31.         {
  32.             return members.Keys;
  33.         }
  34.  
  35.         public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
  36.         {
  37.             int index = (int)indexes[0];
  38.             try
  39.             {
  40.                 result = itemsCollection[index];
  41.             }
  42.             catch (ArgumentOutOfRangeException)
  43.             {
  44.                 result = null;
  45.                 return false;
  46.             }
  47.             return true;
  48.         }
  49.  
  50.         public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
  51.         {
  52.             int index = (int)indexes[0];
  53.             itemsCollection[index] = value;
  54.             OnPropertyChanged(System.Windows.Data.Binding.IndexerName);
  55.             return true;
  56.         }
  57.  
  58.         public override bool TryDeleteMember(DeleteMemberBinder binder)
  59.         {
  60.             if (members.ContainsKey(binder.Name))
  61.             {
  62.                 members.Remove(binder.Name);
  63.                 return true;
  64.             }
  65.             return false;
  66.         }
  67.  
  68.         public override bool TryDeleteIndex(DeleteIndexBinder binder, object[] indexes)
  69.         {
  70.             int index = (int)indexes[0];
  71.             itemsCollection.RemoveAt(index);
  72.             return true;
  73.         }
  74.  
  75.         #endregion DynamicObject overrides
  76.  
  77.         public void AddProperty(string propertyName, object value)
  78.         {
  79.             members[propertyName] = value;
  80.         }
  81.  
  82.         #region INotifyPropertyChanged
  83.  
  84.         public event PropertyChangedEventHandler PropertyChanged;
  85.  
  86.         private void OnPropertyChanged(string propertyName)
  87.         {
  88.             if (PropertyChanged != null)
  89.                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  90.         }
  91.  
  92.         #endregion INotifyPropertyChanged
  93.  
  94.         #region Public methods
  95.  
  96.         public object AddItem(object item)
  97.         {
  98.             itemsCollection.Add(item);
  99.             OnPropertyChanged(Binding.IndexerName);
  100.             return null;
  101.         }
  102.  
  103.         #endregion Public methods
  104.  
  105.         #region Private data
  106.  
  107.         Dictionary<string, object> members = new Dictionary<string, object>();
  108.         ObservableCollection<object> itemsCollection = new ObservableCollection<object>();
  109.  
  110.         #endregion Private data
  111.     }
  112.  
  113. }

This class was almost just what I needed.  It solved the problem of not being able to bind to a dynamic, but I still needed a way to add properties on the fly.  For this, I added the AddProperty() method, which you'll see in the above listing.

Using the class is easy.  Below is an example.

Connect to XMPP
  1. AValue = new DynamicObjectClass();
  2.             AValue.Foo = "Hello"; //use the out-of-the-box syntax for adding a property
  3.  
  4.             AValue.AddProperty("Bar", 5); //add a property discovered at runtime.
  5.             AValue.AddProperty(propName, propValue);

So, using the above, I can simply read the name/value pairs out of the database and then add each to my view model at  runtime.  This means that any new value added to the database will automatically be available on the now dynamic view model for binding.

One Offs

Of course, you don't have to use a completely dynamic view model.  It's just as easy to define properties at design time and have one of those properties be a dynamic type.  Also, it's possible to have a dynamic view model that contains a property that is, itself, a dynamic.

Tags: , , ,

.Net | Database | WPF | dynamic

ActiveX Killbits Problem with Reporting Services Printing

by mgordon 13. February 2009 08:01

We just found and corrected a problem with printing from the reporting services web interface.  When the user clicked the print button, they received a popup message saying ""Unable to load client print control”.  As we found out, the problem was created when the users’ desktops installed a hotfix from Microsoft, KB956391.  We had two choices upon finding this out; we could remove the patch from every workstation or install a subsequent hotfix to Sql Server.  We opted for the latter and installed the hotfix located here.  Printing seems to work perfectly, now.

Tags:

Database | Sql Server 2005

Linq Result to DataSet - Revisited

by mgordon 23. April 2008 06:57

Some time ago, I posted a link to an article explaining how to obtain a DataSet from the result set of a Linq query.  I've continued to look for elegant, natural solutions to this problem and I've found one that appeals to me more, here.This solution, similar to the last, uses extension methods to get the job done, but I like the way this one is implemented better.

Tags:

.Net | Database | Linq | Sql Server 2005

Common Table Expressions and Recursion

by mgordon 12. November 2007 04:48

Some time ago, I was working on a project that needed to work with users as a hierarchy.  The hierarchy was similar to what you would see in typical organizational chart and the users were organized by who they worked for and who worked for them.  As part of one of the requirements for the system, I needed to be able to retrieve from the database any particular user and all users who were placed beneath them in the chart, from their position in it all the way to the bottom.

To store the relationship between all the users, I created a self referencing table that contained user_id and managing_user_id columns.  The idea was that the record would contain the id of a user and also the id of the user over them.  If the user was at the top of the hierarchy, the managing_user_id column would be null.

Obviously, getting the data out of the table in the way I needed it could best be done with recursion.  I needed to get the user, all records that pointed to that user as the managing user...all the records that pointed to each of those records and so on.  I was wondering how to do this without making multiple calls to the database, however, because I could see the number of calls being astronomical for users near the top of the hierarchy.

Then I ran across a technique whereby I could use a CTE in a  recursive way.  The code looked something like this.

      WITH userHierarchyTiers (user_id, managing_user_id, user_hierarchy_id) AS
      (
           SELECT uh.user_id, managing_user_id, user_hierarchy_id
           FROM user_hierarchy uh, users u
           WHERE uh.user_id = @UserId
           AND u.user_id = uh.user_id

           UNION ALL

           SELECT uh.user_id, uh.managing_user_id, uh.user_hierarchy_id
           FROM user_hierarchy uh 
           INNER JOIN userHierarchyTiers uht ON uh.managing_user_id = uht.user_id
      )
      SELECT user_id, managing_user_id from userHierarchyTiers

The key to this technique is in the fact that the second query in the CTE references the entire CTE in its FROM clause.  The net effect is that the CTE is executed and for each row in the result, the CTE is executed again and so on until there are no rows in the result set.

Tags: ,

Sql Server 2005 | Database

Ruby in Practice

by mgordon 25. October 2007 08:32

In my current contract, it's the typical story with compressed timelines and pulling off miracles, so I haven't had the opportunity to make use of Ruby for a "real" production application.  However, we're in the midst of doing some work in migrating data from a legacy system into a completely new database schema.  All imagined challenges apply, I assure you.  Part of this exercise is repeatedly bouncing the data against the new schema, applying validations to the data to make sure all the new referential integrity holds true in the new system and reporting on data that isn't fitting into the new system properly.  I took this as an opportunity to apply some Ruby scripting as a means of automating this part of the project.

 Having played with the DBI library a bit before starting, I knew that I wanted to abstract some of the interaction with it away so I wrapped it like so.

  ########## Class to abstract the server connection ##########
  class SqlServer
   
    def initialize(connectionString)
        @dbh=DBI.connect("DBI:ADO:Provider=SQLOLEDB;" + connectionString)
        @dbh["AutoCommit"] = true
    end
   
    def execute(sql)
        statement = @dbh.prepare(sql)
        statement.execute
    end
   
    def fetch(sql)
        statement = @dbh.execute(sql)
        statement
    end
   
    def disconnect
        @dbh.disconnect
    end
  end

I needed to be able to pull fresh data from other databases into my conversion database, so I abstracted that task, as well.

 ########## Class to manage pulling data from one db to another ##########
 class Pull
    def initialize(tableName)
        @table = tableName
        @destination = SqlServer.new("Data Source=server1;initial catalog=database1; trusted_connection=yes")
        @source = SqlServer.new("Data Source=server2;initial catalog=database2; trusted_connection=yes")
    end
   
    def CopyTable
       
        @destination.execute("TRUNCATE TABLE " + @table)
               
        statement = @source.fetch("select * from " + @table)
       
        statement.fetch do |row|
            columns = ""
            values = ""
            insert = ""
           
           
            i = 0
            statement.column_names.each do |name|
                columns += "," + name if columns.length > 0
                columns += name if columns.length == 0
                if row[i].nil?
                    values += ", NULL" if values.length > 0
                    values += "NULL" if values.length == 0
                else
                    values += ", '" + row[i].to_s.gsub("\\", "").gsub("'", "''") + "'" if values.length > 0
                    values += "'" + row[i].to_s.gsub("\\", "").gsub("'", "''") + "'" if values.length == 0
                end
                i = i + 1
            end
           
            columns = "(" + columns + ")"
            values = "values(" + values + ")"
            begin
                insert = "insert into " + @table + " " + columns + " " + values
                @destination.execute(insert)
            rescue DBI::DatabaseError => e
                puts "An error occurred"
                puts "Error code: #{e.err}"
                puts "Error message: #{e.errstr}"
                puts "Error SQLSTATE: #{e.state}"

                puts insert
                exit
            end
        end
        statement.finish
        @destination.disconnect
        @source.disconnect
    end
 end

In my case, all the columns were varchar, so I didn't have to fuss with the format of the data in the insert statements.  With DBI, however, it's possible to get the meta data about each column to decide whether the data needs to be quoted or not.  Now, I needed to be able to pull data about how the load went and any exceptions that were encountered.  That data needed to be saved into CSV files so they could be attached to emails.  The next class was written to do this.

 ########## Class to pull statistic about the load into csv files ##########
 class StatFile
 
    def initialize(connString)
        @server = SqlServer.new(connString)
    end
 
    def GetStats(filePath, sql)
        statement = @server.fetch(sql)
       
        output = File.open(filePath, "w")
       
        statement.fetch do |row|
            output.puts row.to_a.join(",")
        end
       
        output.close
    end
 end

 With the above classes, I was able to script the tasks at hand something like this.

Pull.new("tableName").CopyTable #Copies the table from one db to another

 conversion = SqlServer.new("Data Source=server;initial catalog=database; trusted_connection=yes")
 conversion.do("EXEC ConversionSproc")

 stat.Getstats("c:\\Stat_Files\\exceptions.csv", "select foo1, foo2 from exception_table where error='bad' group by description")

As I've stated before, I'm not planning to use Ruby to put food on the table at this point, but I would like to get some experience using the language if only to get the chance to think about things differently.

 UPDATE:
After using the above scripts a bit, I found that when executing some rather long running stored procedures, I was receiving a timeout.  It was then that I realized there was not way to set the CommandTimout property on the database connection through DBI.  After hours of searching, I was able to gleen enough information to overcome the problem.  I added the following line to the bottom of the initialize method of the SqlServer class:

  @dbh.handle.instance_variable_get(:@handle).setproperty('CommandTimeout', 7200)

Of course, you can specify whatever value you like for the timeout or even parametize the value.

Tags: ,

Productivity | Sql Server 2005 | Database | Ruby

Bitten by Escaping Single Quotes

by mgordon 17. August 2007 11:55

I've known about it and handled it well for years.  You know...when you have a string with a single quote embedded in it and you need to insert the string into a database column.  For the longest time, I simply replaced any quotes with two quotes and all was well.  ADO.Net was smarter than I gave it credit for, however, and it apparently hung me up.

 I've been working on a brand new client-server application for the past several months.  I created the dal that was to be used by the entire application and it had been working well.  We just started allowing some user acceptance testing and it happened.  A user keyed in a string with an apostrophe in it and the application thre an exception.  "No problem", I thought, "I'll just replace any quotes with two quotes and I'm on my way".

One of the developers on this project approached me,  shortly after,  and pointed out that quotes I was doubling in the dal were, in fact, being saved in the database as double quotes.  Huh?  After two hours of debugging and tweaking I figured out the problem.  I found a post on Scott Gutherie's blog that pointed out that the SqlParameter class automatically takes care of escaping single quotes.   So why was it seemingly ignoring my quotes?

Turns out that the problem laid with the creation of the actual parameters.  I had created an interface and my Dal class implemented it.  Besides the other benefits to ding this, I wanted to be able to write specific implementations for different databases and be able to switch them out, if needed, without changing any of the application code.  So, necessarily, all the application code was written against the interface and not the specific dal class.  A side effect of this was that the code that called into the dal could not create specific parameter clases such as SqlParameter, but rather it asked the dal to create a parameter specific to the database the class was written for, and the parameter was stored in a variable of type  IDataParameter.

Long story short, the dal contained a method called CreateParameter that looked like this

Public Function CreateParameter(ByVal name As String, ByVal value As Object) As IDataParameter
   
Implements IDAL.CreateParameter
   
   
Return New SqlParameter(name, value)
End Function 'CreateParameter

Note that this method does not specify a DBType for the parameter.  Apparently, the lack of a type confused the SqlParameter class.  I modified the dal to check each parameter being passed in to see if it was a string type.  If it was, I manually set it to a DBType of Varchar.  This worked, in my case, because our standard was to use varchars for all string data types unless a char made more sense.  At any rate, this modification allowed me to remove the code that manually escaped the single quote.  The SqlParameter class now had all it needed to handle the situation on its own.

Tags: , ,

.Net | Sql Server 2005 | Database

The DAL is a click away

by mgordon 11. June 2007 11:00
I recently started working on a personal project and decided to give Subsonic a try at the same time.  Subsonic is a DAL generation tool and it simply rocks!  I spent maybe an hour or so watching the helpful videos provided on the site to learn the basics of setting the tool up, configuring it in the .config file and wiring it up to visual studio as an external tool.  After it was set up, I was generating data transport/business classes and data access code from my database in no time.   Subsonic generates business objects and collections that know how to retrieve by criteria and how to save themselves.  Stored procedures are supported, as well.  While I'm not yet a big fan of the query tool, it can be used to generate queries in code without resorting to managing dynamic sql as strings.  I tend to be old school in some ways and still prefer to use stored procedures to manipulate database.  Even so, Subsonic offers me the ability to call any stored procedure on my database with a single line of code.  It supports Sql Server, MySql and Oracle. Again, I love being more productive.  Subsonic helps me be as productive as possible by relieving me of having to write as much code.

Tags: , ,

.Net | Productivity | Database

About the author

Mitch Gordon lives and works in the great state of Georgia.

RecentPosts

Month List