Log in to like this post! 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: 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) 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!