Sytadelle.Systems.Data
1.0.0-alpha.11
Prefix Reserved
dotnet add package Sytadelle.Systems.Data --version 1.0.0-alpha.11
NuGet\Install-Package Sytadelle.Systems.Data -Version 1.0.0-alpha.11
<PackageReference Include="Sytadelle.Systems.Data" Version="1.0.0-alpha.11" />
paket add Sytadelle.Systems.Data --version 1.0.0-alpha.11
#r "nuget: Sytadelle.Systems.Data, 1.0.0-alpha.11"
// Install Sytadelle.Systems.Data as a Cake Addin #addin nuget:?package=Sytadelle.Systems.Data&version=1.0.0-alpha.11&prerelease // Install Sytadelle.Systems.Data as a Cake Tool #tool nuget:?package=Sytadelle.Systems.Data&version=1.0.0-alpha.11&prerelease
Sytadelle.Systems
Sytadelle Systems est un système de base pour commencer un projet en .net. Il est composé de quatre parties : Entities, Data, Services et Api. Ces packages s'imbriquant entre eux (le package Entities est utilisé dans Data, Data est utilisé dans Services et Services est utilisé dans Api) :
- Dans Entities vous retrouverez les modèles de données de base pour chacune de vos entités.
- Ces entités seront accessibles en lecture et écriture via Data.
- La gestion des droits et de sécurité concernant la modification et l'ajout d'entités se fait dans Services.
- Enfin les endpoints sont définis dans Api via des controllers
On va présenter chacune de ces parties pour voir en quoi elles peuvent vous aider et comment vous pouvez les utiliser dans vos projets.
Sytadelle.Systems.Entities
Ce package utilise le code first avec SQLServer, dedans on y définit le modèle de donnée de base qui se nomme BaseEntity :
namespace Sytadelle.Systems.Entities
{
public abstract class BaseEntity
{
public long Id { get; set; }
public Guid Guid { get; set; } = Guid.NewGuid();
public bool Enabled { get; set; }
}
}
Toutes vos entités hériteront indirectement de cette classe.
Il y a un modèle de donnée pour l'entité qui jouera le rôle de gestion des droits dans le package Services : BaseTenant. Dans le cas ou vous n'aurez pas besoin de cloisonner vos entités par un tenant vous n'utiliserez pas cette classe.
namespace Sytadelle.Systems.Entities
{
public class BaseTenant : BaseEntity
{
}
}
Il y a un modèle de donnée pour les entités qui devront être cloisonné par un tenant : BaseTenantEntity. Dans le cas ou vous n'aurez pas besoin de cloisonner vos entités par un tenant vous utiliserez quand même cette classe avec comme paramètre BaseTenant, le tenant de vos entités sera dans ce cas toujours null.
namespace Sytadelle.Systems.Entities
{
public abstract class BaseTenantEntity<T> : BaseEntity
where T : BaseTenant
{
// tenant is the owner of the row, can be an organization
public T? Tenant { get; set; }
}
}
Un BaseUser est une entité qui va permettre de vérifier les droits de lecture et d'écriture d'un utilisateur sur une entité. Ainsi on sécurise certaines données en n'acceptant des modifications ou des suppressions sur entité uniquement si le tenant de celle-ci est le même que le BaseUser connecté. Dans un deuxième temps une vérification aura aussi lieu sur le rôle du BaseUser (User, Manager ou Admin). Dans le cas ou le tenant est null c'est uniquement le rôle qui est vérifié pour la validation d'une action de lecture ou écriture.
namespace Sytadelle.Systems.Entities
{
public abstract class BaseUser<T> : BaseTenantEntity<T>
where T : BaseTenant
{
public Role Role { get; set; }
}
}
Prenons l'exemple d'une application de blog où l'on retrouve des users, des profiles et des postes. Le profile possédera plusieurs postes et il sera le seul à pouvoir modifier ou supprimer ses propres postes. Mais un profile est lié à un User et c'est le user qui fera le pont entre le poste et le profile. Ainsi nous avons le profile qui est un BaseTenant, le poste qui est un BaseTenantEntity et le User qui est un BaseUser. Si lors d'une modification le user a le même tenant que le poste (et donc le même profile) alors la modification sera accordée.
Il est à noter qu'un BaseUser a un rôle et que cela peut déterminer ce qu'il pourra faire ou non sur la donnée. On peut notamment gérer ça grâce à l'entité Permission.
namespace Sytadelle.Systems.Entities.Permissions
{
public class Permission : BaseEntity
{
public string Name { get; set; }
/// <summary>
/// pattern : * or specific entity name
/// </summary>
public string Entity { get; set; }
public int Priority { get; set; }
public bool Create { get; set; }
public bool Read { get; set; }
public bool Update { get; set; }
public bool Delete { get; set; }
// compare with user role to determine the user permission over the specific entity
public Role Role { get; set; }
}
}
L'entité Permission permet de définir pour chaque entité différentes permissions en fonction des rôles des BaseUser. Si l'on reprend l'exemple du blog mais en rajoutant une nuance : plusieurs users peuvent accéder au même profile mais avec des rôles différents. Dans ce cas certains users seront admin tandis que d'autres seront de simple user, les admins pourront modifier, ajouter ou supprimer des données tandis que les simple users ne pourront que les voirs.
Enfin vous pourrez suivre l'évolution de vos données grâce aux évènements. Pour cela vous utiliserez la classe BaseEvent :
namespace Sytadelle.Systems.Entities.Audits
{
public abstract class BaseEvent<T, Tenant, TUser> : BaseTenantEntity<Tenant>
where T : BaseEntity
where Tenant : BaseTenant
where TUser : BaseUser<Tenant>
{
public T? Record { get; set; }
public EventType Type { get; set; }
public DateTime? DateTime { get; set; }
/// <summary>
/// As BaseUser is an abstract class should be overriden by a concret User model
/// </summary>
public TUser? By { get; set; }
}
public enum EventType
{
Created,
Read,
Updated,
Deleted,
Enabled
}
}
Ainsi si vous créez une entité Post vous pouvez également créer une entité PostEvent qui héritera de BaseEvent. Cela vous permettra de suivre chaque modification apportée à de vos entité.
Sytadelle.Systems.Data
On définit dans ce package un système de repository : la classe BaseRepository ainsi que son interface IBaseRepository contiennent diverses fonctions qui permettent d'ajouter, de modifier ou de récupérer des entités.
namespace Sytadelle.Systems.Data
{
public interface IBaseRepository<TEntity> : IDisposable where TEntity : BaseEntity
{
IQueryable<TEntity> Query(Expression<Func<TEntity, bool>>? predicate = null, long? tenantId = null, bool tracking = true, bool? enabled = true);
IQueryable<TEntity> DynamicQuery(string q, string order, long? tenantId, bool? enabled = true);
TEntity? GetById(long id);
TEntity? GetById(Guid guid);
void Add(object obj);
void Add(TEntity entity);
void Delete(TEntity entity);
void Update(TEntity entity);
bool NavigationsHaveSameTenantId(TEntity entity, long? tenantId);
int SaveChanges();
long? GetSubEntityValue(TEntity entity, string property);
}
}
Dans le cas où vous utilisez un tenant vous pourrez utiliser certaines méthodes du BaseRepository dans vos services. Il y a notamment la méthode "NavigationsHaveSameTenantId" qui permet de vérifier que tous les attributs de type BaseTenantEntity d'une entité ont bien le même tenant. On ne pourra pas, par exemple, ajouter un poste sur un profile (ici notre tenant) qui n'est pas lié à l'utilisateur puisque l'utilisateur doit être lié au même profile que notre poste.
Un système de pagination, en dehors du BaseRepository, est également présent dans le package mais n'est utilisé que dans le package Services. Vous pouvez cependant override les fonctions du BaseRepository pour inclure la pagination si vous n'utilisez pas le package Services.
namespace Sytadelle.Systems.Data
{
public class PagedList<T> : List<T> where T : BaseEntity
{
public const int DefaultSize = 25;
public int TotalPages { get; set; }
public int CurrentPage { get; set; }
public int TotalCount { get; set; }
public PagedList(List<T> items, int pageNumber, int pageSize, int count)
{
CurrentPage = pageNumber;
TotalCount = count;
TotalPages = (int)Math.Ceiling((double)count / pageSize);
AddRange(items);
}
public static async Task<PagedList<T>> GetByPageAsync(IQueryable<T> query, int pageNumber, int pageSize)
{
var count = await query.CountAsync();
if (pageNumber <= 0)
{
pageNumber = 1;
}
if (pageSize <= 0 || pageSize > DefaultSize)
{
pageSize = DefaultSize;
}
var results = await query.Skip(pageSize * (pageNumber - 1))
.Take(pageSize)
.ToListAsync();
return new PagedList<T>(results, pageNumber, pageSize, count);
}
}
}
Sytadelle.Systems.Services
Ce package définit un système de sécurité de base (en lien avec les permissions et les tenants qu'on a vu juste avant). On va y faire des vérifications concernant l'utilisateur connecté (si celui est null alors il n'y aura pas de vérification, vous pouvez override les fonctions de ce package pour qu'elle ne fonctionne que s'il y a un user' ) et les tenants. Vous retrouverez toutes les fonctionnalités de base : Add, Update, Patch ou encore Delete. Dans ce package le Delete est un soft Delete : on passe le Enabled de l'entité à false quand on Delete. Cela permet de garder en mémoire les données et de conserver un historique. Vous pouvez implémenter un hard Delete si vous le souhaitez. Vos services, exceptés pour les events, pourront hériter de BaseService ;
namespace Sytadelle.Systems.Services
{
public interface IBaseService<TEntity, Tenant> : IDisposable
where TEntity : BaseEntity
where Tenant : BaseTenant
{
Task<ServiceResult<TEntity>> GetByQueryAsync(
int p, int size, string? q, string? order, Expression<Func<TEntity, bool>>? predicate, bool? enabled);
ServiceResult<TEntity> GetById(string id);
ServiceResult<TEntity> Add(TEntity entity);
ServiceResult<TEntity> Update(string id, TEntity entity);
ServiceResult<TEntity> Delete(string id);
ServiceResult<TEntity> Patch(string id, JsonPatchDocument<TEntity> patchDocument);
}
...
}
Vos entités event hériteront de la classe BaseEventService dans laquelle est défini uniquement une fonction Add. Ainsi les events ne peuvent pas être modifiés ou supprimés, ils ne peuvent qu'être ajoutés.
La création des évènements est aussi réalisé dans ce package : quand vous ajoutez, modifiez ou supprimez une donnée via les méthodes de Services un évènement va se créer dans la table de l'event de l'entité si vous l'avez créée dans vos entités. Par exemple pour avoir automatiquement des évènements sur des données postes il faut créer une entité de type BaseEvent<Post>. La pagination est réalisée au sein de ce package en utilisant la méthode GetByQueryAsync de BaseService, les deux premiers paramètres de cette fonction sont le numéro de la page et le nombre d'éléments dans cette page. De cette façon si on veut récupérer la troisième page des postes et qu'on souhaite récupérer 20 éléments par page alors les deux premiers paramètres seront 3 et 20. Par défaut la taille d'une page est de 25.
Sytadelle.Systems.Controller
Ce package définit les endpoints de base. Ces endpoints appeleront directemnet les méthodes de BaseService. Vos controllers pourront hériter de la classe BaseController :
namespace Sytadelle.Systems.Api.Controllers
{
[Route("api/[controller]")]
[ApiController]
public abstract class BaseController<TEntity, Tenant> : ControllerBase
where TEntity : BaseEntity
where Tenant : BaseTenant
{
private readonly IBaseService<TEntity, Tenant> _baseService;
public BaseController(IBaseService<TEntity, Tenant> baseService)
{
_baseService = baseService;
}
[HttpGet("{id}")]
public virtual IActionResult GetById(string id)
{
return Ok(_baseService.GetById(id));
}
[HttpGet]
public virtual async Task<IActionResult> GetAll(
[FromQuery] int p, [FromQuery] int size, [FromQuery] string? q, [FromQuery] string? order)
{
return Ok(await _baseService.GetByQueryAsync(p, size, q, order, null, null));
}
[HttpPost]
public virtual IActionResult Add([FromBody] TEntity entity)
{
return Ok(_baseService.Add(entity));
}
[HttpPut("{id}")]
public virtual IActionResult Update(string id, [FromBody] TEntity entity)
{
return Ok(_baseService.Update(id, entity));
}
[HttpDelete("{id}")]
public virtual IActionResult Delete(string id)
{
return Ok(_baseService.Delete(id));
}
}
}
Built With
- ASP.NET Core
- EntityFramework Core
- Dynamic Linq
Getting Started
Sytadelle.Systems.Api is a basic system to start a project in .net. The goal is to start quickly and easily a .net project with this package. In fact many basics implementations are already done (like the pagination or some basics security for your data). So with Sytadelle.Systems.Api you will easily create an API which can implement CRUD on all data of your app. You can also use it for differents needs and it is easily adaptable.
It is important to notice that Sytadelle.Systems.Api used three others Sytadelle packages : Entities, Data, Services. These packages intertwining with each other (the Entities package is used in Data, Data is used in Services and Services is used in Api) :
- In the Entities package you will find basics data models for each of your entities.
- We can read and write theses entities with the Data package.
- The management of the rights and security about the modification and the addition of entities is made in Services package.
- The endpoints are defined in Api package with controllers
In resume : You can call endpoints to get or modify your data, the endpoint will call the service (Api package) ⇒ The service will verify if we have the rights for the request, if it is the case the service will call repository (Services) ⇒ The repository will return or modify the data requested (Data). The data has the form you gave it in entities
Now, for this first tutorial, we will create a basic application using Sytadelle.Systems.Api:
Prerequisites
Open Visual Studio (install it if you don't have it, https://visualstudio.microsoft.com/downloads/ ). ⇒ We use Visual Studio because it's the better IDE to code a .net project.
Installation
- Create a new project ASP.NET Core Web API using .net6
- Delete WeatherForecast.cs file and the file WeatherForecastController.cs which is in Controllers folder
- Go in manage nuget packages and install Sytadelle.Systems.Api ⇒ You will access, through Sytadelle.Systems.Api, to the packages Sytadelle.Systems.Entities, Sytadelle.Systems.Data and Sytadelle.Systems.Services.
- Install the package "Microsoft.EntityFrameworkCore.Tools" too, we will need it to run some commands to update our database with our models
Usage
A sample project can be found in the folder Sytadelle.Systems.Samples.EFCore which use basically the sytadelle entities, data framework to build a code first SQL Server application. More samples come later.
Roadmap
Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
License
Distributed under the Sytadelle License. See LICENSE.txt
for more information.
Contact
contact@sytadelle.fr
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. 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. |
-
net8.0
- Microsoft.EntityFrameworkCore (>= 7.0.5)
- Microsoft.EntityFrameworkCore.Relational (>= 7.0.5)
- Microsoft.Extensions.Logging (>= 7.0.0)
- System.Linq.Dynamic.Core (>= 1.3.2)
- Sytadelle.Systems.Entities (>= 1.0.0-alpha.11)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Sytadelle.Systems.Data:
Package | Downloads |
---|---|
Sytadelle.Systems.Services
a comprehensive library to ease automation of API delivery |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
1.0.0-alpha.11 | 406 | 5/13/2024 | |
1.0.0-alpha.9 | 3,667 | 10/24/2023 | |
1.0.0-alpha.8 | 77 | 9/25/2023 | |
1.0.0-alpha.7 | 85 | 8/3/2023 | |
1.0.0-alpha.6 | 89 | 8/3/2023 | |
1.0.0-alpha.5 | 83 | 8/3/2023 | |
1.0.0-alpha.4 | 97 | 7/24/2023 | |
1.0.0-alpha.3 | 87 | 7/24/2023 | |
1.0.0-alpha.2 | 91 | 7/24/2023 | |
1.0.0-alpha | 830 | 7/13/2023 | |
0.9.3 | 1,131 | 3/2/2023 | |
0.9.2 | 2,822 | 2/20/2023 | |
0.9.1 | 518 | 1/26/2023 | |
0.8.32 | 2,986 | 1/23/2023 | |
0.8.31 | 555 | 1/23/2023 | |
0.8.3 | 1,312 | 1/19/2023 | |
0.8.2 | 1,109 | 12/12/2022 |