Sunday, December 30, 2007

Calling page methods from javascript using ASP.NET AJAX

 We can call page methods (static methods declared in scope of asp.net page) using MS Ajax framework for ASP.NET. That is how can we do it :

1) Drop ScriptManager control on page
2) Set EnablePageMethods property of ScriptManager control to true

    <asp:ScriptManager 
        EnablePageMethods="true" 
        ID="MainSM" 
        runat="server" 
        ScriptMode="Release" 
        LoadScriptsBeforeUI="true"
        >
    </asp:ScriptManager>
3) Add static public method in page-behind code (or its parent class) and mark it with [System.Web.Services.WebMethod] attribute:

using System;

public partial class _Default : System.Web.UI.Page 
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    [System.Web.Services.WebMethod]
    public static string GetTime()
    {
        return DateTime.Now.ToString();
    }
}
4) Create new JS file and add the following code to it:

/// --------------------------------------------------
/// mainScreen object
/// --------------------------------------------------
var mainScreen = 
{
    resultDivId : "resultDiv",
    resultDiv : null
}

mainScreen.Init = function() {
    /// <summary>
    /// Initializes mainScreen variables
    /// </summary>
    this.resultDiv = $get(this.resultDivId);
};
mainScreen.GetTime = function () {
    /// <summary>
    /// Loads rendered server control from server
    /// </summary>
    PageMethods.GetTime(mainScreen.GetTimeCallback, mainScreen.GetTimeFailed);
};
mainScreen.GetTimeCallback = function (result) {
    /// <summary>
    /// Is called when server sent result back
    /// </summary>
    /// <param name="result">
    /// Result of calling server method, 
    /// string - server time 
    /// </param>
    if(result) {
        mainScreen.resultDiv.innerHTML = result;
    }
};
mainScreen.GetTimeFailed = function (error, userContext, methodName) {
    /// <summary>
    /// Callback function invoked on failure of the page method 
    /// </summary>
    /// <param name="error">error object containing error</param>
    /// <param name="userContext">userContext object</param>
    /// <param name="methodName">methodName object</param>
    if(error) {
        // TODO: add your error handling
        mainScreen.resultDiv.innerHTML = error.get_message();
    }
};



/// --------------------------------------------------
/// Page events processing
/// --------------------------------------------------

Sys.Application.add_load(applicationLoadHandler);
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequestHandler);
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(beginRequestHandler);

function applicationLoadHandler() {
    /// <summary>
    /// Raised after all scripts have been loaded and 
    /// the objects in the application have been created 
    /// and initialized.
    /// </summary>
    mainScreen.Init()
}

function endRequestHandler() {
    /// <summary>
    /// Raised before processing of an asynchronous 
    /// postback starts and the postback request is 
    /// sent to the server.
    /// </summary>
    
    // TODO: Add your custom processing for event
}

function beginRequestHandler() {
    /// <summary>
    /// Raised after an asynchronous postback is 
    /// finished and control has been returned 
    /// to the browser.
    /// </summary>

    // TODO: Add your custom processing for event
}
5) Add reference to js file we just created to ScriptManager control we created in step 1, so it will look like:

    <asp:ScriptManager 
        EnablePageMethods="true" 
        ID="MainSM" 
        runat="server" 
        ScriptMode="Release" 
        LoadScriptsBeforeUI="true"
        >
        <Scripts>
            <asp:ScriptReference Path="~/Scripts/Main.js" />
        </Scripts>
    </asp:ScriptManager>
6) Add div with id "resultDiv" to the page markup"

<div id="resultDiv"></div>
7) Add a button to the page and set it's onclick event to "mainSrceen.GetTime();" value:

<input value="GetTime" type="button" onclick="mainScreen.GetTime();" />
8) Run the page, click on the button and get time from the server.

You can see online example here

Or download source code here

Friday, December 28, 2007

Using abstract class to separate the logic

Problem: 

