Contact Me

Contact Us
For an informal discussion

Everyone's business is different. Our business is software - ask where we can help.
eMail: john at appsolo.com

Archive for August, 2011

Configuring Deployment of Silverlight

Wednesday, August 17th, 2011

It is sometimes necessary to configure Silverlight differently especially when using WCF services. I read this useful post by Mohamed Ibrahim Mostafa on how to configure the ServiceReferences.ClientConfig XML file to cater for different deployment environments. I found this most useful. As the post suggests the the XAP contains references to the ServiceReferences.ClientConfig XML file. But be warned when you are deploying to a host site the Client Bin folder (from which the XAP file is downloaded) must also have a copy  of ServiceReferences.ClientConfig in it with the same references. Don’t really know why as it should be bundled in the XAP and don’t really know how I would be expected to guess that but I did. You must also remember to rebuild with the changes to the ServiceReferences.ClientConfig file prior to deployment to the Host site.

For local development

 

<configuration>
  <appSettings>
    <add key="Host" value="http://localhost:1420/" />
    <add key="FileServiceEndPoint" value="http://localhost:1420/" />
    <add key="ServerContentFolder" value="LocusPackages" />
    <!--<add key="Host" value="http://Host.com/FTPFileServer/" />
    <add key="FileServiceEndPoint" value="http://Host.com/FTPFileServer/" />
    <add key="ServerContentFolder" value="LocusPackages" />-->

  </appSettings>
</configuration>

For Host deployment 1) rebuild and deploy 2) keep this copy of ServiceReferences.ClientConfig  on the Host Client Bin folder

<configuration>

  <
appSettings>

    <!–
<add key="Host" value="http://localhost:1420/" />

    <add key="FileServiceEndPoint" value="http://localhost:1420/" />

    <add key="ServerContentFolder" value="LocusPackages" />–>       
    <
addkey="Host" value=http://Host.com/FTPFileServer/ />

    <
addkey="FileServiceEndPoint" value=http://Host.com/FTPFileServer/ />

    <
addkey="ServerContentFolder" value="LocusPackages" />

   
  </
appSettings>

</
configuration>

WCF V ASP services for FTP access from Silverlight 4 Part 2

Wednesday, August 17th, 2011

Apologies as this Post is not as detailed as I would like it to be but time pressure dictates. Last time I discussed using a HTTP generic handler to handle the uploading of local files to a FTP server. In this Post I am going to discuss the WCF service to report back on the progress of the uploads to the eventual destination. I use ASP HTTP generic Handlers for file uploads as they do not time out as readily as WCF web services. This is an observational point of view. I don’t see much discussion on the matter of suitability so any comments would be welcome. Below is the Architecture of Our latest app ( I’ve left out the RIA services <-> Database part for simplicity).

image thumb WCF V ASP services for FTP access from Silverlight 4 Part 2

To my mind and in what I know of WCF web services they are suited for short message exchange between web clients and the services. Opinions?

So we use them here to monitor the Asynchronous delivery of local files from a Silverlight client to the FTP server VIA the HTTP generic handler.

I used Duplex Polling for this purpose. The method is based on the WCF fire starter video series on channel 9 and this a video by Tomasz Janczuk on Duplex communication Duplex Communication with WCF in Silverlight 4.

The second video explains the process well. The hardest part of WCF services is in getting the message timing right. The WCF service queries the FTP server and sends message back to the Silverlight 4 client that has subscribed to the the service. Time is a bit tight. So if anyone wants me to post details then lets hear from you and I will. A bit busy at the moment. Later.

P.

WCF v ASP services for FTP access from Silverlight 4 Part 1

Thursday, August 11th, 2011

We  at Appsolo have been charged with writing a SL 4 app that requires access to an FTP server  for a CRM system that we are writing. The system will maintain licences for Flash based software products  our business client produces at the moment. This package of software artefacts are uploaded by our SL client to an FTP server. A HTTP link to the  licenced package is then produced to be emailed to the customers of our business client. This presents a number of  problems that had to be solved.

