private void SaveChanges()
        {
            if (cmbEntities.SelectedItem == null)
            {
                MessageBox.Show("You must select an entity", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            ManageWorkingState(true);
            informationPanel = InformationPanel.GetInformationPanel(this, "Processing entity...", 340, 150);

            var entityitem     = (EntityItem)cmbEntities.SelectedItem;
            var itemsToProcess = lvAttributes.Items
                                 .Cast <ListViewItem>()
                                 .Where(i => !string.IsNullOrEmpty(i.SubItems[0].Text) && !string.IsNullOrEmpty(i.Text)) // Only get items where action is filled
                                 .Select(i => new { Action = i.SubItems[0].Text, Metadata = AttributeMetadataRow.FromListViewItem(i) })
                                 .ToList();

            var bwTransferData = new BackgroundWorker {
                WorkerReportsProgress = true
            };

            bwTransferData.DoWork += (sender, e) =>
            {
                var errors = new List <Tuple <string, string> >();
                var helper = new EntityHelper(entityitem.LogicalName, entityitem.LanguageCode, service);

                for (int i = 0; i < itemsToProcess.Count; i++)
                {
                    // TODO: Validate each row
                    //if (item.SubItems.Count == 6)
                    //{
                    var item = itemsToProcess[i];
                    var attributeMetadata = item.Metadata;
                    InformationPanel.ChangeInformationPanelMessage(informationPanel, string.Format("Processing attribute {0}...", attributeMetadata.LogicalName));
                    SendMessageToStatusBar(this, new StatusBarMessageEventArgs(string.Format("{0}/{1}", i + 1, itemsToProcess.Count)));

                    try
                    {
                        var attribute = EntityHelper.GetAttributeFromTypeName(attributeMetadata.AttributeType);

                        if (attribute == null)
                        {
                            errors.Add(new Tuple <string, string>(attributeMetadata.DisplayName,
                                                                  $"The Attribute Type \"{attributeMetadata.AttributeType}\" is not yet supported."));
                            continue;
                        }

                        attribute.LoadFromAttributeMetadataRow(attributeMetadata);
                        attribute.Entity = entityitem.LogicalName;

                        switch (item.Action)
                        {
                        case "Create":
                            if (cbCreate.Checked)
                            {
                                attribute.CreateAttribute(service);
                            }
                            break;

                        case "Update":
                            if (cbUpdate.Checked)
                            {
                                attribute.UpdateAttribute(service);
                            }
                            break;

                        case "Delete":
                            if (cbDelete.Checked)
                            {
                                attribute.DeleteAttribute(service);
                            }
                            break;
                        }
                    }
                    catch (FaultException <OrganizationServiceFault> error)
                    {
                        errors.Add(new Tuple <string, string>(attributeMetadata.DisplayName, error.Message));
                    }
                    //}
                    //else
                    //{
                    //    errors.Add(new Tuple<string, string>(item.Name, string.Format("Invalid row! Unexpected subitems count [{0}]", item.SubItems.Count)));
                    //}
                }

                helper.Publish();
                e.Result = errors;
            };
            bwTransferData.RunWorkerCompleted += (sender, e) =>
            {
                Controls.Remove(informationPanel);
                informationPanel.Dispose();
                SendMessageToStatusBar(this, new StatusBarMessageEventArgs(string.Empty));
                ManageWorkingState(false);

                var errors = (List <Tuple <string, string> >)e.Result;

                if (errors.Count > 0)
                {
                    var errorDialog = new ErrorList((List <Tuple <string, string> >)e.Result);
                    errorDialog.ShowDialog(ParentForm);
                }

                PopulateAttributes();
            };
            bwTransferData.ProgressChanged += (sender, e) =>
            {
                InformationPanel.ChangeInformationPanelMessage(informationPanel, e.UserState.ToString());
                SendMessageToStatusBar(this, new StatusBarMessageEventArgs(e.UserState.ToString()));
            };
            bwTransferData.RunWorkerAsync();
        }
        private void ImportAttributes()
        {
            if (cmbEntities.SelectedItem == null)
            {
                MessageBox.Show("You must select an entity", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (string.IsNullOrEmpty(txtTemplatePath.Text))
            {
                MessageBox.Show("You must select a template file", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            ManageWorkingState(true);

            informationPanel = InformationPanel.GetInformationPanel(this, "Loading template...", 340, 150);
            SendMessageToStatusBar(this, new StatusBarMessageEventArgs("Loading template..."));

            var entityItem = (EntityItem)cmbEntities.SelectedItem;
            var items      = new List <ListViewItem>();

            var bwTransferData = new BackgroundWorker {
                WorkerReportsProgress = true
            };

            bwTransferData.DoWork += (sender, e) =>
            {
                var attributes  = (List <ListViewItem>)e.Argument;
                var entityitem  = entityItem;
                var errors      = new List <Tuple <string, string> >();
                var nrOfCreates = 0;
                var nrOfUpdates = 0;
                var nrOfDeletes = 0;

                try
                {
                    using (FileStream stream = File.Open(txtTemplatePath.Text, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                        using (SpreadsheetDocument document = SpreadsheetDocument.Open(stream, false))
                        {
                            var sheet = document.WorkbookPart.Workbook.Sheets.Cast <Sheet>().Where(s => s.Name == entityitem.LogicalName).FirstOrDefault();
                            if (sheet == null)
                            {
                                errors.Add(new Tuple <string, string>(entityitem.LogicalName, "Entity sheet is not found in the template!"));
                            }
                            else
                            {
                                var sheetid      = sheet.Id;
                                var sheetData    = ((WorksheetPart)document.WorkbookPart.GetPartById(sheetid)).Worksheet.GetFirstChild <SheetData>();
                                var templaterows = sheetData.ChildElements.Cast <Row>().ToArray();
                                // For shared strings, look up the value in the shared strings table.
                                var stringTable = document.WorkbookPart.GetPartsOfType <SharedStringTablePart>().FirstOrDefault()?.SharedStringTable;

                                // Check all existing attributes
                                foreach (var item in attributes)
                                {
                                    var row = templaterows.Where(r => TemplateHelper.GetCellValue(r, 0, stringTable) == item.SubItems[1].Text).FirstOrDefault();

                                    var attributeMetadataRow = AttributeMetadataRow.FromListViewItem(item);

                                    attributeMetadataRow.UpdateFromTemplateRow(row, stringTable);
                                    var listViewItem = attributeMetadataRow.ToListViewItem();
                                    listViewItem.Tag = item.Tag;

                                    items.Add(listViewItem);

                                    if (attributeMetadataRow.Action == "Update")
                                    {
                                        nrOfUpdates++;
                                        listViewItem.ForeColor = ColorUpdate;
                                    }
                                    else if (attributeMetadataRow.Action == "Delete")
                                    {
                                        nrOfDeletes++;
                                        listViewItem.ForeColor = ColorDelete;
                                    }
                                    else
                                    {
                                        listViewItem.ForeColor = System.Drawing.Color.Black;
                                    }

                                    InformationPanel.ChangeInformationPanelMessage(informationPanel, string.Format("Processing new attribute {0}...", item.Text));
                                }

                                // Check new attributes
                                for (int i = 1; i < templaterows.Length; i++)
                                {
                                    var row         = templaterows[i];
                                    var logicalname = TemplateHelper.GetCellValue(row, 0, stringTable);

                                    if (!string.IsNullOrEmpty(logicalname))
                                    {
                                        var attribute = attributes.Select(a => (AttributeMetadata)a.Tag).Where(a => a.LogicalName == logicalname).FirstOrDefault();

                                        if (attribute == null)
                                        {
                                            InformationPanel.ChangeInformationPanelMessage(informationPanel, string.Format("Processing new attribute {0}...", logicalname));

                                            var attributeMetadataRow = AttributeMetadataRow.FromTableRow(row, stringTable);
                                            attributeMetadataRow.Action = "Create";

                                            var listViewItem = attributeMetadataRow.ToListViewItem();
                                            listViewItem.BackColor = ColorCreate;
                                            items.Add(listViewItem);

                                            nrOfCreates++;
                                        }
                                    }
                                }
                            }
                        }
                    SendMessageToStatusBar(this, new StatusBarMessageEventArgs(string.Format("{0} create; {1} update; {2} delete", nrOfCreates, nrOfUpdates, nrOfDeletes)));
                }
                catch (FaultException <OrganizationServiceFault> error)
                {
                    errors.Add(new Tuple <string, string>(entityitem.LogicalName, error.Message));
                    SendMessageToStatusBar(this, new StatusBarMessageEventArgs(string.Empty));
                }

                e.Result = errors;
            };
            bwTransferData.RunWorkerCompleted += (sender, e) =>
            {
                lvAttributes.BeginUpdate();

                // Add attributes here to avoid accessing lvAttributes across threads.
                lvAttributes.Items.Clear();
                foreach (var item in items)
                {
                    lvAttributes.Items.Add(item);
                }

                lvAttributes.EndUpdate();

                Controls.Remove(informationPanel);
                informationPanel.Dispose();
                ManageWorkingState(false);

                var errors = (List <Tuple <string, string> >)e.Result;

                if (errors.Count > 0)
                {
                    var errorDialog = new ErrorList((List <Tuple <string, string> >)e.Result);
                    errorDialog.ShowDialog(ParentForm);
                }
            };
            bwTransferData.ProgressChanged += (sender, e) =>
            {
                InformationPanel.ChangeInformationPanelMessage(informationPanel, e.UserState.ToString());
                SendMessageToStatusBar(this, new StatusBarMessageEventArgs(e.UserState.ToString()));
            };
            bwTransferData.RunWorkerAsync(lvAttributes.Items.Cast <ListViewItem>().ToList());
        }