1) I have an windows app that I am converting into web app.
2) I have DAL in win app that should be changed as little as possible because it is also currently in development. And if it will be supplemented with new methods, classes etc., it should continue working in web app with minimal changes/corrections.
3) Win app data objects in DAL are mainly ok to use in web version, but the mechanism of connecting to database and populating and caching data objects is very different from one I am going to use in web app. The main problem is that every data object has static private variable that points to SqlConnection. That class is responsible for connecting to database, handling connection open/close states, handling transaction flow etc. So main point is that class can not be used in web app at all. 

Simple example of what I am talking about.

Let's say we have a class that is called from win-app and web-app, and which should not (cannot) be changed.
   1:  public class User
   2:  {
   3:      private DataAccess  dataAccess = new dataAccess();
   4:      
   5:      public void Delete(int id)
   6:      {
   7:          dataAccess.Execute("spDeleteUser", new string[]{"@UserID"}, new object[]{id});
   8:      }
   9:  }
  10:   
  11:  And let's say we have DataAccess   class implemented like this:
  12:   
  13:  public class DataAccess
  14:  {
  15:      private static SqlConnection m_Connection = null;
  16:      
  17:      public void Execute(string spName, string[] parameterNames, object[] parameterValues)
  18:      {
  19:          // Here is some implementation
  20:      }
  21:  }

Searching for solution.

Ok, first I thought about simply changing the base data access class and adding to each method of it check block that checks if the method is called from HttpContext, and if so, execute web-specific piece of code, or otherwise win-specific. 
It works, but it leads to hell :) Class is not simply the set of methods, it has some encapsulated data, it has the sequence of execution. Connection in win-app is opened once, whereas in web it is opened and closed per each request. So I started to search more elegant solution, where I could alter entire class logic instead of altering methods, and where I could separate two platform-specific classes into separate files.

So what I did.

