public async Task <UpsertOutcome> UpsertAsync(ResourceWrapper resource, WeakETag weakETag, bool allowCreate, bool keepHistory, CancellationToken cancellationToken) { int?eTag = weakETag == null ? (int?)null : (int.TryParse(weakETag.VersionId, out var parsedETag) ? parsedETag : -1); // Set the etag to a sentinel value to enable expected failure paths when updating with both existing and nonexistent resources. var resourceMetadata = new ResourceMetadata( resource.CompartmentIndices, resource.SearchIndices?.ToLookup(e => _searchParameterTypeMap.GetSearchValueType(e)), resource.LastModifiedClaims); using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true)) using (SqlCommandWrapper sqlCommandWrapper = sqlConnectionWrapper.CreateSqlCommand()) using (var stream = new RecyclableMemoryStream(_memoryStreamManager)) { CompressedRawResourceConverter.WriteCompressedRawResource(stream, resource.RawResource.Data); stream.Seek(0, 0); PopulateUpsertResourceCommand(sqlCommandWrapper, resource, resourceMetadata, allowCreate, keepHistory, eTag, stream); try { var newVersion = (int?)await sqlCommandWrapper.ExecuteScalarAsync(cancellationToken); if (newVersion == null) { // indicates a redundant delete return(null); } resource.Version = newVersion.ToString(); return(new UpsertOutcome(resource, newVersion == 1 ? SaveOutcomeType.Created : SaveOutcomeType.Updated)); } catch (SqlException e) { switch (e.Number) { case SqlErrorCodes.PreconditionFailed: throw new PreconditionFailedException(string.Format(Core.Resources.ResourceVersionConflict, weakETag?.VersionId)); case SqlErrorCodes.NotFound: if (weakETag != null) { throw new ResourceNotFoundException(string.Format(Core.Resources.ResourceNotFoundByIdAndVersion, resource.ResourceTypeName, resource.ResourceId, weakETag.VersionId)); } goto default; case SqlErrorCodes.MethodNotAllowed: throw new MethodNotAllowedException(Core.Resources.ResourceCreationNotAllowed); default: _logger.LogError(e, "Error from SQL database on upsert"); throw; } } } }
public async Task <ResourceWrapper> GetAsync(ResourceKey key, CancellationToken cancellationToken) { using (SqlConnectionWrapper sqlConnectionWrapper = await _sqlConnectionWrapperFactory.ObtainSqlConnectionWrapperAsync(cancellationToken, true)) { int?requestedVersion = null; if (!string.IsNullOrEmpty(key.VersionId)) { if (!int.TryParse(key.VersionId, out var parsedVersion)) { return(null); } requestedVersion = parsedVersion; } using (SqlCommandWrapper commandWrapper = sqlConnectionWrapper.CreateSqlCommand()) { VLatest.ReadResource.PopulateCommand( commandWrapper, resourceTypeId: _model.GetResourceTypeId(key.ResourceType), resourceId: key.Id, version: requestedVersion); using (SqlDataReader sqlDataReader = await commandWrapper.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken)) { if (!sqlDataReader.Read()) { return(null); } var resourceTable = VLatest.Resource; (long resourceSurrogateId, int version, bool isDeleted, bool isHistory, Stream rawResourceStream) = sqlDataReader.ReadRow( resourceTable.ResourceSurrogateId, resourceTable.Version, resourceTable.IsDeleted, resourceTable.IsHistory, resourceTable.RawResource); string rawResource; using (rawResourceStream) { rawResource = await CompressedRawResourceConverter.ReadCompressedRawResource(rawResourceStream); } var isRawResourceMetaSet = sqlDataReader.Read(resourceTable.IsRawResourceMetaSet, 5); string searchParamHash = null; if (_schemaInformation.Current >= SchemaVersionConstants.SearchParameterHashSchemaVersion) { searchParamHash = sqlDataReader.Read(resourceTable.SearchParamHash, 6); } return(new ResourceWrapper( key.Id, version.ToString(CultureInfo.InvariantCulture), key.ResourceType, new RawResource(rawResource, FhirResourceFormat.Json, isMetaSet: isRawResourceMetaSet), null, new DateTimeOffset(ResourceSurrogateIdHelper.ResourceSurrogateIdToLastUpdated(resourceSurrogateId), TimeSpan.Zero), isDeleted, searchIndices: null, compartmentIndices: null, lastModifiedClaims: null, searchParamHash) { IsHistory = isHistory, }); } } } }