CommandParameterValues GetDocumentParameters(Func <DocumentMap, string> allocateId, object customAssignedId, CustomIdAssignmentBehavior?customIdAssignmentBehavior, object document, DocumentMap mapping, string prefix = null)
        {
            var id = mapping.IdColumn.PropertyHandler.Read(document);

            if (customIdAssignmentBehavior == CustomIdAssignmentBehavior.ThrowIfIdAlreadySetToDifferentValue &&
                customAssignedId != null && id != null && customAssignedId != id)
            {
                throw new ArgumentException("Do not pass a different Id when one is already set on the document");
            }

            if (mapping.IdColumn.Type == typeof(string) && string.IsNullOrWhiteSpace((string)id))
            {
                id = string.IsNullOrWhiteSpace(customAssignedId as string) ? allocateId(mapping) : customAssignedId;
                mapping.IdColumn.PropertyHandler.Write(document, id);
            }

            var result = new CommandParameterValues
            {
                [$"{prefix}{mapping.IdColumn.ColumnName}"] = id
            };

            switch (mapping.JsonStorageFormat)
            {
            case JsonStorageFormat.TextOnly:
                result[$"{prefix}{JsonVariableName}"] = serializer.SerializeText(document, mapping);
                break;

            case JsonStorageFormat.CompressedOnly:
                result[$"{prefix}{JsonBlobVariableName}"] = serializer.SerializeCompressed(document, mapping);
                break;

            case JsonStorageFormat.MixedPreferCompressed:
                result[$"{prefix}{JsonBlobVariableName}"] = serializer.SerializeCompressed(document, mapping);
                result[$"{prefix}{JsonVariableName}"]     = null;
                break;

            case JsonStorageFormat.MixedPreferText:
                result[$"{prefix}{JsonVariableName}"]     = serializer.SerializeText(document, mapping);
                result[$"{prefix}{JsonBlobVariableName}"] = null;
                break;

            case JsonStorageFormat.NoJson:
                // Nothing to serialize
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            foreach (var c in mapping.WritableIndexedColumns())
            {
                var value = c.PropertyHandler.Read(document);
                if (value != null && value != DBNull.Value && value is string && c.MaxLength != null && c.MaxLength.Value > 0)
                {
                    var attemptedLength = ((string)value).Length;
                    if (attemptedLength > c.MaxLength)
                    {
                        throw new StringTooLongException($"An attempt was made to store {attemptedLength} characters in the {mapping.TableName}.{c.ColumnName} column, which only allows {c.MaxLength} characters.");
                    }
                }
                else if (value != null && value != DBNull.Value && value is DateTime && value.Equals(DateTime.MinValue))
                {
                    value = SqlDateTime.MinValue.Value;
                }

                result[$"{prefix}{c.ColumnName}"] = value;
            }

            return(result);
        }