public async Task<IRow[]> Query(IReadOnlyDictionary<string, string> columns)
        {
            if (null == columns)
            {
                throw new ArgumentNullException(CommaDelimitedFileAdapter.ArgumentNameColumns);
            }

            string query = 
                string.Format(
                    CultureInfo.InvariantCulture,
                    CommaDelimitedFileAdapter.QueryTemplate,
                    this.fileName);
            if (columns.Any())
            {
                IReadOnlyCollection<string> filters =
                    columns
                    .Select(
                        (KeyValuePair<string, string> item) =>
                            string.Format(
                                CultureInfo.InvariantCulture,
                                CommaDelimitedFileAdapter.FilterTemplate,
                                item.Key,
                                item.Value))
                    .ToArray();
                string filter =
                    string.Join(CommaDelimitedFileAdapter.DelimiterFilter, filters);

                query = string.Concat(query, CommaDelimitedFileAdapter.WhereClausePrefix, filter);
            }

            OleDbCommand commandQuery = null;
            try
            {
                commandQuery = new OleDbCommand(query, connection);
                DbDataReader reader = null;
                try
                {
                    reader = await commandQuery.ExecuteReaderAsync();
                    IList<IRow> rows = new List<IRow>();

                    while (reader.Read())
                    {
                        string rowKey = (string)reader[0];
                        Dictionary<string, string> rowColumns = 
                            new Dictionary<string, string>(this.headers.Count - 1);
                        for (int indexColumn = 1; indexColumn < this.headers.Count; indexColumn++)
                        {
                            string columnValue = reader[indexColumn] as string;
                            if (string.IsNullOrWhiteSpace(columnValue))
                            {
                                continue;
                            }

                            string columnHeader = this.headers.ElementAt(indexColumn);

                            rowColumns.Add(columnHeader, columnValue);
                        }

                        IRow row = new Row(rowKey, rowColumns);
                        rows.Add(row);
                    }

                    IRow[] results = rows.ToArray();
                    return results;
                }
                finally
                {
                    if (reader != null)
                    {
                        reader.Close();
                        reader = null;
                    }
                }
            }
            finally
            {
                if (commandQuery != null)
                {
                    commandQuery.Dispose();
                    commandQuery = null;
                }
            }
        }
        public override async Task Update(IPatch patch, string correlationIdentifier)
        {
            if (null == patch)
            {
                throw new ArgumentNullException(FileProvider.ArgumentNamePatch);
            }

            if (null == patch.ResourceIdentifier)
            {
                throw new ArgumentException(ProvisioningAgentResources.ExceptionInvalidPatch);
            }

            if (string.IsNullOrWhiteSpace(patch.ResourceIdentifier.Identifier))
            {
                throw new ArgumentException(ProvisioningAgentResources.ExceptionInvalidPatch);
            }

            if (null == patch.PatchRequest)
            {
                throw new ArgumentException(ProvisioningAgentResources.ExceptionInvalidPatch);
            }

            string informationStarting =
                string.Format(
                    CultureInfo.InvariantCulture,
                    AzureTestProvisioningResources.InformationPatching,
                    patch.ResourceIdentifier.SchemaIdentifier,
                    patch.ResourceIdentifier.Identifier);
            ProvisioningAgentMonitor.Instance.Inform(informationStarting, true, correlationIdentifier);

            PatchRequest2 patchRequest = patch.PatchRequest as PatchRequest2;
            if (null == patchRequest)
            {
                string unsupportedPatchTypeName = patch.GetType().FullName;
                throw new NotSupportedException(unsupportedPatchTypeName);
            }

            IRow row = await this.file.ReadRow(patch.ResourceIdentifier.Identifier);

            string rowSchema = null;
            if
            (
                    !row.Columns.TryGetValue(AttributeNames.Schemas, out rowSchema)
                ||  !string.Equals(rowSchema, patch.ResourceIdentifier.SchemaIdentifier, StringComparison.Ordinal)
            )
            {
                return;
            }

            IReadOnlyDictionary<string, string> columns;
            WindowsAzureActiveDirectoryGroup group = null;
            switch (rowSchema)
            {
                case SchemaIdentifiers.Core2EnterpriseUser:
                    ResourceFactory<Core2EnterpriseUser> userFactory = new UserFactory(row);
                    Core2EnterpriseUser user = userFactory.Create();
                    user.Apply(patchRequest);
                    ColumnsFactory<Core2EnterpriseUser> userColumnsFactory = new UserColumnsFactory(user);
                    columns = userColumnsFactory.CreateColumns();
                    break;

                case SchemaIdentifiers.WindowsAzureActiveDirectoryGroup:
                    ResourceFactory<WindowsAzureActiveDirectoryGroup> groupFactory = new GroupFactory(row);
                    group = groupFactory.Create();
                    group.Apply(patchRequest);
                    ColumnsFactory<WindowsAzureActiveDirectoryGroup> groupColumnsFactory = new GroupColumnsFactory(group);
                    columns = groupColumnsFactory.CreateColumns();
                    break;
                default:
                    throw new NotSupportedException(patch.ResourceIdentifier.SchemaIdentifier);
            }

            IRow rowReplacement = new Row(row.Key, columns);
            await this.file.ReplaceRow(rowReplacement);

            if (group != null)
            {
                await this.UpdateMembers(group, patch);
            }
        }
        private async Task<IRow> InsertRow(string key, IReadOnlyDictionary<string, string> columns)
        {
            logger.Info("inserting row to file: " + columns.ToString());

            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentNullException(CommaDelimitedFileAdapter.ArgumentNameRow);
            }

            if (null == columns)
            {
                throw new ArgumentNullException(CommaDelimitedFileAdapter.ArgumentNameColumns);
            }

            IDictionary<string, string> valuesByNormalizedNames =
                columns
                .ToDictionary(
                    (KeyValuePair<string, string> item) =>
                        new Value(item.Key).ToString().ToUpperInvariant(),
                    (KeyValuePair<string, string> item) =>
                        item.Value);

            IList<string> values = new List<string>(this.headers.Count);

            string keyNormalized = new Value(key).ToString();
            values.Add(keyNormalized);

            IEnumerable<string> normalizedHeaders = this.headersNormalized.Skip(1);
            foreach (string normalizedHeader in normalizedHeaders)
            {
                string value = null;
                if (!valuesByNormalizedNames.TryGetValue(normalizedHeader, out value))
                {
                    values.Add(string.Empty);
                    continue;
                }

                string normalizedValue = new Value(value).ToString();
                values.Add(normalizedValue);
            }

            string row =
                string.Join(CommaDelimitedFileAdapter.Comma, values);

            AsyncSemaphore.Releaser? releaser = null;
            try
            {
                releaser = await this.semaphore.EnterAsync();

                StreamWriter writer = null;
                try
                {
                    writer = File.AppendText(this.FilePath);
                    logger.Info("inserting row: " + row);
                    await writer.WriteLineAsync(row);
                }
                finally
                {
                    if (writer != null)
                    {
                        writer.Close();
                        writer = null;
                    }
                }
            }
            finally
            {
                if (releaser.HasValue)
                {
                    releaser.Value.Dispose();
                    releaser = null;
                }
            }

            IRow result = new Row(keyNormalized, columns);
            return result;
        }
        private static IRow FilterColumns(IRow row, IReadOnlyCollection<string> columnNames)
        {
            if (null == row)
            {
                throw new ArgumentNullException(FileProvider.ArgumentNameRow);
            }

            if (null == columnNames)
            {
                throw new ArgumentNullException(FileProvider.ArgumentNameColumnNames);
            }

            Dictionary<string, string> columns;
            if (row.Columns != null)
            {
                columns =
                    row
                    .Columns
                    .Where(
                        (KeyValuePair<string, string> columnItem) =>
                            columnNames
                            .Any(
                                (string columnNameItem) =>
                                    string.Equals(
                                        columnNameItem,
                                        columnItem.Key,
                                        StringComparison.Ordinal)))
                    .ToDictionary(
                        (KeyValuePair<string, string> item) =>
                            item.Key,
                        (KeyValuePair<string, string> item) =>
                            item.Value);
            }
            else
            {
                columns = null;
            }

            IRow result = new Row(row.Key, columns);
            return result;
        }