LSCore.Repository
1.0.4
See the version list below for details.
dotnet add package LSCore.Repository --version 1.0.4
NuGet\Install-Package LSCore.Repository -Version 1.0.4
<PackageReference Include="LSCore.Repository" Version="1.0.4" />
paket add LSCore.Repository --version 1.0.4
#r "nuget: LSCore.Repository, 1.0.4"
// Install LSCore.Repository as a Cake Addin #addin nuget:?package=LSCore.Repository&version=1.0.4 // Install LSCore.Repository as a Cake Tool #tool nuget:?package=LSCore.Repository&version=1.0.4
LSCore
Free and open-source .NET Api framework
A .NET libraries which makes building your API faster and easier. Using DI to implement managers, mappers, validators, converts and others for the faster building process.
To effectively use this framework, follow your project infrastructure as described bellow
For whole Framework to work, install LSCore.Contracts, LSCore.Domain, LSCore.Framework and LSCore.Repository (if you have database)
Api structure
Base structure of API using this framework should contains 4 projects.
- YourProject.Api (Web Api Project)
- YourProject.Contracts (Class Library)
- YourProject.Domain (Class Library)
- YourProject.Repository (Class Library, if you are communicating with database)
.Api
Used to build controllers with endpoints which will call implementations from class library.
Should not contain any logic inside it.
This project must reference:
LSCore.Framework
YourProject.Domain
YourProject.Repository
Important: If you do not reference both Domain
and Repository
proejcts, Dependency Injection will not find Managers!
Important: Endpoints inside your controllers should only be used to catch request and call managers method which contains all the logic, valiadtions, request handling and building resposne.
Only folder you should have in this project is Controllers
which contains your controllers.
.Contracts
Used to declare entity classes, enumerators, manager interfaces, dtos, constants, static classes, dto mappers etc.
This project must reference:
LSCore.Contracts
Important: Do not implement any logic here. Classes should be only pure declaration of properties!
Common folders you should have in this project are:
- DtoMappings
- Dtos
- Entities
- Enums
- Helpers
- IManagers
- Requests
and a Constants.cs
which is used to store all constants used for this API
.Domain
Used to declare all the logic and implementations in your project.
This project must reference:
LSCore.Domain
YourProject.Contracts
YourProject.Repository
(if you have database)
Common folders you should have in this project are:
- Managers
- Validators
.Repository
Used to implement everything regarding your database. If your API do not include communication with database, you do not need this project.
This proejct must reference:
LSCore.Repository
Common folders you should have in this project are:
- DbMappings
- Queries
- Commands
- Filters
and single [YourDatabase]DbContext.cs
file used to declare database structure.
Features
- Responses
- Managers (base & database)
- Request Validators
Responses
Each API endpoint should return some value. You should always return one of these objects:
- LSCoreResponse
- LSCoreResponse<TPayload>
- LSCoreListResponse<TPayload>
- LSCorePaginatedResponse<TPayload> (To be implemented)
All the responses have this JSON structure:
{
status,
notOk,
payload,
errors
}
General rule is your API will always return either status 200 or 500 (Internal Server Error) (or other like 404 if route not found or other 500 error). If it returns 200, that means your API did process client application request and then front end application can parse response in structure described above and check status
to see exact status of that response (200, 201, 400, 404...)
LSCoreResponse is used to return response without any payload
value. It is used to return just simple plain status.
If request is successfull, in inner status you will return 200. If request does not pass validation or you do not want to proceede with request because of some logic annomally, you will return 400 (validation automatically does this for you) with some error messages (see usage of responses bellow).
LSCoreResponse<TPayload> is used same way as LSCoreResponse
however you will use this if you want to return something inside payload
property.
LSCoreListResponse<TPayload> is used same way as LSCoreResponse<TPayload>
, however if you are returning list of objects, do not return LSCoreResponse<List<Class>>
but LSCoreListResponse<class>
.
Example of method which builds response:
public LSCoreResponse<string> ToUppercase(StringRequest request)
{
// This create response object with default properties values like this:
// status: 200
// notOk: false,
// payload: null,
// errors: null
var response = new LSCoreResponse<string>();
// This line validates request.
// Validator is created in separate place (will be covered later) and Automatically
// Scanned and implemented, so no need for additional code except this.
// If validation fails, response will be changed like this:
// status: 200 > 400
// notOk: false > true
// errors: new List<string>()
// {
// "Message we declared in validator",
// "Message number 2 if more than one validation failed",
// ...
// }
if(request.IsInvalid(response))
return response;
// This lanes populate Payload inside response with our logic
response.Payload = request.Value.ToString();
return response;
}
In controller, implementation would look like this:
[HttpGet]
[Route("/string-to-uppercase")]
public LSCoreResponse<string> GetToUppercase(StringRequest request)
{
return _stringManager.ToUppercase(request);
}
All endpoints body should look like this. Only call to managers method.
Managers
Managers are used to implement logic which will be used by controllers.
To create valid manager you need to do two things.
First is to create interface for the manager inside .Contracts.IManagers
folder
Second is to create implementation of the manager inside .Domain.Managers
which inherits the interface.
// Location .Contracts.IManagers
public interface IUserManager
{
LSResponse Logout();
}
// Location .Domain.managers
public class UserManager : LSCoreBaseManager<UserManager>, IUserManager
{
public UserManager(ILogger<UserManager> logger)
: base(logger)
{
}
public LSCoreResponse Logout()
{
// Implementation
}
}
Important thing is naming convention of manager and its interface. If you create UserManager
, interface must be same name prefixed with I
> IUserManager
. If it differs by 1 letter, Dependency Injection will not automatically work.
Manager will be automatically picked up and available using DI, so to use it simple catch its interface in constructor like this:
public class UsersController : ControllerBase
{
private readonly IUserManager _userManager;
public UsersController(IUserManager userManager)
{
_userManager = userManager;
}
[HttpGet]
[Route("/logout")]
public LSCoreResponse Logout()
{
return _userManager.Logout();
}
}
If you want to implement manager which communicates with database table, instead of LSBaseManager
use LSBaseTableManager
like so:
public class UserManager : LSCoreBaseTableManager<UserManager, UserEntity>, IUserManager
{
public UserManager(ILogger<UserManager> logger, YourDatabaseDbContext dbContext) // dbContextClass is declared inside `.Repository.YourDatabaseDbContext.cs`
: base(logger, dbContext)
{
}
public LSCoreResponse Logout()
{
// Implementation
}
}
Both BaseManager
and BaseTableManager
(if you want BaseManager
to access it, you need to catch DbContext and pass it to :base()
just like it is done in BaseTableManager
) and they have similiar methods.
Difference is when using BaseTableManager
we do not need to specify entity we are working with, since we already declared it when declaring class as second type parameter `: LSCoreBaseTableManager<ManagerClass, EntityClass>.
Bellow is example of some methods available in managers. Non-generic methods are available only to BaseTableManager
while others are alvailable to both.
public class UserManager : LSCoreBaseTableManager<UserManager, UserEntity>, IUserManager
{
public UserManager(ILogger<UserManager> logger, YourDatabaseDbContext dbContext)
: base(logger, dbContext)
{
}
public LSCoreResponse Logout()
{
// Implementation
}
public LSCoreResponse<UserDto> Get(IdRequest request) // IdRequest is part of LSCore
{
var user = FirstOrDefault(x => x.Id == request.Id); // Method from BaseTableManager
if(user == null)
return LSCoreResponse<UserDto>.NotFound(); // Static metod from LSCoreResponse
var blockedUser = FirstOrDefault<BlockedUser>(x => x.Id == request.Id); // Generic FirstOrDefault method available in both managers and will work on table specified in generic type.
if(blockedUser == null) // Selected user is not found in BlockedUser table, we will return it
return new LSCoreResponse<UserDto>(user.ToDto()); // .ToDto(this UserEntity userEntity) is extension you need to declare in .Contracts.DtoMappings.UserDtoMappings.cs static class
// If came here, then user is found inside BlockedUser
return LSCoreResponse<UserDto>.BadRequest(string.format(UsersValidationCodes.UVC_001, blockedUser.Reason)); // this will return inner status of 400, with message = new List<string>() { "This user is blocked for the reasion: {0}" }
}
}
Requests validation
To validate request use .IsInvalid(responseBuffer)
extension which rules are using FluentValidation.
This extensions work on classes which inherit ILSCoreRequest
interface, so make sure your request class does so.
To create validator for your request do this:
First create request class inside .Contracts.Requests
like so:
public class StringRequest : ILSCoreRequest
{
public string Value { get; set; }
}
Then create StringRequestValidator
class inside .Domain.Validators
like so:
// Your class must inherit `LSCoreValidatorBase<TRequestClassToValidate>`
public class StringRequestValidator : LSCoreValidatorBase<StringRequest>
{
public StringRequestValidator() : base()
{
RuleFor(x => x.Value)
.NotEmpty()
.MinLength(Constants.StringRequestValueMinLength) // Constants class is located in ProjectName.Contracts.Constants.cs
.WithMessage(string.Format(StringsValidationCodes.SRV_001.GetDescription(), nameof(StringRequest.Value), Constant.StringRequestMinLength)); // This message will be used only for first validator before.
}
}
And then call IsInvalid(response)
where you want to validate request like so:
public class StringManager : LSCoreBaseManager<StringManager>, IStringManager
{
// ...
public LSCoreResponse<string> ToLowercase(StringRequest request)
{
var response = new LSCoreResponse<string>();
if(request.IsInvalid(response))
return response;
// Implementation
}
}
Inside validator we called line .WithMessage(StringsValidationCodes.SRV_001.GetDescription())
.
This is enum which you sould create inside ProjectName.Contracts.Enums.ValidationCodes.StringsValidationCodes
and should look like this:
public partial class ValidationCodes // All validations class should share same class
{
public enum StringsValidationCodes
{
[Description("Property '{0}' must have length greater than '{1}'")]
SRV_001,
}
}
Note: Fluent validation have good handling with default messages and for the MinLength
we did not need to declare custom message since it handles it good, however it is just used for example purpose.
// TODO: More documentation
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net7.0
- LSCore.Contracts (>= 1.0.4)
- Microsoft.EntityFrameworkCore (>= 7.0.13)
- Microsoft.Extensions.Configuration.Abstractions (>= 7.0.0)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 6.0.8)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.4.0 | 92 | 7/22/2024 |
1.3.2 | 1,230 | 7/5/2024 |
1.3.1 | 101 | 6/29/2024 |
1.3.0 | 272 | 6/24/2024 |
1.2.1110 | 92 | 6/24/2024 |
1.2.119 | 98 | 6/22/2024 |
1.2.118 | 95 | 6/22/2024 |
1.2.117 | 108 | 6/22/2024 |
1.2.116 | 121 | 6/17/2024 |
1.2.115 | 85 | 6/13/2024 |
1.2.114 | 100 | 6/12/2024 |
1.2.13 | 91 | 6/24/2024 |
1.2.8 | 82 | 6/12/2024 |
1.2.7 | 91 | 6/12/2024 |
1.2.6 | 85 | 6/11/2024 |
1.2.4 | 101 | 6/8/2024 |
1.2.3 | 102 | 6/3/2024 |
1.2.2 | 90 | 6/3/2024 |
1.2.1 | 95 | 6/3/2024 |
1.2.0 | 104 | 5/26/2024 |
1.1.3 | 1,002 | 3/19/2024 |
1.1.2 | 599 | 2/10/2024 |
1.1.1 | 95 | 2/10/2024 |
1.1.0 | 93 | 2/9/2024 |
1.0.41 | 429 | 1/11/2024 |
1.0.40 | 149 | 1/10/2024 |
1.0.39 | 106 | 1/10/2024 |
1.0.38 | 221 | 1/7/2024 |
1.0.37 | 228 | 12/24/2023 |
1.0.36 | 143 | 12/23/2023 |
1.0.35 | 131 | 12/23/2023 |
1.0.34 | 120 | 12/23/2023 |
1.0.33 | 120 | 12/23/2023 |
1.0.32 | 131 | 12/17/2023 |
1.0.31 | 138 | 12/13/2023 |
1.0.30 | 131 | 12/13/2023 |
1.0.29 | 142 | 12/10/2023 |
1.0.28 | 128 | 12/9/2023 |
1.0.27 | 136 | 12/9/2023 |
1.0.26 | 138 | 12/9/2023 |
1.0.25 | 135 | 12/9/2023 |
1.0.24 | 144 | 12/9/2023 |
1.0.23 | 138 | 12/9/2023 |
1.0.22 | 155 | 12/3/2023 |
1.0.21 | 145 | 12/3/2023 |
1.0.20 | 139 | 12/3/2023 |
1.0.19 | 151 | 11/22/2023 |
1.0.18 | 136 | 11/16/2023 |
1.0.17 | 120 | 11/16/2023 |
1.0.16 | 135 | 11/10/2023 |
1.0.15 | 127 | 11/10/2023 |
1.0.14 | 133 | 11/10/2023 |
1.0.13 | 121 | 11/10/2023 |
1.0.12 | 130 | 11/10/2023 |
1.0.11 | 149 | 11/5/2023 |
1.0.10 | 136 | 11/5/2023 |
1.0.9 | 141 | 11/5/2023 |
1.0.8 | 137 | 11/5/2023 |
1.0.7 | 148 | 11/5/2023 |
1.0.6 | 144 | 11/5/2023 |
1.0.5 | 156 | 10/30/2023 |
1.0.4 | 144 | 10/30/2023 |
1.0.3 | 144 | 10/30/2023 |
1.0.2 | 122 | 12/16/2023 |
1.0.1 | 147 | 10/30/2023 |
1.0.1-alpha | 119 | 10/30/2023 |