I created the following classes (naming conventions I used is taken from providers naming, although I am not using Provider Model, it's very similar approach)

DataAccessProvider .cs:
   1:   
   2:  public abstract class DataAccessProvider 
   3:  {
   4:      public abstract void Execute(string spName, string[] parameterNames, object[] parameterValues);
   5:  }

WindowsDataAccessProvider.cs:
   1:  public abstract class WindowsDataAccessProvider :DataAccessProvider 
   2:  {
   3:      private static SqlConnection m_Connection = null;
   4:   
   5:      public override void Execute(string spName, string[] parameterNames, object[] parameterValues)
   6:      {
   7:          // Windows-forms specific code, if static connection is already opened
   8:          // use it, don’t close connection until application termination
   9:      }
  10:  }
WebDataAccessProvider.cs:
   1:  public abstract class WebDataAccessProvider :DataAccessProvider 
   2:  {
   3:      private SqlConnection m_Connection = null;
   4:   
   5:      public override void Execute(string spName, string[] parameterNames, object[] parameterValues)
   6:      {
   7:          // Web-specific code, open instance of private connection
   8:          // use it, close it after execution
   9:      }
  10:  }
DataAccess.cs:
   1:  public class DataAccess
   2:  {
   3:      private DataAccessProvider _provider;
   4:      
   5:      public DataAccess()
   6:      {
   7:          if(HttpContext.Current==null) // we have code called from Windows app
   8:          {
   9:              _provider = new WindowsDataAccessProvider();
  10:          }
  11:          else // we have code called from Web app
  12:          {
  13:              _provider = new WebDataAccessProvider();
  14:          }
  15:      }
  16:      
  17:      public void Execute(string spName, string[] parameterNames, object[] parameterValues)
  18:      {
  19:          _provider.Execute(spName,  parameterNames, parameterValues);
  20:      }
  21:  }
Note: 
if DataAccess class is inherited from some another class, we should redirect all public methods of parent class to provider as well. This can be a limitation to that model if we have no access to parent class's code.

Overall it worked great in my case. I separated logic in 5 core classes that needed to be changed. Separation is great, all the logic is split into different files, and real implementation of specific classes  is very different for different platforms



Ok and one more note. If we need to load different implementation at run time, depending on configuration information for example, its very simple to change classes I demonstrated to work like that. We just need to inherit DataAccessProvider class from ProviderBase, and add Initialise method's override to WindowsDataAccessProvider and WebDataAccessProvider classes. Then instead of checking HttpContext.Current in constructor of DataAccess class to load provider we just need to use ProvidersHelper.InstantiateProviders method.

Tuesday, December 18, 2007

.NET assembly resource browser

Several days ago I needed to retrieve some resources from .NET assemblies.Currently I am porting .NET windows-forms application to web platform. And as part of the work I need to retrieve image and text resources from existing application to place them in my web application source code. Unfortunately there are some difficulties in doing so even using Visual Studio. For example I could not retrieve image resources from ImageList object. Of course first I opened Reflector , which as I remembered had this feature. But when I opened target dll and started searching for "Export" button.. there was no such a feature there for images inside resource files and ImageLists .. So I decided to waste a hour to write some very simple resource reflector for this purpose. I hope it will be useful for you. So how we start ? Lets declare a class level object of type Assembly for reflection purposes: Assembly assembly = null; Also we need a button on a simple form that will allow us to show OpenDialog object and give a user an opportunity to choose the assembly (exe or dll file) which we will open for reflection. Click event for the button looks like this:
   1:          private void button1_Click(object sender, EventArgs e)
   2:          {
   3:              // ofdAssembly - OpenFileDialog component
   4:              if (ofdAssembly.ShowDialog() == DialogResult.OK)
   5:              {
   6:                  // Load assembly using reflection
   7:                  assembly = Assembly.LoadFrom(ofdAssembly.FileName);
   8:                  // call the method that will retrieve all the information
   9:                  // about resources inside our assembly
  10:                  ShowInfo();
  11:              }
  12:          }
Before we continue lets drop on the form TreeView component and call it tvResources ShowInfo Method looks like this:
   1:          private void ShowInfo()
   2:          {
   3:              // Just clean up the tree (if it contains any nodes from
   4:              // previously selected assembly)
   5:              tvResources.Nodes.Clear();
   6:              //check if assembly is assigned
   7:              if (assembly != null)
   8:              {
   9:                  // yes unfortunately I did not found way better than this,
  10:                  // we need to use exception try catch blocks to find out
  11:                  // what resource exactly we are talking to
  12:                  // see next, resource list is just list of strings so we
  13:                  // need to find the type ourselves
  14:                  // and in the case of exception try next type, I hope you
  15:                  // will reply if you know better way
  16:                  try
  17:                  {
  18:                      // set the status label text to point full name of an
  19:                      // assembly
  20:                      lbStatus.Text = assembly.FullName;
  21:                      // enumerate all resources into array of strings
  22:                      string[] resourceNames = assembly.GetManifestResourceNames();
  23:                      // iterate through retrieved list
  24:                      foreach (string resourceName in resourceNames)
  25:                      {
  26:                          // create default "empty" treenode which will stay
  27:                          // the way we set it here if we cannot resolve its
  28:                          // type farther
  29:                          TreeNode tn = new TreeNode(resourceName);
  30:                          tn.ImageIndex = 0;
  31:                          tvResources.Nodes.Add(tn);
  32:                          // create ResourceStream object from current string
  33:                          // representation of resource name
  34:                          // I used IDisposable pattern here to make sure it
  35:                          // is disposed after use
  36:                          using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName))
  37:                          {
  38:                              try
  39:                              {
  40:                                  // try to create ResourceSet object from
  41:                                  // current stream
  42:                                  // it represents simple resource file in
  43:                                  // real VS project
  44:                                  ResourceSet resources = new ResourceSet(resourceStream);
  45:                                  IDictionaryEnumerator enu = resources.GetEnumerator();
  46:                                  //  if we succeeded and exception wasnot
  47:                                  // caught then we are on right way -
  48:                                  // so just enumerate through separate
  49:                                  // resources inside ResourceSet
  50:                                  while (enu.MoveNext())
  51:                                  {
  52:                                      // Create child nodes (second level) and
  53:                                      // assign them name from resource key
  54:                                      TreeNode childNode = new TreeNode(enu.Key.ToString());
  55:                                      tn.Nodes.Add(childNode);
  56:                                      // set default image for node, if
  57:                                      // resolve it type next, we will change it to
  58:                                      // correspond to the type
  59:                                      childNode.ImageIndex = 0;
  60:                                      // current value of resource object of
  61:                                      // course has GetType method
  62:                                      // which will allow us to resolve type
  63:                                      // of current resource
  64:                                      switch (enu.Value.GetType().Name)
  65:                                      {
  66:                                              // resource is Icon
  67:                                          case "Icon":
  68:                                              // Path the Icon value into
  69:                                              // treeNode's tag and set corresponding image to the node
  70:                                              childNode.Tag = enu.Value;
  71:                                              childNode.ImageIndex = 2;
  72:                                              childNode.SelectedImageIndex = 2;
  73:                                              break;
  74:                                              // resource is bitmap
  75:                                          case "Bitmap":
  76:                                              // path the value to the node
  77:                                              childNode.Tag = enu.Value;
  78:                                              // set the same image as we did
  79:                                              // for the Icon - it is image again
  80:                                              childNode.ImageIndex = 2;
  81:                                              childNode.SelectedImageIndex = 2;
  82:                                              break;
  83:                                              // Resource is ImageList
  84:                                          case "ImageListStreamer":
  85:                                              // create ImageList object from
  86:                                              // stream
  87:                                              ImageList il = new ImageList();
  88:                                              il.ImageStream = (ImageListStreamer)enu.Value;
  89:                                              // set the image to the node to
  90:                                              // differentiate it from another types
  91:                                              childNode.ImageIndex = 3;
  92:                                              childNode.SelectedImageIndex = 3;
  93:                                              int ind = 0;
  94:                                              // enumerate through the images
  95:                                              // in the list and create new level of nodes under current one
  96:                                              foreach (Image img in il.Images)
  97:                                              {
  98:                                                  // create subnodes and pass
  99:                                                  // Image objects to Tag property
 100:                                                  TreeNode grandchildNode = new TreeNode(ind.ToString());
 101:                                                  childNode.Nodes.Add(grandchildNode);
 102:                                                  grandchildNode.Tag = img;
 103:                                                  grandchildNode.ImageIndex = 2;
 104:                                                  grandchildNode.SelectedImageIndex = 2;
 105:                                                  ind++;
 106:                                              }
 107:                                              break;
 108:                                          // resource is String
 109:                                          case "String":
 110:                                              childNode.Tag = enu.Value;
 111:                                              childNode.ImageIndex = 1;
 112:                                              childNode.SelectedImageIndex = 1;
 113:                                              break;
 114:                                      }
 115:                                  }
 116:                              }
 117:                              catch (Exception ex)
 118:                              {
 119:                                  // if we are here most probably we have a
 120:                                  // problem creating ResourceSet object,
 121:                                  // so try to create more needed and possible
 122:                                  // types from strings
 123:                                  try
 124:                                  {
 125:                                      // try to create an Image object from
 126:                                      // resource
 127:                                      Image img = new Bitmap(resourceStream);
 128:                                      tn.Tag = img;
 129:                                      tn.ImageIndex = 2;
 130:                                      tn.SelectedImageIndex = 2;
 131:                                  }
 132:                                  catch (Exception exInner)
 133:                                  {
 134:                                      // we cannot (dont need) find out what
 135:                                      // is the type of current resource,
 136:                                      // so just read stream as text and show
 137:                                      // it to the user when he clicks on the node.
 138:                                      // (we pass it to Tag property which
 139:                                      // will hold this value for later use)
 140:                                      try
 141:                                      {
 142:                                          using (TextReader tr = new StreamReader(resourceStream))
 143:                                          {
 144:                                              tn.Tag = tr.ReadToEnd();
 145:                                              tn.ImageIndex = 0;
 146:                                              tn.SelectedImageIndex = 0;
 147:                                          }
 148:                                      }
 149:                                      catch (Exception exTextInner)
 150:                                      {
 151:                                          ; // we ignore errors for now
 152:                                      }
 153:                                  }
 154:                              }
 155:                          }
 156:                      }
 157:                  }
 158:                  catch (Exception ex)
 159:                  {
 160:                      // if we have got an error during calling
 161:                      // GetManifestResourceNames method,
 162:                      // show the error in status label
 163:                      lbStatus.Text = ex.Message;
 164:                  }
 165:              }
 166:              else
 167:              {
 168:                  lbStatus.Text = "";
 169:              }
 170:          }
