Update to latest packages and show examples

This commit is contained in:
2026-07-05 22:05:53 +02:00
parent 3a36978997
commit c7ddc282ed
52 changed files with 2156 additions and 21 deletions
@@ -0,0 +1,17 @@
using GerstITS.Examples.Logic.Shared.Search.Sorting;
namespace GerstITS.Examples.Logic.Customers;
public class Customer : ISortable
{
#region Properties
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EMail { get; set; }
public string DisplayName { get; set; }
public bool IsActive { get; set; }
#endregion
}
@@ -0,0 +1,12 @@
namespace GerstITS.Examples.Logic.Customers;
public class CustomerNote
{
#region Properties
public int Id { get; set; }
public string Text { get; set; }
public DateTime ValidFrom { get; set; }
#endregion
}
@@ -0,0 +1,13 @@
using GerstITS.Search;
namespace GerstITS.Examples.Logic.Customers;
public class CustomerSearchFilter : SearchFilterBase
{
#region Properties
public string Name { get; set; }
public string EMail { get; set; }
#endregion
}
@@ -0,0 +1,23 @@
using GerstITS.Data;
using GerstITS.Search;
namespace GerstITS.Examples.Logic.Customers;
public interface ICustomerProvider
{
Customer GetById(int id);
Customer GetById(int id, bool includeInactive);
SearchResult<Customer> Search(CustomerSearchFilter filter);
Customer Create(Customer customer);
Customer Update(Customer customer);
bool Delete(int id);
bool Delete(int id, bool forceDelete);
BulkResult Import(IEnumerable<Customer> customers);
IEnumerable<CustomerNote> GetNotes(int customerId);
CustomerNote AddNote(int customerId, string text);
}
@@ -0,0 +1,148 @@
using AutoMapper;
using GerstITS.Common;
using GerstITS.Data;
using GerstITS.Examples.Data.Entities;
using GerstITS.Search;
using GerstITS.System.Customizations;
namespace GerstITS.Examples.Logic.Customers;
internal sealed class CustomerProvider : ICustomerProvider
{
#region Fields
private readonly IRepository _repository;
private readonly IMapper _mapper;
private readonly ISearchEngine _searchEngine;
#endregion
#region Constructors
public CustomerProvider(IRepository repository,
IMapper mapper,
ISearchEngine searchEngine)
{
_repository = repository;
_mapper = mapper;
_searchEngine = searchEngine;
}
#endregion
#region ICustomerProvider
public Customer GetById(int id)
{
return GetById(id, false);
}
public Customer GetById(int id, bool includeInactive)
{
var entity = _repository.Query<CustomerEntity>(includeInactive)
.FirstOrDefault(i => i.Id == id)
.ThrowsAnExceptionIfNotExisting();
return ToCustomizedModel(entity);
}
public SearchResult<Customer> Search(CustomerSearchFilter filter)
{
var searchResult = _searchEngine.Search<Customer, CustomerSearchFilter>(filter);
searchResult.Result = searchResult.Result
.Select(i => i.ApplyCustomizations())
.ToList();
return searchResult;
}
public Customer Create(Customer customer)
{
var entity = _mapper.Map<CustomerEntity>(customer);
_repository.Save(entity);
return ToCustomizedModel(entity);
}
public Customer Update(Customer customer)
{
var entity = _repository.Query<CustomerEntity>()
.FirstOrDefault(i => i.Id == customer.Id)
.ThrowsAnExceptionIfNotExisting();
_mapper.Map(customer, entity);
_repository.Save(entity);
return ToCustomizedModel(entity);
}
public bool Delete(int id)
{
return Delete(id, false);
}
public bool Delete(int id, bool forceDelete)
{
var entity = _repository.Query<CustomerEntity>(forceDelete)
.FirstOrDefault(i => i.Id == id)
.ThrowsAnExceptionIfNotExisting();
_repository.Delete(entity, forceDelete);
return true;
}
public BulkResult Import(IEnumerable<Customer> customers)
{
var importableCustomers = customers.Is(x => x.IsNullOrEmpty())
.ThenThrows<ArgumentException>()
.WithMessage("At least one customer is required for an import.");
var entities = importableCustomers.Select(_mapper.Map<CustomerEntity>)
.ToList();
return _repository.BulkSave(entities);
}
public IEnumerable<CustomerNote> GetNotes(int customerId)
{
return _repository.Query<CustomerNoteEntity>()
.Where(i => i.CustomerId == customerId)
.OrderBy(i => i.ValidFrom)
.ToList()
.Select(_mapper.Map<CustomerNote>)
.ToList();
}
public CustomerNote AddNote(int customerId, string text)
{
_repository.Query<CustomerEntity>()
.FirstOrDefault(i => i.Id == customerId)
.ThrowsAnExceptionIfNotExisting();
var entity = new CustomerNoteEntity
{
CustomerId = customerId,
Text = text
};
_repository.Save(entity);
return _mapper.Map<CustomerNote>(entity);
}
#endregion
#region Methods
private Customer ToCustomizedModel(CustomerEntity entity)
{
return _mapper.Map<Customer>(entity)
.ApplyCustomizations();
}
#endregion
}
@@ -0,0 +1,23 @@
using GerstITS.Common;
using GerstITS.System.Customizations;
namespace GerstITS.Examples.Logic.Customers.Customizations;
internal sealed class CustomerDisplayNameCustomization : CustomizationBase<Customer>
{
#region Methods
protected override bool CanCustomize(Customer item)
{
return item.IsNotNull();
}
protected override Customer Customize(Customer item)
{
item.DisplayName = $"{item.LastName}, {item.FirstName}";
return item;
}
#endregion
}
@@ -0,0 +1,29 @@
using AutoMapper;
using GerstITS.Examples.Data.Entities;
namespace GerstITS.Examples.Logic.Customers;
internal sealed class CustomerMapping : Profile
{
#region Constructors
public CustomerMapping()
{
CreateMap<CustomerEntity, Customer>()
.ForMember(x => x.DisplayName, m => m.Ignore());
CreateMap<Customer, CustomerEntity>()
.ForMember(x => x.Created, m => m.Ignore())
.ForMember(x => x.CreatedBy, m => m.Ignore())
.ForMember(x => x.Modified, m => m.Ignore())
.ForMember(x => x.ModifiedBy, m => m.Ignore())
.ForMember(x => x.Deactivated, m => m.Ignore())
.ForMember(x => x.DeactivatedBy, m => m.Ignore())
.ForMember(x => x.Version, m => m.Ignore())
.ForMember(x => x.Notes, m => m.Ignore());
CreateMap<CustomerNoteEntity, CustomerNote>();
}
#endregion
}
@@ -0,0 +1,28 @@
using GerstITS.Common;
using GerstITS.Search;
namespace GerstITS.Examples.Logic.Customers.Search;
internal sealed class CustomerSearchFilterInitializationRule : InitializationRuleBase<CustomerSearchFilter>
{
#region Fields
private const int _defaultTake = 50;
#endregion
#region Methods
protected override CustomerSearchFilter InitializeInstance(CustomerSearchFilter instance)
{
instance.Skip ??= 0;
instance.Take ??= _defaultTake;
if (instance.SortBy.IsNullOrWhiteSpace())
instance.SortBy = nameof(Customer.LastName);
return instance;
}
#endregion
}
@@ -0,0 +1,49 @@
using GerstITS.Common;
using GerstITS.Data;
using GerstITS.Examples.Data.Entities;
using GerstITS.Search;
namespace GerstITS.Examples.Logic.Customers.Search;
internal sealed class CustomerSearchQueryBuilderRule : SearchQueryBuilderRuleBase<Customer, CustomerSearchFilter>
{
#region Fields
private readonly IReadOnlyRepository _repository;
#endregion
#region Constructors
public CustomerSearchQueryBuilderRule(IReadOnlyRepository repository)
{
_repository = repository;
}
#endregion
#region Methods
protected override IQueryable<Customer> CreateQuery(CustomerSearchFilter filter)
{
var query = _repository.Query<CustomerEntity>();
if (filter.Name.IsNotNullOrWhiteSpace())
query = query.Where(i => i.FirstName.Contains(filter.Name)
|| i.LastName.Contains(filter.Name));
if (filter.EMail.IsNotNullOrWhiteSpace())
query = query.Where(i => i.EMail == filter.EMail);
return query.Project(x => new Customer
{
Id = x.Id,
FirstName = x.FirstName,
LastName = x.LastName,
EMail = x.EMail,
IsActive = x.IsActive
});
}
#endregion
}
@@ -0,0 +1,7 @@
using GerstITS.Search;
namespace GerstITS.Examples.Logic.Customers.Validation;
internal sealed class CustomerSearchFilterValidationRule : PagingValidationRuleBase<CustomerSearchFilter>
{
}
@@ -0,0 +1,24 @@
using FluentValidation;
using GerstITS.Validation;
namespace GerstITS.Examples.Logic.Customers.Validation;
internal sealed class CustomerValidationRule : ValidationRuleBase<Customer>
{
#region Constructors
public CustomerValidationRule()
{
RuleFor(x => x.FirstName)
.NotEmpty();
RuleFor(x => x.LastName)
.NotEmpty();
RuleFor(x => x.EMail)
.NotEmpty()
.IsEMail();
}
#endregion
}
@@ -0,0 +1,29 @@
using GerstITS.Examples.Logic.Customers;
using GerstITS.Examples.Logic.Customers.Customizations;
using GerstITS.Examples.Logic.Customers.Search;
using GerstITS.Examples.Logic.Customers.Validation;
using GerstITS.Mapping.AutoMapper;
using GerstITS.Search;
using GerstITS.System.Customizations;
using GerstITS.Validation;
using Microsoft.Extensions.DependencyInjection;
namespace GerstITS.Examples.Logic;
public sealed partial class Module
{
#region Methods
private static void RegisterCustomers(IServiceCollection container)
{
container.RegisterMappingsOf<ICustomerProvider>();
container.RegisterValidationRulesOf<CustomerValidationRule>();
container.RegisterSearchQueryBuilderRulesOf<CustomerSearchQueryBuilderRule>();
container.RegisterInitializationRulesOf<CustomerSearchFilterInitializationRule>();
container.RegisterCustomizationsOf<CustomerDisplayNameCustomization>();
container.AddScoped<ICustomerProvider, CustomerProvider>();
}
#endregion
}