Using Multiple Worker Process with Ignite UI Upload Control - Part 1 (Web Gardens)

[Infragistics] Mihail Mateev / Friday, August 15, 2014

File upload controls are important part from many web applications. Upload components enables you to provide users with a way to send a file from their computer to the server. These controls are useful for allowing users to upload pictures, text files, or other files.

The Infragistics Ignite UI suite  includes the File Upload Control (igUpload) that you can use to offer to your users all features expected from upload components. If you prefer to use C# you can use the Html helper in APS.NET MVC to get it running in no time. File Upload is available also for Infragistics ASP.Net Web Forms components as Web Upload (it an igUpload wrapper ).

In several posts you will learn how to use Ignite UI Upload Control in specific cases when you need better performance ( using igUpload in Web Garden, Web Farm, Cloud solutions / Microsoft Azure ) . Performance could be an issue  when you need to backup many large files using File Upload. Reliability is another reason to use similar solutions.

We will not do an overview of the igUpload features. You can find detailed information about Ignite UI upload functionalities in Infragistics documentation or www.igniteui.com

This article is focused on how to use Ignite UI upload control in applications, configured as Web Gardens. If you have no experience with Web Gardens – don’t worry – this post also makes an overview how to  configure one web application to use multiple worker processes ( Web Garden) in IIS, what is the difference between Web Garden , Web Farm, cloud applications and more…

What is IIS

IIS (Internet Information Server) from Microsoft is used to host your ASP.NET Web applications. IIS has it’s own ASP.NET Process Engine  to handle the ASP.NET request. So, when a request comes from client to server, IIS takes that request and  process it and send response back to clients.

Worker processes are a way of segmenting the execution of your website across multiple exe's. You do this for a couple of reasons, one if one of the workers gets clobbered by run time issues it doesn't take the others down.

 

  • Worker Process

Worker Process (w3wp.exe) runs the ASP.Net application in IIS. This process is responsible to manage all the request and response that are coming from client system.  All the ASP.Net functionality runs under the scope of worker process

  • Application pool

Application pool is the container of worker process.  Application pools is used to separate sets of IIS worker processes that share the same configuration.  Application pools enables a better security, reliability, and availability for any web application.  The worker process serves as the process boundary that separates each application pool so that when one worker process or application is having an issue or recycles, other applications or worker processes are not affected.

 

 

Web Farm

You may need to use multiple servers to host the application and divide the traffic among them. This is called “Web Farm”. So when you are hosting your single web site on multiple web servers over load balancer is called “Web Farm”. We will cover more details, related to the web farms in the next part of this article.

 

Web Garden

By default, each  Application pool contains a single worker process. Application which contains the multiple worker process is called “Web Garden”. Below is the typical diagram for a web garden application.

 

So, a Web application hosted on multiple servers and access based on the load on servers is called Web Farms and when a single application pool contains multiple Worker processes, it is called a web garden.

 

Ignite UI Upload Implementation for Web Garden:

The current implementation uses Visual Studio 2013, ASP.Net MVC 5 and Ignite UI 14.1 . The igUpload control is updated to use cistom dictionary providers for Web Farm / Web Garden scenarios -  It was not possible to use it in previous 13.x versions of Ignite UI of you have the latest service releases.

 

When application is configured to use multiple worker processes, the main issue is how to sync information about the uploading files. This functionality is supported via  CustomDictionaryProvider.

CustomDictionaryProvider configures a third party dictionary provider (the structure which holds the metadata for the currently uploading files).
This setting is specifically designed for Web Farm/Web Garden scenarios where a common file metadata should be shared between multiple machines/processes.
This setting expects a name of a type which implements ISafeDictionary<string, UploadInfo> interface.

There are different possible approaches to keep your metadata independent from different processes. On of possible solutions includes implementation of the web service (WCF service) where to keep upload info metadata. It could run in separate IIS application pool, where

 