Also we need to show the contents of each resource to the user. This happens when user clicks on the node. So the AfterSelect event looks like:
   1:          private void tvResources_AfterSelect(object sender, TreeViewEventArgs e)
   2:          {
   3:              ShowOneObject(e.Node.Tag, e.Node.Text);
   4:          }
ShowOneObject method is the following:
   1:          // declare m_filename variable it will be useful when exporting
   2:          // image resources
   3:          string m_filename = "";
   4:          private void ShowOneObject(object obj, string name)
   5:          {
   6:              // set TextBox (tbValue), PictureBox (pbValue)to initial
   7:              // visibility states (hidden),
   8:              // so we will show them if node with corresponding type was clicked
   9:              tbValue.Visible = false;
  10:              pbValue.Visible = false;
  11:              // set "Export" button's Visible property to false, we show it
  12:              // only for images, for strings user can copy the text 
  13:              // right to the clipboard
  14:              btnExport.Visible = false;
  15:              m_filename = "";
  16:              // quick and dirty way to make test tool app not to stay on our
  17:              // way when we want just to do a simple task :)
  18:              // we need just images, we dont try to write a Reflector !
  19:              try
  20:              {
  21:                  // check if passed Tag property of node has value at all
  22:                  if (obj != null)
  23:                  {
  24:                      // different actions for different types
  25:                      switch (obj.GetType().Name)
  26:                      {
  27:                              // Object is Icon - so show picturebox and
  28:                              // convert icon to bitmap to show it on the screen
  29:                          case "Icon":
  30:                              pbValue.Visible = true;
  31:                              pbValue.Dock = DockStyle.Fill;
  32:                              pbValue.Image = ((Icon)obj).ToBitmap();
  33:                              btnExport.Visible = true;
  34:                              // set filename so if we export this moment, we
  35:                              // will get the icon file with original resource's key name
  36:                              m_filename = String.Format("{0}.ico", name);
  37:                              break;
  38:                              // if object is Image or Bitmap - the action it
  39:                              // the same - show it in PictureBox component
  40:                          case "Image":
  41:                          case "Bitmap":
  42:                              pbValue.Visible = true;
  43:                              pbValue.Dock = DockStyle.Fill;
  44:                              pbValue.Image = (Image)obj;
  45:                              btnExport.Visible = true;
  46:                              // set name of file to be exported
  47:                              m_filename = String.Format("{0}.bmp", name);
  48:                              break;
  49:                              // object is string - just show the multiline
  50:                              // TextBox with tecxt from resource
  51:                          case "String":
  52:                              tbValue.Visible = true;
  53:                              tbValue.Dock = DockStyle.Fill;
  54:                              tbValue.Text = obj.ToString();
  55:                              break;
  56:                      }
  57:                  }
  58:              }
  59:              catch (Exception ex)
  60:              {
  61:                  tbValue.Visible = true;
  62:                  tbValue.Dock = DockStyle.Fill;
  63:                  tbValue.Text = ex.Message;
  64:              }
  65:          }
Ok and only one thing left is to create event handler for Click event of our btnExport button:
   1:          private void btnExport_Click(object sender, EventArgs e)
   2:          {
   3:              // set filename property of SaveFileDialog object
   4:              sfdAssembly.FileName = m_filename;
   5:              if (sfdAssembly.ShowDialog() == DialogResult.OK)
   6:              {
   7:                  // just save the resource to the file
   8:                  pbValue.Image.Save(sfdAssembly.FileName);
   9:              }
  10:          }
So we did it, - the quick and dirty application for retrieving resources from managed assemblies. Completed application looks like this : Hope this helps, Kirill