public void ReadEntityDataFromFile(string sFileName)
        {
            LogInfo("Reading CRM metadata from file: " + sFileName);
            FileEntitySet = new clEntities(); //initialise target
            OneRecord rec;
            var       csv = new CsvReader(new System.IO.StreamReader(sFileName, Encoding.UTF8));

            csv.Configuration.Delimiter = mySettings.CSVDelimiter;
            csv.Configuration.Quote     = mySettings.TextDelimiter[0];
            csv.Read();
            csv.ReadHeader();
            while (csv.Read())
            {
                rec = csv.GetRecord <OneRecord>();
                if (rec.RowType == "Entity" || rec.RowType == "Attribute") //check entity exists and if so update, otherwise create
                {
                    LogInfo2("Read row of type " + rec.RowType + " with entity " + rec.EntityLogicalName + ((rec.RowType == "Attribute") ? ", attribute " + rec.AttributeLogicalName : ""));
                    string[] Tags = GetTagsFromCSVRecord(rec);
                    if (!FileEntitySet.entities.ContainsKey(rec.EntityLogicalName))
                    {
                        FileEntitySet.AddEntity(rec.EntityLogicalName, rec.EntitySchemaName, rec.EntityDescription, rec.LanguageCode, new SortedDictionary <string, clOneAttribute>(), Tags);
                    }
                    else if (rec.RowType == "Entity")
                    {
                        FileEntitySet.entities[rec.EntityLogicalName].schemaName  = rec.EntitySchemaName;
                        FileEntitySet.entities[rec.EntityLogicalName].description = rec.EntityDescription;
                    }
                }
                if (rec.RowType == "Attribute")
                {
                    string[] Tags = GetTagsFromCSVRecord(rec);
                    if (!FileEntitySet.entities[rec.EntityLogicalName].attributes.ContainsKey(rec.AttributeLogicalName))
                    {
                        FileEntitySet.entities[rec.EntityLogicalName].attributes.Add(
                            rec.AttributeLogicalName, new clOneAttribute(rec.AttributeLogicalName, rec.AttributeSchemaName, rec.AttributeType, rec.AttributeDescription, rec.LanguageCode, Tags, false));
                    }
                    else
                    {
                        FileEntitySet.entities[rec.EntityLogicalName].attributes[rec.AttributeLogicalName].attributeTypeDescription = rec.AttributeType;
                        FileEntitySet.entities[rec.EntityLogicalName].attributes[rec.AttributeLogicalName].schemaName  = rec.AttributeSchemaName;
                        FileEntitySet.entities[rec.EntityLogicalName].attributes[rec.AttributeLogicalName].description = rec.AttributeDescription;
                    }
                }

                if (rec.RowType != "Entity" && rec.RowType != "Attribute")
                {
                    throw new Exception("Row must have first cell of Entity or Attribute. Suggests file corruption");
                }
            }
        }
        /// <summary>
        /// Reads entities into memory
        /// </summary>
        public void ReadEntitiesFromCRM()
        {
            LogInfo("Reading CRM metadata from CRM");
            CRMEntitySet = new clEntities(); //initialise
            string[] Tags = { "", "", "", "", "", "", "", "", "", "" };

            RetrieveAllEntitiesRequest request = new RetrieveAllEntitiesRequest()
            {
                EntityFilters = EntityFilters.All
            };

            request.RetrieveAsIfPublished = cxReadasIfPublished.Checked;

            RetrieveAllEntitiesResponse response = (RetrieveAllEntitiesResponse)Service.Execute(request);

            LogInfo2("Entity data read from CRM");

            foreach (EntityMetadata currentEntity in response.EntityMetadata)
            {
                LogInfo2("Processsing entity " + currentEntity.LogicalName);

                if (currentEntity.IsCustomizable.Value) //only output items for customisable entities
                {
                    string sDescription;
                    //Get attributes
                    SortedDictionary <string, clOneAttribute> attributes = new SortedDictionary <string, clOneAttribute>();
                    foreach (AttributeMetadata att in currentEntity.Attributes)
                    {
                        LogInfo2("Processsing attribute " + att.LogicalName);
                        sDescription = att.Description.LocalizedLabels.Where(l => l.LanguageCode == mySettings.LanguageCode).FirstOrDefault() != null?att.Description.LocalizedLabels.Where(l => l.LanguageCode == mySettings.LanguageCode).FirstOrDefault().Label : "";

                        if (mySettings.UseTags)
                        {
                            ExtractTags(sDescription, out Tags, out sDescription);
                        }
                        attributes.Add(att.LogicalName, new clOneAttribute(att.LogicalName, att.SchemaName, att.AttributeType.Value.ToString(), sDescription, mySettings.LanguageCode, Tags, (att.SourceType != null && att.SourceType > 0)));
                        if (att.AttributeType.Value.ToString() == "Picklist")
                        {
                            attributes[att.LogicalName].DefaultOptionSetValue = ((PicklistAttributeMetadata)att).DefaultFormValue;
                        }
                    }

                    sDescription = currentEntity.Description.LocalizedLabels.Where(l => l.LanguageCode == mySettings.LanguageCode).FirstOrDefault() != null?currentEntity.Description.LocalizedLabels.Where(l => l.LanguageCode == mySettings.LanguageCode).FirstOrDefault().Label : "";

                    if (mySettings.UseTags)
                    {
                        ExtractTags(sDescription, out Tags, out sDescription);
                    }

                    //Flag linked rollup fields _state and _date as these are hidden and cannot be updated
                    foreach (string sKey in attributes.Keys)
                    {
                        if (attributes[sKey].IsRollupField)                                       //is this a rollup
                        {
                            if (attributes.Keys.Contains(attributes[sKey].logicalName + "_date")) //if so and if the _date fields exists then flag it
                            {
                                attributes[attributes[sKey].logicalName + "_date"].NotUpdateAble = true;
                            }
                            if (attributes.Keys.Contains(attributes[sKey].logicalName + "_state")) //if so and if the _state fields exists then flag it
                            {
                                attributes[attributes[sKey].logicalName + "_state"].NotUpdateAble = true;
                            }
                        }
                    }

                    //Add entity to set
                    CRMEntitySet.AddEntity(currentEntity.LogicalName, currentEntity.SchemaName, sDescription, mySettings.LanguageCode, attributes, Tags);
                    if (!currentEntity.IsRenameable.Value)
                    {
                        CRMEntitySet.entities[currentEntity.LogicalName].NotUpdateAble = true; //flag if cannot be updated
                    }
                }
            }
        }