diff --git a/Source/BSN.Commons.PresentationInfrastructure/BSN.Commons.PresentationInfrastructure.csproj b/Source/BSN.Commons.PresentationInfrastructure/BSN.Commons.PresentationInfrastructure.csproj index eccb3bc..dda0e7f 100644 --- a/Source/BSN.Commons.PresentationInfrastructure/BSN.Commons.PresentationInfrastructure.csproj +++ b/Source/BSN.Commons.PresentationInfrastructure/BSN.Commons.PresentationInfrastructure.csproj @@ -17,6 +17,7 @@ + diff --git a/Source/BSN.Commons.PresentationInfrastructure/Converters/JsonForceDefaultConverter.cs b/Source/BSN.Commons.PresentationInfrastructure/Converters/JsonForceDefaultConverter.cs new file mode 100644 index 0000000..a333444 --- /dev/null +++ b/Source/BSN.Commons.PresentationInfrastructure/Converters/JsonForceDefaultConverter.cs @@ -0,0 +1,24 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace BSN.Commons.Converters +{ + /// + /// Force to keep numeral object or value, while convert an object or value to or from JSON. + /// for example 'StatusCode' in Response that should stay numeral. + /// + /// The type of object or value handled by the converter. + public class JsonForceDefaultConverter : JsonConverter + { + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return JsonSerializer.Deserialize(ref reader); + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value); + } + } +} diff --git a/Source/BSN.Commons.PresentationInfrastructure/Responses/GenericResponseBase.cs b/Source/BSN.Commons.PresentationInfrastructure/Responses/GenericResponseBase.cs new file mode 100644 index 0000000..5710686 --- /dev/null +++ b/Source/BSN.Commons.PresentationInfrastructure/Responses/GenericResponseBase.cs @@ -0,0 +1,13 @@ +namespace BSN.Commons.Responses +{ + /// + /// Add data as given type to response class. + /// + /// The type of object or value handled by the class. + public class GenericResponseBase : Response where T : class + { + public GenericResponseBase() { } + + public T Data { get; set; } + } +} diff --git a/Source/BSN.Commons.PresentationInfrastructure/Responses/GenericResponseBaseWithPagination.cs b/Source/BSN.Commons.PresentationInfrastructure/Responses/GenericResponseBaseWithPagination.cs new file mode 100644 index 0000000..80edcb1 --- /dev/null +++ b/Source/BSN.Commons.PresentationInfrastructure/Responses/GenericResponseBaseWithPagination.cs @@ -0,0 +1,17 @@ +namespace BSN.Commons.Responses +{ + /// + /// Generic response base for paginated data. + /// + /// + /// Paginated response provides metadata for navigation purpose. + /// + /// Data type. + public class GenericResponseBaseWithPagination : GenericResponseBase where T : class + { + /// + /// Pagination metada used by the client as the parameters for navigation through whole records. + /// + public PaginationMetadata Meta { get; set; } + } +} diff --git a/Source/BSN.Commons.PresentationInfrastructure/Responses/PaginationMetadata.cs b/Source/BSN.Commons.PresentationInfrastructure/Responses/PaginationMetadata.cs new file mode 100644 index 0000000..f57abdc --- /dev/null +++ b/Source/BSN.Commons.PresentationInfrastructure/Responses/PaginationMetadata.cs @@ -0,0 +1,28 @@ +namespace BSN.Commons.Responses +{ + /// + /// Stores the paginated meta data. + /// + public class PaginationMetadata + { + /// + /// Current page number + /// + public uint Page { get; set; } + + /// + /// Total number of pages + /// + public uint PageCount { get; set; } + + /// + /// Number of records per page + /// + public uint PageSize { get; set; } + + /// + /// Total number of records that exist + /// + public uint RecordCount { get; set; } + } +} diff --git a/Source/BSN.Commons.PresentationInfrastructure/Responses/Response.cs b/Source/BSN.Commons.PresentationInfrastructure/Responses/Response.cs new file mode 100644 index 0000000..3524f93 --- /dev/null +++ b/Source/BSN.Commons.PresentationInfrastructure/Responses/Response.cs @@ -0,0 +1,19 @@ +using BSN.Commons.Converters; +using BSN.Commons.PresentationInfrastructure; +using System.Text.Json.Serialization; + +namespace BSN.Commons.Responses +{ + /// + /// During serialization and deserialization operations, enumeration values are always converted to and from strings. + /// This is possible through registering the 'JsonStringEnumConverter' in our DI infrastructure. There is once exception, + /// namely the 'StatusCode' property of the 'ResponseBase' class which should keep it's default numeral value when being converted. + /// + public class Response : ResponseBase + { + public new bool IsSuccess => (int)StatusCode >= 200 && (int)StatusCode <= 299; + + [JsonConverter(typeof(JsonForceDefaultConverter))] + public new ResponseStatusCode StatusCode { get; set; } + } +} diff --git a/Source/BSN.Commons/Dto/PagedEntityCollection.cs b/Source/BSN.Commons/Dto/PagedEntityCollection.cs new file mode 100644 index 0000000..5b3c28c --- /dev/null +++ b/Source/BSN.Commons/Dto/PagedEntityCollection.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; + +namespace BSN.Commons.Dto +{ + /// + /// Stores the paginated data for the given entity type. + /// + /// Type of Entity for which pagination is being implemented. + public class PagedEntityCollection + { + /// + /// Current page number + /// + public uint CurrentPage { get; set; } + + /// + /// Total number of pages + /// + public uint PageCount { get; set; } + + /// + /// Number of records per page + /// + public uint PageSize { get; set; } + + /// + /// Total number of records that exist for the query + /// + public uint RecordCount { get; set; } + + /// + /// IEnumerable of the paginated data + /// + public IEnumerable Results { get; set; } + } +} diff --git a/Source/BSN.Commons/Extensions/IQueryableExtensions.cs b/Source/BSN.Commons/Extensions/IQueryableExtensions.cs new file mode 100644 index 0000000..68925fa --- /dev/null +++ b/Source/BSN.Commons/Extensions/IQueryableExtensions.cs @@ -0,0 +1,34 @@ +using BSN.Commons.Dto; +using System; +using System.Linq; + +namespace BSN.Commons.Extensions +{ + public static partial class IQueryableExtensions + { + /// + /// Paginate IQueryable of + /// with given pageNumber and pageSize + /// + public static PagedEntityCollection Paginate(this IQueryable query, uint pageNumber, uint pageSize) + { + if (pageNumber == 0) + throw new ArgumentException("Must be greater than zero.", nameof(pageNumber)); + + if (pageSize == 0) + throw new ArgumentException("Must be greater than zero.", nameof(pageSize)); + + var result = new PagedEntityCollection + { + CurrentPage = pageNumber, + PageSize = pageSize, + RecordCount = (uint)query.Count(), + Results = query.Skip((int)((pageNumber - 1) * pageSize)).Take((int)pageSize).ToList() + }; + + result.PageCount = (uint)Math.Ceiling((double)result.RecordCount / pageSize); + + return result; + } + } +}