Hi,
I found where it's cached after repository is pulled: https://github.com/abpframework/abp/blob/b54f4bff04181fad94f1f27a0300eda37cfbc9f0/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Documents/DocumentAdminAppService.cs#L234
Unfortunately it's not a virtual method that you cannot easily override it but you can override the methods that call this method such as PullAsync and PullAllAsync to define caching logic on your own:
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IDocumentAdminAppService), typeof(DocumentAdminAppService))]
public class MyDocumentAdminAppService : DocumentAdminAppService
{
private readonly IProjectRepository _projectRepository;
private readonly IDocumentRepository _documentRepository;
private readonly IDocumentSourceFactory _documentStoreFactory;
private readonly IDistributedCache<DocumentUpdateInfo> _documentUpdateCache;
private readonly IDistributedCache<List<VersionInfo>> _versionCache;
private readonly IDistributedCache<LanguageConfig> _languageCache;
private readonly IDocumentFullSearch _elasticSearchService;
public MyDocumentAdminAppService(IProjectRepository projectRepository, IDocumentRepository documentRepository, IDocumentSourceFactory documentStoreFactory, IDistributedCache<DocumentUpdateInfo> documentUpdateCache, IDistributedCache<List<VersionInfo>> versionCache, IDistributedCache<LanguageConfig> languageCache, IDocumentFullSearch elasticSearchService) : base(projectRepository, documentRepository, documentStoreFactory, documentUpdateCache, versionCache, languageCache, elasticSearchService)
{
this._projectRepository = projectRepository;
this._documentRepository = documentRepository;
this._documentStoreFactory = documentStoreFactory;
this._documentUpdateCache = documentUpdateCache;
this._versionCache = versionCache;
this._languageCache = languageCache;
this._elasticSearchService = elasticSearchService;
}
private async Task UpdateDocumentUpdateInfoCache(Document document)
{
var cacheKey = $"DocumentUpdateInfo{document.ProjectId}#{document.Name}#{document.LanguageCode}#{document.Version}";
await _documentUpdateCache.SetAsync(cacheKey, new DocumentUpdateInfo
{
Name = document.Name,
CreationTime = document.CreationTime,
LastUpdatedTime = document.LastUpdatedTime
},
// 👇 Define your cache options here
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30),
SlidingExpiration = TimeSpan.FromMinutes(10)
});
}
/*
* ====================
* OVERRIDE OTHER METHODS WITH ORIGINAL LOGIC BELOW
* THAT ENSURES THEY CALLS THE METHOD WE WROTE ABOVE
* ====================
*/
public override async Task PullAsync(PullDocumentInput input)
{
var project = await _projectRepository.GetAsync(input.ProjectId);
var source = _documentStoreFactory.Create(project.DocumentStoreType);
var sourceDocument = await source.GetDocumentAsync(project, input.Name, input.LanguageCode, input.Version);
await _documentRepository.DeleteAsync(sourceDocument.ProjectId, sourceDocument.Name,
sourceDocument.LanguageCode, sourceDocument.Version);
await _documentRepository.InsertAsync(sourceDocument, true);
await UpdateDocumentUpdateInfoCache(sourceDocument);
}
public override async Task PullAllAsync(PullAllDocumentInput input)
{
var project = await _projectRepository.GetAsync(input.ProjectId);
var navigationDocument = await GetDocumentAsync(
project,
project.NavigationDocumentName,
input.LanguageCode,
input.Version
);
if (!DocsJsonSerializerHelper.TryDeserialize<NavigationNode>(navigationDocument.Content, out var navigation))
{
throw new UserFriendlyException($"Cannot validate navigation file '{project.NavigationDocumentName}' for the project {project.Name}.");
}
var leafs = navigation.Items.GetAllNodes(x => x.Items)
.Where(x => x.IsLeaf && !x.Path.IsNullOrWhiteSpace())
.ToList();
var source = _documentStoreFactory.Create(project.DocumentStoreType);
var documents = new List<Document>();
foreach (var leaf in leafs)
{
if (leaf.Path.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
leaf.Path.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
(leaf.Path.StartsWith("{{") && leaf.Path.EndsWith("}}")))
{
continue;
}
try
{
var sourceDocument = await source.GetDocumentAsync(project, leaf.Path, input.LanguageCode, input.Version);
documents.Add(sourceDocument);
}
catch (Exception e)
{
Logger.LogException(e);
}
}
foreach (var document in documents)
{
await _documentRepository.DeleteAsync(document.ProjectId, document.Name,
document.LanguageCode,
document.Version);
await _documentRepository.InsertAsync(document, true);
await UpdateDocumentUpdateInfoCache(document);
}
}
private async Task<Document> GetDocumentAsync(
Project project,
string documentName,
string languageCode,
string version)
{
version = string.IsNullOrWhiteSpace(version) ? project.LatestVersionBranchName : version;
var source = _documentStoreFactory.Create(project.DocumentStoreType);
var document = await source.GetDocumentAsync(project, documentName, languageCode, version);
return document;
}
}
It seems this module is not easily extended, I'll create an issue to the team to configure / customize it easily
Hi,
It seems it targets one single framework at the moment: https://github.com/abpframework/abp/blob/e9412912c6e4ec5ae1d26f9c4d83110390e5388d/framework/src/Volo.Abp.AspNetCore/Volo.Abp.AspNetCore.csproj#L7
There might be transition version or temporary package, in the source code of that package, it seems it targets the .NET9.
I'm refunding your ticket since the problem is on our side
You can still use Static Client Proxies in your existing project to prevent package dependencies with.Contracts packages. This dependency easily can be eliminated
Hi, we tried updating to v9.1.3 and the issue is still there. Worth mentioning that this happens only on reload, navigating to different pages back and forth works fine.
Any other suggestions on what we can try? Thanks.
Can you try to delete all node_modules folder and yarn.lock/package.lock files. Try to install dependencies from scratch again executing yarn command. If the problem still exists, I'll deliver this issue to the @angular team asap
Hi,
In the screenshot it seems you're using old microservice template. In the new template most of the problems are resolved. Can you try creating a new Microservice Template by using ABP Studio and check how it works. You may slowly migrate to the new pattern
Object Extensions: https://abp.io/docs/latest/framework/fundamentals/object-extensions Module Entity Extensions: https://abp.io/docs/latest/framework/architecture/modularity/extending/module-entity-extensions
They're not template or UI framework specific, whenever you configure them, they'll be available to use. You can start using them by API endpoints. The problem why they don't affect angular seems different topic. I assigned this issue our @angular team and they'll help you in this incident.
I found this feature that provides you a way to add new fields to existing module forms: https://abp.io/docs/latest/framework/ui/angular/dynamic-form-extensions Normally you don't have to do it manually but you may use it as a workaround until solution.
Hi,
You can use gRPC in your newly created custom services by following this article: https://abp.io/docs/commercial/8.1/startup-templates/microservice/using-grpc
But the pre-built ABP modules are using HTTP calls and distributed events to access each other, and Administrator service has to access multiple database to build all the application configuration properly. It's really tough to change it right now. We also discussed a lot of different cases and still most optimized way was this one.
I understand your concerns right now, especially if you use multi-repo and host your services in different repositories, this dependencies may become a bigger case to solve. You may push .Shared and .Contracts package to a private nuget server and consume them in a distributed repository case or use submodues of git, but each case will have different cons.
As a short answer, currently is not possible to fully decouple ABP's built-in services, but you can still build your services without coupling
Sorry for the late reply, I was trying to check case,
Yes openiddict.pfx is ignored on Git by default and if you clone your project from zero to publish or use a pipleine that file will not be included. You need to manually copy it.
I'm not sure if that file has an expiration date, but nice to hear that re-generating fixes the problem. Also always make sure the PassPhrase is always correct in the published server-side
Hi Gökalp, It seems you're correct, you already found a previous incident and it's the expected working flow the external logic provider, so you can ignore it for now
Hi,
Can you check the running backend application logs and provide us? We need to get logs to understand what was going on in that case. If you're running your application on your local machine on localhost, you can directly check Logs/ folder in your YourProjectName.HttpApi.Host project folder