Firstly you cannot access FTP directly from a SL client as the ports used are not available to the browser.

Secondly the SL client runs in a UI thread, ASP service(s) runs in another thread(s). I have two HTTP based ASP services. A first to create an FTP directory to place the files in. Upload speed needs to be addressed. A second ASP HTTP service to handle the streamed upload of the file. The timing of Threading of all these needs to handled.

Thirdly is the problem of reporting back to the SL client that the File has been delivered to the FTP server having first stopped off at the ASP service on the way. A multi streamed upload HTTP service as is used here is quick as it threads to handle requests. But you cannot access the HTTP response from the SL client as it is write and not a read. Also you do not want to tie up the client waiting for the slower FTP service to complete as called from the ASP service. I integrated a WCF Duplex Polling Service handle the polling of the FTP directory to report on the Files delivered and to decide when the upload is finished.

I’ll discuss things here that I found out in the course of bring this all together.

So part of the solution is to use an ASP based HTTP file streaming (WCF is not really built for this  type of service – although it can be done) service to upload the files to a staging area Directory on the host website. We also needed to create a directory to store the files in on the FTP Server. This is where it starts to get tricky. You want to make sure that the directory is created before you start sending Files to it (or check if it exists already). So on the SL client your have to have nested delegates

NOTE: there a lot of different ideas mixed in here that I got from various people out there, some of which I cannot remember. I’ll list all the references at the end of the posts.

   1:  // Host value is set in ServiceReferences.ClientConfig
   2:                  string hostbase = getAppSetting("Host");
   3:                  UriBuilder packagehandlerUrl = new UriBuilder(hostbase + "FTPSetupPackageFolder.ashx");
   4:                  string FTPDirName = "FTPpath=" + PackageTextBox.Text;
   5:                  packagehandlerUrl.Query = FTPDirName;
   6:                  WebClient FTPCreateDir = new WebClient();
   7:                  // Call asp handler
   8:                  FTPCreateDir.OpenReadAsync(packagehandlerUrl.Uri);
   9:                  // Read the response sent back from the packagehandlerUrl call
  10:                  FTPCreateDir.OpenReadCompleted += (read, ev) =>
  11:                  {
  12:                      int len = (int)ev.Result.Length;
  13:                      byte[] b = new byte[len];
  14:                      ev.Result.Read(b, 0, len);
  15:                      // Check the return message to see if directory already exists and report
  16:                      // Could put choice here
  17:                      if(len == 0)
  18:                          MessageBox.Show("Directory already exists ");
  19:                      else MessageBox.Show("Directory Created " + System.Text.UTF8Encoding.UTF8.GetString(b,0,len) );
  20:                      txtMessage.Text = "FTP Uploading........";
  21:                      // Kick off FTP service Listing operation See end of this file
  22:                      FtpListing();
  23:                      // Do Upload
  24:                      #region HTTP Handled Streamed Upload
  25:                      foreach (FileInfo file in filesToUpload)
  26:                      {
  27:                          // This section uses Asynchronous streamed uploading to upload the selected Files
  28:                          UriBuilder FilehandlerUrl = new UriBuilder(hostbase + "UploadFileHandler.ashx");
  29:                          string InputFile = "InputFile=" + file.Name;
  30:                          FilehandlerUrl.Query = InputFile + "&" + FTPDirName;
  31:   
  32:                          string fileName = file.Name;
  33:                          FileStream FsInputFile = file.OpenRead();
  34:                          WebClient webClient = new WebClient();
  35:                          //Now make an async class for writing the file to the server
  36:                          //Here I am using Lambda Expression
  37:                          webClient.Encoding = System.Text.UTF8Encoding.UTF8;
  38:                          webClient.OpenWriteCompleted += (sent, evt) =>
  39:                          {
  40:                              try
  41:                              {
  42:                                  // Do the actual streamed Upload which goes off on it's merry way
  43:                                  // and we continue
  44:                                  UploadFileData(FsInputFile, evt.Result);
  45:                                  evt.Result.Close();
  46:                                  FsInputFile.Close();
  47:                              }
  48:                              catch (Exception ex)
  49:                              { MessageBox.Show(ex.Message); }
  50:   
  51:                          };
  52:                          webClient.OpenWriteAsync(FilehandlerUrl.Uri);
  53:   
  54:                      }
  55:                      #endregion
  56:                  };

 