WPF Service (FileUploadSvc):

Implementation includes simple WCF application.

  • IUploadService interface ( IUploadService.cs )
   1: using System;
   2: using System.Collections.Generic;
   3: using System.ServiceModel;
   4: using Infragistics.Web.Mvc;
   5:  
   6: namespace FileUploadSvc
   7: {
   8:     [ServiceContract]
   9:     public interface IUploadService
  10:     {
  11:         [OperationContract]
  12:         void SetData(Dictionary<string, UploadInfo> dict);
  13:  
  14:         [OperationContract]
  15:         Dictionary<string, UploadInfo> GetData();
  16:  
  17:         [OperationContract]
  18:         bool RemoveData(string key);
  19:     }
  20: }

 

  • UploadService.svc.cs

Code below shows the implementation of IUploadService , where the most important is how to manage dictionaries with upload information ( upload metadata structure is defined in UploadInfo type )

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.ServiceModel;
   5: using Infragistics.Web.Mvc;
   6:  
   7: namespace FileUploadSvc
   8: {
   9:  
  10:  
  11:     [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
  12:     public class UploadService : IUploadService
  13:     {
  14:  
  15:         Dictionary<string, UploadInfo> _dictFiles = new Dictionary<string, UploadInfo>();
  16:  
  17:         public void SetData(Dictionary<string, UploadInfo> dict)
  18:         {
  19:             try
  20:             {
  21:                 if (dict != null)
  22:                 {
  23:                     this._dictFiles = Merge(new Dictionary<string, UploadInfo>[] { dict, this._dictFiles });
  24:                 }
  25:             }
  26:             catch (Exception ex)
  27:             {
  28:                 Console.Write(ex.Message);
  29:             }
  30:         }
  31:  
  32:         public bool RemoveData(string key)
  33:         {
  34:             bool isRemoved = false;
  35:             if (this._dictFiles != null && this._dictFiles.ContainsKey(key))
  36:             {
  37:                 isRemoved = this._dictFiles.Remove(key);
  38:             }
  39:             return isRemoved;
  40:         }
  41:  
  42:         public Dictionary<string, UploadInfo> Merge(Dictionary<string, UploadInfo>[] dictionaries)
  43:         {
  44:             return dictionaries.SelectMany(x => x)
  45:                 .ToLookup(pair => pair.Key, pair => pair.Value)
  46:                 .ToDictionary(group => group.Key, group => group.First());
  47:         }
  48:  
  49:         public Dictionary<string, UploadInfo> GetData()
  50:         {
  51:             return this._dictFiles;
  52:         }
  53:  
  54:  
  55:     }
  56: }

 

Upload implementation (ASP.Net MVC 5 Application)

 

  • ISafeDictionary interface

Below is shown ISafeDictionary interface

   1: public interface ISafeDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable
   2: {
   3:     // Summary:
   4:     //     Check if the key exists in the dictionary, if not adds the value
   5:     //
   6:     // Parameters:
   7:     //   key:
   8:     //
   9:     //   value:
  10:     bool CheckAndAdd(TKey key, TValue value);
  11: }

 

  • CustomSafeDictionary class

 

Implementation of Serialization in CustomSafeDictionary (as shown below)

   1: #region Serialization
   2: private Dictionary<string, UploadInfo> Dictionary
   3: {
   4:     get
   5:     {
   6:         return this.Deserialize();
   7:     }
   8: }
   9:  
  10: private Dictionary<string, UploadInfo> Deserialize()
  11: {
  12:     UploadService.UploadServiceClient client = new UploadService.UploadServiceClient();
  13:     return client.GetData();
  14: }
  15:  
  16: private void Serialize(Dictionary<string, UploadInfo> d)
  17: {
  18:     UploadService.UploadServiceClient client = new UploadService.UploadServiceClient();
  19:     client.SetData(d);
  20: }
  21: #endregion

 

The whole CustomSafeDictionary  class code is available below:

   1: [Serializable]
   2: public class CustomSafeDictionary : ISafeDictionary<string, UploadInfo>
   3: {
   4:  
   5:     #region Members
   6:     private readonly object syncRoot = new object();
   7:     private Dictionary<string, UploadInfo> _dictionary;
   8:     #endregion
   9:  
  10:  
  11:     public CustomSafeDictionary()
  12:     {
  13:         this._dictionary = new Dictionary<string, UploadInfo>();
  14:         Serialize(this._dictionary);
  15:     }
  16:  
  17:     #region Serialization
  18:     private Dictionary<string, UploadInfo> Dictionary
  19:     {
  20:         get
  21:         {
  22:             return this.Deserialize();
  23:         }
  24:     }
  25:  
  26:     private Dictionary<string, UploadInfo> Deserialize()
  27:     {
  28:         UploadService.UploadServiceClient client = new UploadService.UploadServiceClient();
  29:         return client.GetData();
  30:     }
  31:  
  32:     private void Serialize(Dictionary<string, UploadInfo> d)
  33:     {
  34:         UploadService.UploadServiceClient client = new UploadService.UploadServiceClient();
  35:         client.SetData(d);
  36:     }
  37:     #endregion
  38:  
  39:     #region Properties
  40:     #region Keys
  41:     /// <summary>
  42:     /// Returns the collection of keys
  43:     /// </summary>
  44:     public ICollection<string> Keys
  45:     {
  46:         get
  47:         {
  48:             ICollection<string> keys;
  49:             lock (syncRoot)
  50:             {
  51:                 keys = this.Dictionary.Keys;
  52:             }
  53:             return keys;
  54:         }
  55:     }
  56:     #endregion
  57:  
  58:     #region Values
  59:     /// <summary>
  60:     /// Gets the collection containing the values
  61:     /// </summary>
  62:     public ICollection<UploadInfo> Values
  63:     {
  64:         get
  65:         {
  66:             ICollection<UploadInfo> values;
  67:             lock (syncRoot)
  68:             {
  69:                 values = this.Dictionary.Values;
  70:             }
  71:             return values;
  72:         }
  73:     }
  74:     #endregion
  75:  
  76:     #region this[string key]
  77:     /// <summary>
  78:     /// Gets or sets the value associated with the specified key.
  79:     /// </summary>
  80:     /// <param name="key"></param>
  81:     /// <returns></returns>
  82:     public UploadInfo this[string key]
  83:     {
  84:         get
  85:         {
  86:             UploadInfo value;
  87:             lock (syncRoot)
  88:             {
  89:                 value = this.Dictionary[key];
  90:             }
  91:             return value;
  92:         }
  93:         set
  94:         {
  95:             lock (syncRoot)
  96:             {
  97:                 this._dictionary = this.Dictionary;
  98:                 this._dictionary[key] = value;
  99:                 this.Serialize(this._dictionary);
 100:  
 101:             }
 102:         }
 103:     }
 104:     #endregion
 105:  
 106:     #region Count
 107:     /// <summary>
 108:     /// Gets the number of key/value pairs in the dictionary
 109:     /// </summary>
 110:     public int Count
 111:     {
 112:         get
 113:         {
 114:             int count = 0;
 115:             lock (syncRoot)
 116:             {
 117:                 count = this.Dictionary.Count;
 118:             }
 119:             return count;
 120:         }
 121:     }
 122:     #endregion
 123:  
 124:     #region IsReadOnly
 125:     /// <summary>
 126:     /// Returns whether the dictionary is read only
 127:     /// </summary>
 128:     public bool IsReadOnly
 129:     {
 130:         get { return false; }
 131:     }
 132:     #endregion
 133:     #endregion
 134:  
 135:     #region Methods
 136:     #region IDictionary<string,UploadInfoMembers>
 137:     /// <summary>
 138:     /// Adds the specified key and value to the dictionary
 139:     /// </summary>
 140:     /// <param name="key"></param>
 141:     /// <param name="value"></param>
 142:     public void Add(string key, UploadInfo value)
 143:     {
 144:         lock (syncRoot)
 145:         {
 146:             this._dictionary = this.Dictionary;
 147:             this._dictionary.Add(key, value);
 148:             this.Serialize(this._dictionary);
 149:         }
 150:     }
 151:  
 152:     /// <summary>
 153:     /// Check if the key exists in the dictionary, if not adds the value
 154:     /// </summary>
 155:     /// <param name="key"></param>
 156:     /// <param name="value"></param>
 157:     /// <returns></returns>
 158:     public bool CheckAndAdd(string key, UploadInfo value)
 159:     {
 160:         bool isAdded = false;
 161:         lock (syncRoot)
 162:         {
 163:             if (!this.Dictionary.ContainsKey(key))
 164:             {
 165:                 this._dictionary = this.Dictionary;
 166:                 this._dictionary.Add(key, value);
 167:                 this.Serialize(this._dictionary);
 168:                 isAdded = true;
 169:             }
 170:         }
 171:  
 172:         return isAdded;
 173:     }
 174:  
 175:     /// <summary>
 176:     /// Check if the key exists in dictionary, if not adds the value
 177:     /// </summary>
 178:     /// <param name="keyValuePair"></param>
 179:     /// <returns></returns>
 180:     public bool CheckAndAdd(KeyValuePair<string, UploadInfo> keyValuePair)
 181:     {
 182:         bool isAdded = false;
 183:         lock (syncRoot)
 184:         {
 185:             if (!this.Dictionary.ContainsKey(keyValuePair.Key))
 186:             {
 187:                 Add(keyValuePair);
 188:                 isAdded = true;
 189:             }
 190:         }
 191:  
 192:         return isAdded;
 193:     }
 194:  
 195:     /// <summary>
 196:     /// Check if the dictionary contains the specified key
 197:     /// </summary>
 198:     /// <param name="key"></param>
 199:     /// <returns></returns>
 200:     public bool ContainsKey(string key)
 201:     {
 202:         bool isContains = false;
 203:         lock (syncRoot)
 204:         {
 205:             isContains = this.Dictionary.ContainsKey(key);
 206:         }
 207:         return isContains;
 208:     }
 209:  
 210:  
 211:     /// <summary>
 212:     /// Removes from the dictionary item with the specified key
 213:     /// </summary>
 214:     /// <param name="key"></param>
 215:     /// <returns></returns>
 216:     public bool Remove(string key)
 217:     {
 218:         lock (syncRoot)
 219:         {
 220:             UploadService.UploadServiceClient client = new UploadService.UploadServiceClient();
 221:  
 222:             return client.RemoveData(key);
 223:         }
 224:     }
 225:  
 226:     public bool TryGetValue(string key, out UploadInfo value)
 227:     {
 228:         lock (syncRoot)
 229:         {
 230:             this._dictionary = this.Dictionary;
 231:             return this._dictionary.TryGetValue(key, out value);
 232:         }
 233:     }
 234:  
 235:  
 236:     #endregion
 237:  
 238:     #region ICollection<KeyValuePair<string,UploadInfo>Members
 239:     /// <summary>
 240:     /// Adds the item to collection
 241:     /// </summary>
 242:     /// <param name="item"></param>
 243:     public void Add(KeyValuePair<string, UploadInfo> item)
 244:     {
 245:         lock (syncRoot)
 246:         {
 247:             this._dictionary = this.Dictionary;
 248:             ((ICollection<KeyValuePair<string, UploadInfo>>)this._dictionary).Add(item);
 249:             this.Serialize(this._dictionary);
 250:         }
 251:     }
 252:  
 253:     /// <summary>
 254:     /// Removes all keys and values from the dictionary
 255:     /// </summary>
 256:     public void Clear()
 257:     {
 258:         lock (syncRoot)
 259:         {
 260:             this._dictionary = this.Dictionary;
 261:             this._dictionary.Clear();
 262:             this.Serialize(this._dictionary);
 263:         }
 264:     }
 265:  
 266:     /// <summary>
 267:     /// Check whether the dictionary contains the specific item
 268:     /// </summary>
 269:     /// <param name="item"></param>
 270:     /// <returns></returns>
 271:     public bool Contains(KeyValuePair<string, UploadInfo> item)
 272:     {
 273:         return ((ICollection<KeyValuePair<string,
 274:         UploadInfo>>)this.Dictionary).Contains(item);
 275:     }
 276:  
 277:     /// <summary>
 278:     /// Copies the dictionary KeyCollection
 279:     /// elements to an existing one-dimensional System.Array, starting at the specified array index.
 280:     /// </summary>
 281:     /// <param name="array"></param>
 282:     /// <param name="arrayIndex"></param>
 283:     public void CopyTo(KeyValuePair<string, UploadInfo>[] array, int arrayIndex)
 284:     {
 285:         lock (syncRoot)
 286:         {
 287:             this._dictionary = this.Dictionary;
 288:             ((ICollection<KeyValuePair<string, UploadInfo>>)this._dictionary).CopyTo(array,
 289:             arrayIndex);
 290:             this.Serialize(this._dictionary);
 291:         }
 292:     }
 293:  
 294:     /// <summary>
 295:     /// Removes the value with the specified key from the dictionary.
 296:     /// </summary>
 297:     /// <param name="item"></param>
 298:     /// <returns></returns>
 299:     public bool Remove(KeyValuePair<string, UploadInfo> item)
 300:     {
 301:         lock (syncRoot)
 302:         {
 303:             this._dictionary = this.Dictionary;
 304:             return ((ICollection<KeyValuePair<string,
 305:             UploadInfo>>)this._dictionary).Remove(item);
 306:             this.Serialize(this._dictionary);
 307:         }
 308:     }
 309:  
 310:     #endregion
 311:  
 312:     #region IEnumerable<KeyValuePair<string,UploadInfo>Members
 313:     /// <summary>
 314:     /// Returns an enumerator that iterates through the collection.
 315:     /// </summary>
 316:     /// <returns></returns>
 317:     public IEnumerator<KeyValuePair<string, UploadInfo>> GetEnumerator()
 318:     {
 319:         return ((ICollection<KeyValuePair<string, UploadInfo>>)this.Dictionary).GetEnumerator();
 320:     }
 321:  
 322:     #endregion
 323:  
 324:     #region IEnumerable Members
 325:     /// <summary>
 326:     /// Returns an enumerator that iterates through a collection.
 327:     /// </summary>
 328:     /// <returns>An System.Collections.IEnumerator object that can be used to iterate through the collection</returns>
 329:     System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 330:     {
 331:         return ((System.Collections.IEnumerable)this.Dictionary).GetEnumerator();
 332:     }
 333:  
 334:     #endregion
 335:     #endregion
 336: }

 

  • Configuration:

 

  • Web.config file

 

The appSettings section

   1: <appSettings>
   2:     ....
   3:     <add key="fileUploadPath" value="~/Uploads" />
   4:     <add key="maxFileSizeLimit" value="5194304" />
   5:     <add key="bufferSize" value="56384" />
   6:     <add key="CustomDictionaryProvider" value="IgUploadMvc03.CustomSafeDictionary, IgUploadMvc03" />
   7:  </appSettings>

 

The webServer section

   1: <system.webServer>
   2:     <modules runAllManagedModulesForAllRequests="true">
   3:       <add name="IGUploadModule" type="Infragistics.Web.Mvc.UploadModule"
   4:                                  preCondition="managedHandler" />
   5:     </modules>
   6:     <handlers>
   7:       <add name="IGUploadStatusHandler" path="IGUploadStatusHandler.ashx" verb="*"
   8:            type="Infragistics.Web.Mvc.UploadStatusHandler" preCondition="integratedMode" />
   9:     </handlers>
  10:     ....
  11: </system.webServer>

 

  • RouteConfig class
   1: public class RouteConfig
   2: {
   3:     public static void RegisterRoutes(RouteCollection routes)
   4:     {
   5:         routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
   6:         routes.IgnoreRoute("IGUploadStatusHandler.ashx");
   7:         routes.MapRoute(
   8:             name: "Default",
   9:             url: "{controller}/{action}/{id}",
  10:             defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
  11:         );
  12:     }
  13: }

 

  • Controllers ( MVC project)
   1: public ActionResult Upload()
   2: {
   3:     ViewBag.Message = "Your upload page.";
   4:  
   5:     return View();
   6: }
   7:  
   8:  
   9: public ActionResult UploadMvc()
  10: {
  11:     ViewBag.Message = "Your upload MVC page.";
  12:  
  13:     return View();
  14: }

 

  • Views:
  • HTML5 /  jQuery implementation (Upload view )
   1: <!DOCTYPE html>
   2:  
   3: <html>
   4: <head>
   5:     <title></title>
   6:     <link href="http://cdn-na.infragistics.com/jquery/20141/latest/css/themes/infragistics/infragistics.theme.css" rel="stylesheet" />
   7:     <link href="http://cdn-na.infragistics.com/jquery/20141/latest/css/structure/infragistics.css" rel="stylesheet" />
   8:  
   9:     <script src="http://modernizr.com/downloads/modernizr-latest.js"></script>
   1:  
   2:     <script src="http://code.jquery.com/jquery-1.9.1.min.js">
   1: </script>
   2:     <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.min.js">
   1: </script>
   2:  
   3:     <!-- Ignite UI Required Combined JavaScript Files -->
   4:     <script src="http://cdn-na.infragistics.com/jquery/20141/latest/js/infragistics.core.js">
   1: </script>
   2:     <script src="http://cdn-na.infragistics.com/jquery/20141/latest/js/infragistics.lob.js">
   1: </script>
   2:  
   3: </head>
   4: <body>
   5:     <link href="http://cdn-na.infragistics.com/jquery/20141/latest/css/themes/infragistics/infragistics.theme.css" rel="stylesheet" />
   6:     <link href="http://cdn-na.infragistics.com/jquery/20141/latest/css/structure/infragistics.css" rel="stylesheet" />
   7:  
   8:     <style type="text/css">
   9:         .container-info, .error-info {
  10:             margin: 10px 0;
  11:             font-weight: bold;
  12:         }
  13:  
  14:         .error-info {
  15:             color: #FF0000;
  16:         }
  17:     </style>
  18:  
  19:     <script src="http://modernizr.com/downloads/modernizr-latest.js">
   1: </script>
   2:     <script src="http://code.jquery.com/jquery-1.9.1.min.js">
   1: </script>
   2:     <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.min.js">
   1: </script>
   2:  
   3:     <!-- Ignite UI Required Combined JavaScript Files -->
   4:     <script src="http://cdn-na.infragistics.com/jquery/20141/latest/js/infragistics.core.js">
   1: </script>
   2:     <script src="http://cdn-na.infragistics.com/jquery/20141/latest/js/infragistics.lob.js">
   1: </script>
   2:  
   3: </head>
   4: <body>
   5:  
   6:     <div id="igUpload1"></div>
   7:     <div id="error-message" style="color: #FF0000; font-weight: bold;"></div>
   8:     <script>
   9:  
  10:         $(function () {
  11:             $("#igUpload1").igUpload({
  12:                 mode: 'multiple',
  13:                 maxUploadedFiles: 4,
  14:                 maxSimultaneousFilesUploads: 2,
  15:                 autostartupload: true,
  16:                 progressUrl: "/IGUploadStatusHandler.ashx",
  17:                 onError: function (e, args) {
  18:                     showAlert(args);
  19:                 }
  20:             });
  21:         });
  22:  
  23:         function showAlert(args) {
  24:             $("#error-message").html(args.errorMessage).stop(true, true).fadeIn(500).delay(3000).fadeOut(500);
  25:         }
  26:  
  27:         //more optional code
  28:     
</script>
  10:     
  11: </body>
  12: </html>

 

  • Razor implementation (UploadMvc view )
   1: @using Infragistics.Web.Mvc
   2:  
   3: @{
   4:     Layout = null;
   5: }
   6:  
   7: <!DOCTYPE html>
   8:  
   9: <html>
  10: <head>
  11:     <meta name="viewport" content="width=device-width" />
  12:     <title>IgUpload MVC Helper</title>
  13:     <!-- Ignite UI Required Combined CSS Files -->
  14:     <link href="http://cdn-na.infragistics.com/jquery/20141/latest/css/themes/infragistics/infragistics.theme.css" rel="stylesheet" />
  15:     <link href="http://cdn-na.infragistics.com/jquery/20141/latest/css/structure/infragistics.css" rel="stylesheet" />
  16:  
  17:     <script src="http://modernizr.com/downloads/modernizr-latest.js"></script>
  18:     <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
  19:     <script src="http://code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
  20:  
  21:     <!-- Ignite UI Required Combined JavaScript Files -->
  22:     <script src="http://cdn-na.infragistics.com/jquery/20141/latest/js/infragistics.core.js"></script>
  23:     <script src="http://cdn-na.infragistics.com/jquery/20141/latest/js/infragistics.lob.js"></script>
  24:  
  25: </head>
  26: <body>
  27:     <div>
  28: @(
  29:  Html.Infragistics().Upload()
  30:         .ID("igUpload1")
  31:         .Mode(UploadMode.Single)
  32:         .AutoStartUpload(true)
  33:         .ProgressUrl(Url.Content("~/IGUploadStatusHandler.ashx"))
  34:         .ControlId("serverID1")
  35:         .Render()
  36: )
  37:  
  38:         <div id="error-message" style="color: #FF0000; font-weight: bold;"></div>
  39:  
  40:         <script type="text/javascript">
  41:             $(function () {
  42:                 $("#igUpload1").bind({
  43:                     iguploadonerror: function (e, args) {
  44:                         $("#error-message").html(args.errorMessage).stop(true, true).fadeIn(500).delay(3000).fadeOut(500);
  45:                     }
  46:                 });
  47:             });
  48:         </script>   
  49:     </div>
  50: </body>
  51: </html>

 

ASP.Net Web Forms solution:

If you need to use ASP.Net Web Forms application, the only one difference is *aspx file, where you should add a WebUpload ASP.Net control

*.aspx file:

   1: <div>
   2:     <div id="main" style="margin: 50px;">
   3:         <div id="error-message" style="color: #FF0000; font-weight: bold;">
   4:         </div>
   5:         <ig:WebUpload AutoStartUpload="true" ID="IGUpload" runat="server" ClientIDMode="Static"
   6:             LabelUploadButton="Browse PDF" LabelAddButton="Browse PDF" FileSizeMetric="KBytes" ProgressUrl="IGUploadStatusHandler.ashx" Style="width: auto;">
   7:             <AllowedExtensions>
   8:                     <ig:FileUploadExtension Extension="pdf" />     
   9:                     <ig:FileUploadExtension Extension="PDF" /> 
  10:                     <ig:FileUploadExtension Extension="Pdf" /> 
  11:                     <ig:FileUploadExtension Extension="PDf" /> 
  12:                     <ig:FileUploadExtension Extension="pDF" /> 
  13:                     <ig:FileUploadExtension Extension="pDf" /> 
  14:                     <ig:FileUploadExtension Extension="pdF" /> 
  15:                     <ig:FileUploadExtension Extension="PdF" /> 
  16:                     <ig:FileUploadExtension Extension="PDf" />                   
  17:                 </AllowedExtensions>
  18:                 <FileExtensionIcons>
  19:                     <ig:FileUploadExtensionIcon Default="True">
  20:                         <Extensions>
  21:                             <ig:FileUploadExtension Extension="pdf" />
  22:                         </Extensions>
  23:                     </ig:FileUploadExtensionIcon>
  24:                 </FileExtensionIcons>
  25:                 <ClientEvents FileUploaded="fileUploaded" />
  26:                 <ClientEvents FileSelecting="fileSelecting" />
  27:                 <ClientEvents FileSelected="fileSelected" />
  28:                 <ClientEvents OnError="onErrorHandler" />
  29:         </ig:WebUpload>
  30:         <asp:HiddenField ID="filePath" ClientIDMode="Static" runat="server" />   
  31:     </div>   
  32:     <a href="#" id="btnImportDocsAdd" class="ui-button ui-state-default ui-corner-all"  style="display:none;"
  33:             runat="server" clientidmode="static"><span class="ui-icon ui-icon-plus"></span>Move Documents</a> 
  34: </div>

 

Configuation in web.config is identical with this one for ASP.Net MVC project

 

IIS configuration ( IIS 7.x  and IIS 8.x)

 

  • Create /set application pools

IIS Management Console –> Application Pools –> Add Application Pool

 

Select the pool and choose “Advanced Settings”.

Add a new application pool ( IgUploadMvc) .

 

 

Select the created new application pool and choose “Advanced Settings”

 

 

  • Check the setting for our ASP.Net MVC project (IgUploadMVC03)

Manage Application –> Advanced Settimgs->Application Pool

 

 

  • Check the setting for WCF application project (FIleUploadSvc)

 

It is important when we need multiple worker processes for Ignite UI Upload component to have different application pools for the MVC application, including upload control and your WCF service.
WCF service is used to keep upload information for each file

Ensure that the application pool is different (even if it is a default application pool / DefaultAppPool ) and its Maximum Worker Processes is set to 1

 

 

Application pool default settings:

 

Sample application: ASP.Net MVC 5 project.

If you want to test your application in debug mode you need to set in Web properties to use Local IIS or External Host ( IIS Express cannot allow you to test multiple worker processes )

 

 

Sample application , available for download here , has two views to demonstrate Ignite UI file upload , one with pure jQuery implementation (Upload view) and another one with MVC helper (UploadMvc view)

 

Screens below demonstrate how to upload file using jQuery implementation ( view with MVC helper provides identical functionalities)

Initial state of the upload

 

Choose files

 

Upload progress

 

Final screen state

 

Uploaded files

We would be able to summarize that you need to use igUpload with multiple worker roles you need to:

  1. Implement a CustomDuctionaryProvider
    1.1 This provider should maintain upload metadata out of the same application pool where works your application.
    1.2 One simple implementation is via WCF service that works in separate application pool (This pool should have maximum worker processes per application set to 1. Otherwise you can have metadata for each worker process that is not consistent with the metadata for other worker processes)
  2. Set your CustomDictionaryProvider in the Web.config of your ASP.Net MVC / Web Forms application

This implementation is mainly for Web Gardens , but it could work for Web Farms / Cloud solutions as you will see in the next parts of this article.

Sample project, used for this blog could be downloaded here:

Click on this image to get a fully support trial version of Infragistics Ignite UI controls:

To view all the samples and code for HTML, MVC & ASP.NET, click here:

http://www.igniteui.com/

Follow news from Infragistics for more information about new Infragistics products.

As always, you can follow us on Twitter @mihailmateev and @Infragistics and stay in touch on Facebook,Google+andLinkedIn!