I was able to fix it my own by follwing these steps:
I am not sure what was the right step.. but now it works. please refund that credit
I know that a pro template is not a microservice. But i want to move the relevant parts from pro template solution to a new microservice .. Thats why asked for a guide.. I think its a valid option for moving towards a microservice solution
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 ?
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 ?