Lines 2 and 3 retrieve information from the ServiceReferences.ClientConfig file that is a handy place for storing Host based information which can be changed at deployment time after development and testing.

The contents if the ServiceReferences.ClientConfig XML file are something like this

   1:  <configuration>
   2:    <appSettings>
   3:      <add key="Host" value="http://[YourHost]/FTPFileServer/" />
   4:      <add key="FileServiceEndPoint" value="http://[YourHost or DifferentHost]/FTPFileServer/" />
   5:    </appSettings>
   6:  </configuration>

The associated Function to read this configuration file

   1:  private string getAppSetting(string strKey)
   2:          {
   3:              string strValue = string.Empty;
   4:              XmlReaderSettings settings = new XmlReaderSettings();
   5:              settings.XmlResolver = new XmlXapResolver();
   6:              XmlReader reader = XmlReader.Create("ServiceReferences.ClientConfig");
   7:              reader.MoveToContent();
   8:              while (reader.Read())
   9:              {
  10:                  if (reader.NodeType == XmlNodeType.Element && reader.Name == "add")
  11:                  {
  12:                      if (reader.HasAttributes)
  13:                      {
  14:                          strValue = reader.GetAttribute("key");
  15:                          if (!string.IsNullOrEmpty(strValue) && strValue == strKey)
  16:                          {
  17:                              strValue = reader.GetAttribute("value");
  18:                              return strValue;
  19:                          }
  20:                      }
  21:                  }
  22:              }
  23:              return strValue;
  24:          }
  25:      }

Lines 5 to 20 call and read the response from FTPSetupPackageFolder.ashx to find out if the Directory existed or not.

The ASHX Service to handle this call on the server is….

public void ProcessRequest(HttpContext context)
        {
            string FtpRootDir = context.Request.QueryString["FTPpath"].ToString();
            string FtpBaseAddress = null;
            string FtpUserName = null;
            string FtpPassword = null;
            if (System.Configuration.ConfigurationManager.AppSettings["FtpBaseFolder"] != null)
                FtpBaseAddress = System.Configuration.ConfigurationManager.AppSettings["FtpBaseFolder"].ToString();
            //Get FTP Credential settings
            if (System.Configuration.ConfigurationManager.AppSettings["FtpUserName"] != null)
                FtpUserName = System.Configuration.ConfigurationManager.AppSettings["FtpUserName"].ToString();
            if (System.Configuration.ConfigurationManager.AppSettings["FtpPassword"] != null)
                FtpPassword = System.Configuration.ConfigurationManager.AppSettings["FtpPassword"].ToString();

            NetworkCredential credentials = new NetworkCredential(FtpUserName, FtpPassword);
            string DirName = FtpBaseAddress;
            string WorkingDirectory = DirName + FtpRootDir + "/";
            if (!FtpDirectoryExists(credentials, WorkingDirectory))
            {
                FtpWebRequest setupDir = (FtpWebRequest)WebRequest.Create(WorkingDirectory);
                setupDir.Method = WebRequestMethods.Ftp.MakeDirectory;
                setupDir.Credentials = credentials;
                setupDir.Timeout = -1; // Don't timeout for Debugging and slow connection
                FtpWebResponse dirResponse = (FtpWebResponse)setupDir.GetResponse();
                context.Response.Write(dirResponse.StatusDescription.ToString());
                dirResponse.Close();
            }

        }

