I have built up a microservice solution and would like to add an other project based on the standard template pro as service to it. At the end my solution will look similiar to you eshoponabp project, where you use postgres and mongo as db.
i did follow the doc from here: https://docs.abp.io/en/commercial/latest/startup-templates/microservice/add-microservice to generate a service with the same name. It seems that the parameter for the database provider is ignored (-d mongo) . It always creates a service based on EF. Is this a bug ?
I did also compare the generated files from the new service with the files from the standard template pro and found differences in the module files and structure. I would like to know howto modify the files from the standard template to be able to use it as service in the microservice solution.
As far as i can see, i need to edit all csproj files and *module.cs files to remove unneeded things and add at least something like "typeof(AbpDddDomainModule)," etc., remove Identity Project, move some files to other projects and so on.
I would like to have a detailed step by step guide for a demo project like eventhub /boookstore to see howto integrate it in a microservice solution, Any help would be great
Add Parameter for Microservice to specify mongodb as dbms
Make it possible to change dbms after creation of solution . p.E from sql to postgres or mongo
Add Document howto add a standard template pro project into a microservice solution as service
Hi @gterdem, any plans when this will be available or at least on your roadmap ?
It would be great if you would implement that hopefully in near future.
I can work with the current microservice-template and infos from eshoponabp, but its still a valid business case. In production enviroments you could use multiple db providers, but you dont need to. For our purpose we did not find any issues or performance problems in using one db provider like cosmosdb. We did start with entity Framework, did some testing and switched to mongodb after that. Also you could use for all an other provider like postgres sql.. So from my point of view it would be great if you could add that parameter for the microservice template. I know this will take time..
Meanwhile you could update your document for switching the microservice-template to mongodb ab v5.2.
Hi,
i know eshopOnAbp.. the question is if there is a reason, why the abp cli doesn't support the - db mongodb parameter with the microservice template ?
I use the microservice-pro template v5.2 which uses as DB EntityFramework. Is there a reason why its not possible to choose the DB provider like in other templates with abp cli (-db mongodb)? In our production enviroment we gonna use azure cosmodb for all databases, so instead of mixing different db providers it should be possible to switch/create directly for all mongodb. with abp cli. Or is there a good reason not to do soo ? Also an example based complete on mongodb with the microservice template would be great.
I did try you document: https://docs.abp.io/en/commercial/latest/guides/microservice-mongodb
But it doesn't seem to match to the current v5.2. It would be great if you could update that as soon as possible. Currently i have errors with ProductServiceDatabaseMigrationChecker etc.
Hi Maliming,
i did rethink my problem and found a good solution:
Other Applicationservices only need the Download Method, so i setup an ApplicationService as you suggested with IRemoteStreamContent . From Blazor-Client Side its not a problem, because they directly use the ApiController..
So i moved only the Download Part from ApiController to ApplicationService like this:
public class FileManagerAppService : ApplicationService , IFileManagerAppService
{
public PhysicalFileProvider operation;
public string basePath;
public string fullPath;
public string uploadPath;
public string workPath;
public string installPath;
public string root = "FileManagement";
public FileManagerAppService()
{
basePath = AppDomain.CurrentDomain.BaseDirectory;
fullPath = Path.Combine(basePath, root);
uploadPath = Path.Combine(fullPath, "Uploads");
workPath = Path.Combine(fullPath, "Work");
installPath = Path.Combine(fullPath, "Install");
operation = new PhysicalFileProvider();
operation.RootFolder(fullPath); // Data\Files denotes in which files and folders are available.
}
public async Task<IRemoteStreamContent> Download(string downloadInput)
{
//Invoking download operation with the required paramaters
// path - Current path where the file is downloaded; Names - Files to be downloaded;
FileManagerDirectoryContent args = JsonSerializer.Deserialize<FileManagerDirectoryContent>(downloadInput);
if (args.Path.IsNullOrEmpty())
{
throw new UserFriendlyException("Error during Download. Parameter downloadInput doesn't contain a valid Path.");
}
var downStream = operation.Download(args.Path, args.Names);
// wait max 60s (for zipping files)
WaitforStream(downStream, 60);
// set download name to id, if more then one file is downloaded
if (downStream.FileDownloadName == "files.zip")
downStream.FileDownloadName = args.Path.Split("/")[2] + ".zip";
return new RemoteStreamContent(downStream.FileStream) { ContentType = "application/octet-stream", FileName = downStream.FileDownloadName };
}
public async Task CreateFileAsync(CreateFileInput input)
{
using (var fs = new FileStream("C:\\Temp\\" + input.Content.FileName, FileMode.Create))
{
await input.Content.GetStream().CopyToAsync(fs);
await fs.FlushAsync();
}
}
private static bool WaitforStream(FileStreamResult fs, int timeout)//* in sec
{
var time = Stopwatch.StartNew();
while (time.ElapsedMilliseconds < timeout * 1000)
{
try
{
if (fs?.FileStream?.Length > 0)
return true;
}
catch (IOException)
{
return false;
}
}
throw new UserFriendlyException("Failed perform action: download within allowed time.");
}
}
In my other ApplicationSertvice where i perform other requires operation, i can then call it like that:
public virtual async Task Download(Guid id)
{
.... perform other action
...
// Generate downloadInput for Syncfusion
var downloadInput = JsonSerializer.Serialize(
new DownloadInfo()
{
Path = ... ,
Names = ....Select(x => x.Name).ToArray()
});
await _fileManagerAppService.CreateFileAsync(new CreateFileInput()
{
Content = await _fileManagerAppService.Download(downloadInput)
});
}
Maliming, i thank you for your hints and good scrrenshots.. The were very helpful. !!
I did test my workaround.. If i call from swagger and hit directly the webapi for download it works. But if i try to access it via Applicationservice from other Applicationservice it doesn't work, because the apicontroller isn't called (whats the correct behaviour). So my workaround doesn't work.
I would like to specify more detailed, where i see the problem here:
The ApiController performes some actions, and also manipulate HttpRequest and HttpResponse for example if i upload a file and add custom data from clientside to the request header, i would get it in the controller like :
[HttpPost]
[Route("save")]
public async Task Save(IList<IFormFile> chunkFile, IList<IFormFile> UploadFiles)
{
var data = Request.Form["path"]; <<----- here
var path = data.ToString().Replace("/", "\\");
....
}
I did check your links. Yes i could implement the needed return type in the controller method. But that still doesn't solve my problem. I can't move the complete logic to the application layer (because it depends on api request,response etc.) and i also can't generate an application service for it to make it accessable for other applicationservices ? I still get not the advantage of RemoteStreamContent.
I would like to make that working api call accessable through application service. To be more concret, how can i achieve this, when some logic is always neccesary at apiController level.
Please let me know how you would really solve this, pleae do not only post a link like. I need a concret pattern.
The code above again calls from an apicontroller the applicationservice. Also IRemoteStreamContent is System.IO.Stream and i get FileResultStream. And casting and bufferings in same app from different controller doesn't seam right.
My Controller works fine when used in swagger, but isn't accessible in application service.
I found a possible workaround:
public virtual async Task<IActionResult> Download(string downloadInput)
{
// only dummy
return new ContentResult();
}
public virtual async Task<IActionResult> Upload(string path, IList<IFormFile> uploadFiles, string action)
{
// only dummy
return new ContentResult();
}
I created the functions as dummy at applicationlevel. In fact they will never be called, because at apicontroller level, we wont call them.. But for other apllicationservices this should work, because of same method pattern
What i mean is, that its possible to use IRemoteStreamContent like in the link if you call the download /upload from an other Application Service. The logic in the link is the same as always. Thelogic and operations are done at application level, not apicontroller level. ( also the download method return a mvc FileStreamResult not a io stream - Casting would cause additional memory use. )
But here the logic and operations for the download/upload are defined in the apiController. So before i am able to use these method, i first need to generate an ApplicationService. Before this is not done, i can't access these methods from other applicationservice.
So please explain, how you would write the ApplicationService/IApplicationService for the above code ?