/// <summary>
        /// Returns 'true' if the bulk population succeeds, or false if it doesn't.
        /// </summary>
        private bool BulkPopulateProjectIdsWorker(SqlConnection connection, Project project)
        {
            // First, in bulk, get string-ids for all the paths and names for the project and documents.
            if (!AddIndividualProjectAndDocumentComponentIds())
            {
                return(false);
            }

            // Now, ensure we have the project id known locally.  We cannot do this until we've
            // gotten all the IDs for the individual project components as the project ID is built
            // from a compound key using the IDs for the project's FilePath and Name.
            //
            // If this fails for any reason, we can't proceed.
            var projectId = TryGetProjectId(connection, project);

            if (projectId == null)
            {
                return(false);
            }

            // Finally, in bulk, determine the final DB IDs for all our documents. We cannot do
            // this until we have the project-id as the document IDs are built from a compound
            // ID including the project-id.
            return(AddDocumentIds());

            // Local functions below.

            // Use local functions so that other members of this class don't accidentally use these.
            // There are invariants in the context of BulkPopulateProjectIdsWorker that these functions
            // can depend on.
            bool AddIndividualProjectAndDocumentComponentIds()
            {
                var stringsToAdd = new HashSet <string>();

                AddIfUnknownId(project.FilePath, stringsToAdd);
                AddIfUnknownId(project.Name, stringsToAdd);

                foreach (var document in project.Documents)
                {
                    AddIfUnknownId(document.FilePath, stringsToAdd);
                    AddIfUnknownId(document.Name, stringsToAdd);
                }

                return(AddStrings(stringsToAdd));
            }

            bool AddStrings(HashSet <string> stringsToAdd)
            {
                if (stringsToAdd.Count > 0)
                {
                    using (var idToString = s_dictionaryPool.GetPooledObject())
                    {
                        try
                        {
                            connection.RunInTransaction(() =>
                            {
                                foreach (var value in stringsToAdd)
                                {
                                    var id = InsertStringIntoDatabase_MustRunInTransaction(connection, value);
                                    idToString.Object.Add(id, value);
                                }
                            });
                        }
                        catch (SqlException ex) when(ex.Result == Result.CONSTRAINT)
                        {
                            // Constraint exceptions are possible as we may be trying bulk insert
                            // strings while some other thread/process does the same.
                            return(false);
                        }
                        catch (Exception ex)
                        {
                            // Something failed. Log the issue, and let the caller know we should stop
                            // with the bulk population.
                            StorageDatabaseLogger.LogException(ex);
                            return(false);
                        }

                        // We succeeded inserting all the strings.  Ensure our local cache has all the
                        // values we added.
                        foreach (var kvp in idToString.Object)
                        {
                            _stringToIdMap[kvp.Value] = kvp.Key;
                        }
                    }
                }

                return(true);
            }

            bool AddDocumentIds()
            {
                var stringsToAdd = new HashSet <string>();

                foreach (var document in project.Documents)
                {
                    // Produce the string like "projId-docPathId-docNameId" so that we can get a
                    // unique ID for it.
                    AddIfUnknownId(GetDocumentIdString(document), stringsToAdd);
                }

                // Ensure we have unique IDs for all these document string ids.  If we fail to
                // bulk import these strings, we can't proceed.
                if (!AddStrings(stringsToAdd))
                {
                    return(false);
                }

                foreach (var document in project.Documents)
                {
                    // Get the integral ID for this document.  It's safe to directly index into
                    // the map as we just successfully added these strings to the DB.
                    var id = _stringToIdMap[GetDocumentIdString(document)];
                    _documentIdToIdMap.TryAdd(document.Id, id);
                }

                return(true);
            }

            string GetDocumentIdString(Document document)
            {
                // We should always be able to index directly into these maps.  This function is only
                // ever called after we called AddIndividualProjectAndDocumentComponentIds.
                var documentPathId = _stringToIdMap[document.FilePath];
                var documentNameId = _stringToIdMap[document.Name];

                var documentIdString = SQLitePersistentStorage.GetDocumentIdString(
                    projectId.Value, documentPathId, documentNameId);

                return(documentIdString);
            }

            void AddIfUnknownId(string value, HashSet <string> stringsToAdd)
            {
                // Null strings are not supported at all.  Just ignore these. Any read/writes
                // to null values will fail and will return 'false/null' to indicate failure
                // (which is part of the documented contract of the persistence layer API).
                if (value == null)
                {
                    return;
                }

                if (!_stringToIdMap.TryGetValue(value, out var id))
                {
                    stringsToAdd.Add(value);
                }
                else
                {
                    // We did know about this string.  However, we want to ensure that the
                    // actual string instance we're pointing to is the one produced by the
                    // rest of the workspace, and not by the database.  This way we don't
                    // end up having tons of duplicate strings in the storage service.
                    //
                    // So overwrite whatever we have so far in the table so we can release
                    // the DB strings.
                    _stringToIdMap[value] = id;
                }
            }
        }
Example #2
0
 public SolutionAccessor(SQLitePersistentStorage storage) : base(storage)
 {
 }
 public Accessor(SQLitePersistentStorage storage)
 {
     Storage = storage;
 }
Example #4
0
 public PooledConnection(SQLitePersistentStorage sqlitePersistentStorage, SqlConnection sqlConnection)
 {
     this.sqlitePersistentStorage = sqlitePersistentStorage;
     Connection = sqlConnection;
 }