Hi,
I am seeking your assistance.
In the .Core project, I've added the following:
public class HostEntityBase<TPrimaryKey> : FullAuditedEntity<TPrimaryKey>, IMayHaveTenant
{
public virtual int? TenantId { get; set; }
[Required]
[StringLength(HostEntityConsts.MaxLabelLength)]
public virtual string Label { get; protected set; }
public virtual string Notes { get; protected set; }
public virtual void UpdateLabel(string newLabel)
{
//Validate newLabel
Check.NotNull(newLabel, nameof(newLabel));
Label = newLabel;
}
public virtual void UpdateNotes(string newNotes)
{
Notes = newNotes;
}
protected HostEntityBase()
{
}
}
public class HostEntityWithIntKeyBase : HostEntityBase<int>
{
}
[Table("DrcClothingSize")]
public class ClothingSize : HostEntityWithIntKeyBase, IMayHaveTenant
{
/// <summary>
/// EF
/// </summary>
protected ClothingSize()
{
}
public static ClothingSize Create(string label, string notes = null)
{
var clothingSize = new ClothingSize();
clothingSize.UpdateLabel(label);
clothingSize.UpdateNotes(notes);
return clothingSize;
}
}
public interface IReferenceDataManager<THostEntityBase> : IReferenceDataManager<THostEntityBase, int>
{
}
public interface IReferenceDataManager<THostEntityBase, TPrimaryKey>
{
/// <summary>
/// Gets a list of all available <typeparamref name="THostEntity"/> in the host
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<IReadOnlyList<THostEntityBase>> GetAsync(string filter);
/// <summary>
/// Adds a <typeparamref name="THostEntity"/> record
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task CreateAsync(THostEntityBase input);
/// <summary>
/// Adds a <typeparamref name="THostEntity"/> record
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task UpdateAsync(THostEntityBase input);
/// <summary>
/// Deletes an existing <typeparamref name="THostEntity"/> record
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task DeleteAsync(TPrimaryKey input);
}
public class ReferenceDataManager<THostEntityBase> :
ReferenceDataManager<THostEntityBase, int>,
IReferenceDataManager<THostEntityBase>
where THostEntityBase : HostEntityBase<int>
{
public ReferenceDataManager(
ICacheManager cacheManager,
IRepository<THostEntityBase, int> repository,
IUnitOfWorkManager unitOfWorkManager,
string cacheName = null)
: base(
cacheManager,
repository,
unitOfWorkManager,
cacheName)
{
}
}
public class ReferenceDataManager<THostEntityBase, TPrimaryKey> :
IReferenceDataManager<THostEntityBase, TPrimaryKey>,
ISingletonDependency
where THostEntityBase : HostEntityBase<TPrimaryKey>
{
public string CacheName { get; private set; }
protected ICacheManager CacheManager { get; private set; }
protected IUnitOfWorkManager UnitOfWorkManager { get; private set; }
protected IRepository<THostEntityBase, TPrimaryKey> Repository { get; private set; }
public ReferenceDataManager(ICacheManager cacheManager,
IRepository<THostEntityBase, TPrimaryKey> repository,
IUnitOfWorkManager unitOfWorkManager,
string cacheName = null)
{
UnitOfWorkManager = unitOfWorkManager;
Repository = repository;
CacheManager = cacheManager;
CacheName = cacheName ?? GenerateDefaultCacheName();
}
public virtual async Task<IReadOnlyList<THostEntityBase>> GetAsync(string filter = null)
{
return (await GetListAsync(filter));
}
[UnitOfWork]
public virtual async Task CreateAsync(THostEntityBase input)
{
if ( (await GetAsync() as IReadOnlyList<THostEntityBase>)
.Any(t => t.Label.ToLower().Contains(input.Label.ToLower())))
{
throw new AbpException($"There is already an entity with Label = {input.Label}");
}
var existingEntity = (await GetAsync(input.Label));
if (existingEntity != null)
{
}
using (UnitOfWorkManager.Current.SetTenantId(null))
{
await Repository.InsertAsync(input);
await UnitOfWorkManager.Current.SaveChangesAsync();
}
}
[UnitOfWork]
public virtual async Task UpdateAsync(THostEntityBase input)
{
//Check the entity doesnt exist
var existingEntity = ((await GetAsync() as IReadOnlyList<THostEntityBase>)
.FirstOrDefault(t => t.Label.ToLower().Contains(input.Label.ToLower())));
if (existingEntity != null)
{
if (!existingEntity.Id.Equals(input.Id))
{
throw new AbpException($"There is already an entity with Label = {input.Label}");
}
}
existingEntity.UpdateLabel(input.Label.Trim());
existingEntity.UpdateNotes(input.Notes.Trim());
using (UnitOfWorkManager.Current.SetTenantId(null))
{
await Repository.UpdateAsync(existingEntity);
await UnitOfWorkManager.Current.SaveChangesAsync();
}
}
[UnitOfWork]
public virtual async Task DeleteAsync(TPrimaryKey input)
{
//Get all records from cache
var existingEntity = (await GetAsync() as IReadOnlyList<THostEntityBase>).FirstOrDefault(t => t.Id.Equals(input));
if (existingEntity == null)
{
return;
}
using (UnitOfWorkManager.Current.SetTenantId(null))
{
await Repository.DeleteAsync(existingEntity.Id);
await UnitOfWorkManager.Current.SaveChangesAsync();
}
}
[UnitOfWork]
private async Task<List<THostEntityBase>> GetListAsync(string filter)
{
//Get THostEntityBase from host not any specific tenant
using (UnitOfWorkManager.Current.SetTenantId(null))
{
return await Repository
.GetAllListAsync(
WithFitler(filter)
);
}
}
/// <summary>
/// Prepares a dynamic filter to insert into GetAllListAsync
/// </summary>
/// <param name="filter"></param>
/// <returns></returns>
private Expression<Func<THostEntityBase, bool>> WithFitler(string filter)
{
if (filter.IsNullOrEmpty())
{
return (t => true);
}
return (t => t.Label.ToLower().Contains(filter.ToLower()) || t.Notes.ToLower().Contains(filter.ToLower()));
}
protected virtual string GenerateDefaultCacheName()
{
return GetType().FullName;
}
}
This is just a ReferenceDataManager class that can handle Reading/Updating/Deleting/Creating for all objects that inherit from HostEntityBase.
Now in the .Application project, I have the following:
public interface IClothingSizeAppService : IApplicationService
{
/// <summary>
/// Gets a list of all available ClothingSizes in the host
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<GetClothingSizesOutput> GetClothingSizes(GetClothingSizeInput input);
/// <summary>
/// Adds or updates a clothing size record
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task CreateOrUpdateClothingSize(CreateOrUpdateClothingSizeInput input);
/// <summary>
/// Deletes an existing ClothingSize record
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task DeleteClothingSize(EntityDto input);
}
[AbpAuthorize(AppPermissions.Pages_HOSTREFDATA_CLOTHINGSIZE)]
public class ClothingSizeAppService : OnlineSystemsAppServiceBase, IClothingSizeAppService
{
private readonly IReferenceDataManager<ClothingSize> _clothingSizeManager;
private readonly ICacheManager _cacheManager;
private readonly IRepository<ClothingSize> _repository;
private readonly IUnitOfWorkManager _unitOfWorkManager;
public ClothingSizeAppService(ICacheManager cacheManager,
IRepository<ClothingSize> repository,
IUnitOfWorkManager unitOfWorkManager)
{
_cacheManager = cacheManager;
_repository = repository;
_unitOfWorkManager = unitOfWorkManager;
_clothingSizeManager = new ReferenceDataManager<ClothingSize>(_cacheManager, _repository, _unitOfWorkManager, "ClothingSizes");
}
public async Task<GetClothingSizesOutput> GetClothingSizes(GetClothingSizeInput input)
{
var clothingSizes = await _clothingSizeManager.GetAsync(input.Filter);
return new GetClothingSizesOutput
{
Items = clothingSizes.MapTo<List<ClothingSizeListDto>>()
};
}
public async Task CreateOrUpdateClothingSize(CreateOrUpdateClothingSizeInput input)
{
if (input.ClothingSize.Id.HasValue)
{
await UpdateClothingSizeAsync(input);
}
else
{
await CreateClothingSize(input);
}
}
[AbpAuthorize(AppPermissions.Pages_HOSTREFDATA_CLOTHINGSIZE_DELETE)]
public async Task DeleteClothingSize(EntityDto input)
{
var existingClothingSize = await _repository.GetAsync(input.Id);
if (existingClothingSize == null)
{
return;
}
await _clothingSizeManager.DeleteAsync(input.Id);
}
#region Private methods
[AbpAuthorize(AppPermissions.Pages_HOSTREFDATA_CLOTHINGSIZE_EDIT)]
private async Task CreateClothingSize(CreateOrUpdateClothingSizeInput input)
{
var newClothingSize = ClothingSize.Create(input.ClothingSize.Label, input.ClothingSize.Notes);
//Check that this new clothing size doesn't exist
await CheckClothingSizeIfAlreadyExists(newClothingSize.Label);
await _clothingSizeManager.CreateAsync(newClothingSize);
}
[AbpAuthorize(AppPermissions.Pages_HOSTREFDATA_CLOTHINGSIZE_EDIT)]
private async Task UpdateClothingSizeAsync(CreateOrUpdateClothingSizeInput input)
{
//Check that this new clothing size doesn't exist
await CheckClothingSizeIfAlreadyExists(input.ClothingSize.Label, input.ClothingSize.Id);
var existingClothingSize = await _repository.GetAsync(input.ClothingSize.Id.Value);
existingClothingSize.UpdateLabel(input.ClothingSize.Label);
existingClothingSize.UpdateNotes(input.ClothingSize.Notes);
await _clothingSizeManager.UpdateAsync(existingClothingSize);
}
private async Task CheckClothingSizeIfAlreadyExists(string label, int? expectedId = null)
{
var allClothingSizes = await _clothingSizeManager.GetAsync(label);
var existingClothingSize = allClothingSizes.Count > 0 ? allClothingSizes[0] : null;
if (existingClothingSize == null)
{
return;
}
if (expectedId != null && existingClothingSize.Id == expectedId.Value)
{
return;
}
throw new UserFriendlyException(L("ThisClothingsizeAlreadyExists", label));
}
#endregion
}
The problem is in this line:
public ClothingSizeAppService(ICacheManager cacheManager,
IRepository<ClothingSize> repository,
IUnitOfWorkManager unitOfWorkManager)
{
_cacheManager = cacheManager;
_repository = repository;
_unitOfWorkManager = unitOfWorkManager;
_clothingSizeManager = new ReferenceDataManager<ClothingSize>(_cacheManager, _repository, _unitOfWorkManager, "ClothingSizes");
}
I would like to be able to inject IReferenceDataManager<ClothingSize> to the constructor.
I have also another 7 similar classes to ClothingSize and all have the same behavior and share the same object properties. So I thought of creating such a generic way of handling stuff.
Then on the ApplicationService level, I would create a new AppService per Entity.
I know that the framework is already doing it with IRepository<Entity> and others.
Can you please guide me on how to do the same in my custom code?
Many Thanks, Bilal
Hi,
I want to check the HTML/js/css for some features used in the metronic theme. Are those files freely available? If not, what's the workaround here?
Thanks
Hi, I was checking this github: <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate-samples/tree/master/MultipleDbContextDemo">https://github.com/aspnetboilerplate/as ... ontextDemo</a>
I am wondering, how would IRepository<Person> make use of "Default" DbContext, while IRepository<Course> make use of "Second" DbContext?
How do you direct each IRepository to use a different DbContext?
Thanks
Hi,
I am browsing Metronic Theme online and I can see it has a lot of UI things to make use in an application.
When I want to use a certain feature of that theme, shall I do any extra thing to integrate with Abp? Or I can simply check the HTML/JS code from the online demo of Metronic and use the same in my app?
For instance, the Modal control, which one Abp boilertplate is using? Ui-Bootstrap or the one from Metronic?
Thanks
Hi,
How can I organize my code through VS Projects in such a way:
1- I want to separate "business modules" by VS Solution 2- Every VS Solution would contain (.Core), (.EntityFramework) and (.Application)
This way, multiple developers can work on separate "business modules" at the same time.
Looking forward to hearing your recommendations.
Regards Bilal
Hi,
I need to add custom validation to DTOs and Entities. This validation cannot be satisfied by the usual Data Annotations.
Is it okay to access DB when inside DTO or Entity to get some data from the database and then validate the object?
Is there a better way of doing it, the boilerplate way?
Thanks
Hi,
In one of the forms I am working on. I need to show/hide fields. The data to show/hide fields is stored in the database, so for example, Field 1 - Show, Field 2 - Hide.
So in the cshtml view (Angular view) I need to load this info from the database. Is this doable?
The reason I am asking is because this .cshtml view is used by Angular not a normal MVC View that I can pass a Model to it.
Thanks
Hi,
I clicked on Forgot Password to retrieve the password for one of the users. I enter the tenant name and email and I receive the email.
Then, when I click on the link inside the email, it opens as follows:
http://localhost:6240/Default./Account/ResetPassword?userId=sLwljR%2FBdR7W64QgwVFk%2Bw%3D%3D&tenantId=mFlm%2BKHDrbFA%2BQWcyocM9g%3D%3D&resetCode=9748642471e34c738bea375aefa66c86
And the page shows something like:
What's wrong?
Hi, I read somewhere that the Module Zero() makes use of the Database to store Application Language Texts.
Now, in the template I downloaded, it makes use of Embedded Application Language Text files.
My question is, isn't this template using Module Zero()? If not, how can i store Application Language Texts in Database?
Thanks
Hello, in this source code below, the class already inherits from MyTestsDomainServiceBase, hence, all methods are handled automatically by UOW.
Why the code still adds an attribute [UnitOfWork], is it necessary in this case?
Thanks
public class FriendshipManager : MyTestsDomainServiceBase, IFriendshipManager
{
private readonly IRepository<Friendship, long> _friendshipRepository;
public FriendshipManager(IRepository<Friendship, long> friendshipRepository)
{
_friendshipRepository = friendshipRepository;
}
[UnitOfWork]
public void CreateFriendship(Friendship friendship)
{
if (friendship.TenantId == friendship.FriendTenantId &&
friendship.UserId == friendship.FriendUserId)
{
throw new UserFriendlyException(L("YouCannotBeFriendWithYourself"));
}
using (CurrentUnitOfWork.SetTenantId(friendship.TenantId))
{
_friendshipRepository.Insert(friendship);
CurrentUnitOfWork.SaveChanges();
}
}