public MasterBuilderConfig LoadConfigClass(string configClassName)
        {
            Debug.Log($"ConfigClssName: {configClassName}");
            Type                configType  = LoadConfigClassType(configClassName);
            ConstructorInfo     constructor = configType.GetConstructor(new Type[] { });
            MasterBuilderConfig config      = (MasterBuilderConfig)constructor.Invoke(null);

            return(config);
        }
        public async Task <(DataPool DataPool, Dictionary <string, Dictionary <string, string> > AvailableSheetDictionary)> LoadAll()
        {
            var sw = new System.Diagnostics.Stopwatch();

            sw.Start();
            service = await OpenSheet();

            Debug.Log($"OpenSheet() Elapsed: {sw.ElapsedMilliseconds} ms");


            var availableSheetsDict    = GetAvailableSheetProperties(service).ToDictionary(e => e["Title"]);
            MasterBuilderConfig config = fsWrapper.LoadConfigClass(configClassName);
            List <Type>         availableTableTypes = config.MasterTables()
                                                      .Where(e =>
            {
                var contains = availableSheetsDict.ContainsKey(e.Name);
                if (!contains)
                {
                    Debug.LogError($"Error: SheetNotFound. SheetName: {e.Name} URL:{GetSheetURL()}");
                }
                return(contains);
            }).ToList();
            List <string> availableSheets = availableTableTypes
                                            .Select(e => e.Name)
                                            .ToList();
            List <string> loadRanges = availableSheets
                                       .Select(e => $"{e}!A1:Z").ToList();

            ValuesResource.BatchGetRequest request = service.Spreadsheets.Values.BatchGet(spreadSheetId);
            request.Ranges = loadRanges;

            Debug.Log($"Send request to Google... Elapsed: {sw.ElapsedMilliseconds} ms");
            BatchGetValuesResponse response = request.Execute();

            Debug.Log($"Send request to Google... done. Elapsed: {sw.ElapsedMilliseconds} ms");
            var masterData = new MasterData();

            if (response != null && response.ValueRanges.Count > 0)
            {
                var tableCount = availableSheets.Count;
                for (var i = 0; i < tableCount; i++)
                {
                    var table     = response.ValueRanges[i];
                    var tableName = availableSheets[i];
                    var dataType  = availableTableTypes[i];

                    Debug.Log($"deserialize table({i + 1} / {tableCount}) tableName: {tableName} start. Elapsed: {sw.ElapsedMilliseconds} ms");

                    var            recordCount = table.Values.Count - 1;
                    IList <string> columns     = table.Values.FirstOrDefault().Select(e => e.ToString()).ToList();
                    if (recordCount < 1 || columns == null || columns.Count < 1)
                    {
                        continue;
                    }
                    var propertyDict = BuildPropertyDictionary(dataType);
                    var fieldDict    = BuildFieldAttributesDictionary(dataType);


                    var tasks = table.Values.Skip(1).Select((e, i) => Task.Run(() => {
                        var recordIndex = i - 1;
                        return(BuildRecord(columns, e.Select(e2 => e2.ToString()).ToList(), propertyDict, fieldDict, dataType, recordIndex));
                    }));


                    var rawRecords = await Task.WhenAll(tasks.ToArray());

                    masterData.Set(dataType, rawRecords.AsEnumerable());
                    Debug.Log($"deserialize table({i + 1} / {tableCount}) tableName: {tableName} done. Elapsed: {sw.ElapsedMilliseconds} ms");
                }
            }

            return(DataPool : masterData, AvailableSheetDictionary : availableSheetsDict);
        }