/// <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; } } }
public SolutionAccessor(SQLitePersistentStorage storage) : base(storage) { }
public Accessor(SQLitePersistentStorage storage) { Storage = storage; }
public PooledConnection(SQLitePersistentStorage sqlitePersistentStorage, SqlConnection sqlConnection) { this.sqlitePersistentStorage = sqlitePersistentStorage; Connection = sqlConnection; }