Again the web.config XML file on the server side holds the relevant values for flexibility of deployment.

Line 22 calls the WCF service to start reporting on delivery of Files to the created directory See next post.

Line 25 to 52 then uploads whatever files are in the FilesToUpload collection in separate threads. It calls the function to open read and write the appropriate streams to the HTTP service

private void UploadFileData(Stream inputFile,Stream resultFile)
        {
            try
            {
                byte[] fileData = new byte[4096];
                int fileDataToRead;
                while ((fileDataToRead = inputFile.Read(fileData, 0, fileData.Length)) != 0)
                {
                    resultFile.Write(fileData, 0, fileDataToRead);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Exception Thrown --> " + ex.Message);
            }
        }

The ASHX HTTP service to handle the uploading stream is as follows

public void ProcessRequest(HttpContext context)
        {

                ctx = context;
                //Query Parameters
                string filename = context.Request.QueryString["InputFile"].ToString();
                string FtpRootDir = context.Request.QueryString["FTPpath"].ToString();
                // Local HTTP Storage folder for staging to FTP upload
                string filePath = context.Server.MapPath("~/FilesServer/");
                // Get the Base address of the FTP server folder from the web.Config
                string  FtpBaseAddress = null;
                string FtpUserName = null;
                string FtpPassword = null;

                // Upload via HTTP stream initiatied by client
                using (FileStream fileStream = File.Create(context.Server.MapPath("~/FilesServer/" + filename)))
                {

                    byte[] bufferData = new byte[4096];
                    int bytesToBeRead;
                    while ((bytesToBeRead = context.Request.InputStream.Read(bufferData, 0, bufferData.Length)) != 0)
                    {
                        fileStream.Write(bufferData, 0, bytesToBeRead);
                    }
                    fileStream.Close();
                }
                try
                {
                    if (System.Configuration.ConfigurationManager.AppSettings["FtpBaseFolder"] != null)
                        FtpBaseAddress = System.Configuration.ConfigurationManager.AppSettings["FtpBaseFolder"].ToString();
                    //Get FTP Credential settings
                    if (System.Configuration.ConfigurationManager.AppSettings["FtpUserName"] != null)
                        FtpUserName = System.Configuration.ConfigurationManager.AppSettings["FtpUserName"].ToString();
                    if (System.Configuration.ConfigurationManager.AppSettings["FtpPassword"] != null)
                        FtpPassword = System.Configuration.ConfigurationManager.AppSettings["FtpPassword"].ToString();

                    // Create logon Credential
                    NetworkCredential credentials = new NetworkCredential(FtpUserName, FtpPassword);
                    string DirName = FtpBaseAddress;
                    string WorkingDirectory = DirName + FtpRootDir + "/";
                    // Upload File
                    FtpWebRequest request = (FtpWebRequest)WebRequest.Create(WorkingDirectory + filename);
                    request.Method = WebRequestMethods.Ftp.UploadFile;
                    request.Credentials = credentials;
                    request.Timeout = -1; // never timeout incase of slow connection
                    // Copy the contents of the file to the request stream.
                    byte[] bufferData = new byte[7168];
                    int length = bufferData.Length;
                    using (FileStream fileStream =
                        new FileStream(context.Server.MapPath("~/FilesServer/" + filename)
                            , FileMode.Open, FileAccess.Read, FileShare.Read, length, false)) // Last argument should block thread
                    {
                        Stream requestStream = request.GetRequestStream();
                        int bytesToBeRead;
                        while ((bytesToBeRead = fileStream.Read(bufferData, 0, bufferData.Length)) != 0)
                        {
                            requestStream.Write(bufferData, 0, bytesToBeRead);
                        }
                        requestStream.Flush(); // Flush any outstanding buffer
                        requestStream.Close();
                        fileStream.Close();
                        File.Delete(context.Server.MapPath("~/FilesServer/" + filename)); //

                    }
                    //context.Response.Write(filename + "Uploaded");     no can do it's a writable stream processor
                }
        

In the Next Post I’ll Cover the Duplex Polling WCF service to poll the FTP folder to check if the files have arrived at the FTP site.

That’s all the Time I have for now. Cheers. P.

Silverlight app is out of date version

Wednesday, August 10th, 2011

An interesting and frustrating problem during Silverlight development that we regularly run into is that of the app you’re running during debug not reflecting the changes made to the code before the build. In other words, the version you’re witnessing running in the browser isn’t the one that just got compiled.

This can lead to serious loss of productivity as you make further changes to your code believing that the previous changes didn’t ‘work’ when in reality they may have but you just weren’t witnessing their effect.

Though I haven’t fully understood this phenomenon there are a few things that seem to help:

  1. Kill IIS development server before you run, forcing another session to be launched
  2. If using Chrome, click Wrench…Options…Beneath the Bonnet and Clear Browsing History. Now I know you may not want this since it affects other stuff but just delete the history for the last hour will suffice. I’ve found this to work most times so well worth trying.
  3. Sometimes, in VS2010 the running process gets attached to the wrong Silverlight runtime process. Clearing this and forcing a new Silverlight process to be instantiated can help. Do so by choose (in VS2010) Debug…Attach to Process and choose the process with the type ‘Silverlight’. This task is made easier if you don’t have many tabs open.

None of these are guaranteed but they all have worked on occasion. The key thing is to be aware when testing your app, that it may not reflect your work to date. Otherwise, you enter a spiral of fixes that weren’t needed. Or you could just toss Silverlight and make the app from sticks and duck-tape.

Entity Framework Metadata not generated

Monday, August 8th, 2011

For those of you suffering with the half-baked n-tier solution that is Entity Framework and RIA Services, here’s a snippet of use. As you’ll know the metadata file is very necessary (so why is it optional?) but there’s no way you’re going to get the database design right first (or fiftieth) time so you’ll be regenerating a domain service lots.

This is a pain generally speaking but more of a pain given that you’ll have made modifications to both the DomainService class as well as the associated metadata classes. Now, those in the know will have created a buddy partial class for the DomainService so your custom queries will live there and be undisturbed by the creation of a new DomainService. However, I haven’t figured out a way to do the same for the metadata classes and these have a fair bit of custom attributes for things like validation. So when the metadata classes are regenerated there’s a cut-paste body of work to do.

And then there’s the interesting issue of your metadata classes not being re-generated with the DomainService even though you ticked the ‘Metadata’ box. You might have thought that excluding the old metadata file from the project would do the trick and well, it should. However, Visual Studio in its wisdom still ’sees’ these metadata classes – heck, you could probably delete them and it’ll still think they’re there. So the trick is to build the web project to flush out the offending old metadata classes and it works.

Of course, the burning question is why this kind of fudge is necessary? Surely, someone is supposed to test these things properly. It’s not like RIA Services and Entity Framework is fresh from the factory. Microsoft – if you’re listening – this is causing you to bleed developers. There’s, what, 80,000 working there? You can’t all be supporting IE6?

1 pages

latest news

UI Flow

Posted on Tuesday, 16th April, 2013

Often, we use wire-framing or mock-up tools (like the good guys at Balsamiq) to help communicate design ideas between developers as well as to clients. However, there is a need for something more efficient to aid communication of possible user interface flows through our emerging application. This communication is for internal use typically and doesn’t [...]

Testimonials

Excellent design skills

Posted on Sunday, 2nd May, 2010

We at Taxonomy.ie are happy to be associated with Appsolo and look forward to further work together.

follow me

twitter facebook delicious

AppsoloLtd. VAT No. IE97548691 - Copyright © 2010.