I have created a WebApi project and deployed this as a serverless application to AWS Lambda. It all works fine except that for one method, I need to read from a file. I can see that this file is deployed as it's in the zip file that gets pushed up but for some reason, when I come to read from it in the code, it can't find it.
I've tried both embedded resource and content for build action but neither seem to work. Is there something in AWS that needs configuring so that my serverless application can access local files?
For anyone that needs more detail. Add your embedded files into your .csproj file as below (resources is your folder, note the "resources\" in .csproj and "resources/" in .cs):
<ItemGroup>
<Content Include="resources\*.*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
In your .cs file, to get the file path:
var path = Path.Combine(Directory.GetCurrentDirectory(), "resources/example.txt");
Related
I have an appsettings.json file which is the same for all projects in the solution.
The trouble I'm having with it, is that the file isn't available at runtime. The file is available when I test locally in my Visual Studio environment, but when the test runs through azure devops pipeline, the file suddenly isn't there.
Basicly each project has this element added
<ItemGroup>
<None Include="$(SolutionDir)\Shared\appsettings.json" CopyToOutputDirectory="Always" LinkBase="\" />
</ItemGroup>
Which references the appsettings file and should copy the file into the bin folder.
However, when I try to access the file during testing, the file is not available, despite the fact that I can find the file with powershell.
So basicly trying to get the file via the base path
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
if(!File.Exits(filePath)){
throw new ArgumentException($"appsettings.json is not found at {filePath}");
}
gives an error, while get-childitem -file -path {path} shows that the file is indeed in the folder.
I have an azure function that has a Queue trigger. In the trigger I create a pdf. This pdf should contain a couple of images. How do I include these images in an azure function so that they can be accessed from the code?
My previous version of the code was implemented as a webjob and here we accessed the image like this:
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Images", "My_Image.png"
This does not seem to work in my azure function as this path no longer contains my image. The image has build action "None" and is set to "Copy always".
Would be great if someone could point me in the right direction.
There are a number of ways forward here:
You could use Blob Storage to contain your image. You can open that programmatically and get a stream, url, etc.
It might be possible to proceed with your current plan although it seems a little more fraught than I would expect: Including a file when I publish my Azure function in Visual Studio
You could possibly convert the image to base64 and include it in code.
There is a much easier way depending on your CI/CD pipeline to not have to put few files into a separate blob container. In my case, I just needed to include ONE font (ttf) file as my azure function was generating a pdf.
Note: DI will make things easier if you choose to follow this method.
Create a folder called 'Resources' (not necessary but keeps the project clean), and add your file in it. Right-click properties and select 'Copy if Newer'.
Next if you're using the Azure DevOps, add this to your .csproj -
<Target Name="CopyRequiredResources" AfterTargets="_FunctionsPostPublish">
<ItemGroup>
<ResourcesToCopy Include="$(ProjectDir)Resources\*.*" />
</ItemGroup>
<Copy SourceFiles="#(ResourcesToCopy)" DestinationFolder="$(PublishDir)bin" />
</Target>
If you're publishing directly from Visual Studio, then change DestinationFolder to "$(TargetDir)bin".
Edit: Above mentioned destination folders, could be the other way around. It's been a while. Try out both. Best way is to publish to ZIP files and check if the bin folder has the required files. In the case of DevOps, check the bin of the created artifacts. And make the necessary changes to the DestinationFolder.
Basically moving all the required files to the bin folder to keep things easy. The above will work for Azure Function, now to also make sure that the files exists in the same path for local development as well, add the following -
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<ItemGroup>
<ResourcesToCopy Include="$(ProjectDir)Resources\*.*" />
</ItemGroup>
<Copy SourceFiles="#(ResourcesToCopy)" DestinationFolder="$(TargetDir)bin" />
</Target>
In the above bits of MSBuild actions, you're moving your required resources to the publish/bin folder.
now in my pdf writer i had to inject the execution context to get the running app directory -
public class PdfWriter
{
string resourceLoc;
public PdfWriter(IOptions<ExecutionContextOptions> contextOptions)
{
resourceLoc = Path.Combine(contextOptions.Value.AppDirectory, "bin");
}
// img would be in Path.Combine(resourceLoc, "img.jpg");
}
Why I like this method better than the rest -
Dont require a separate blob container just for 1-2 files.
If you decide to change the img or your resource, you dont need to re-do the Base64 conversions. You just replace and republish.
Its all contained within the app itself and has no outside dependency (this ties back to point 1), and hence if you decide to revisit the code in the future, it'll run out of the box.
Makes things easy for collaboration etc. As your team is not reliant on keeping their local blob storage with the required files for things to work.
Further to point 4, I would use this method even if I had several resources. Function code is anyways kept in Azure Storage, so might as well ensure your resources are kept with your code neatly packaged.
I tried to find files located in the root project of my Blazor Client WebAssembly project. While it works when executed locally on my computer, it doesn't work when hosted on Azure.
When executed locally, I got 1
When executed on Azure, I got 0 (no access denied errors of things like that)
To be more complete, I will have some files needed to fill an initial blank database.
If I cannot access files in the project folder, then I will go for a Blob Azure Storage. I would like to clarify that this is only a test project for myself.
The reason for this problem is that the TextFile.txt file was not included at the time of publication.
Akos said is right. I am modifying the .csproj file to send out my answer. Can better help other users of the forum.
You can paste code in your .csproj file.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ResolvedFileToPublish Include="TextFile.txt">
<RelativePath>TextFile.txt</RelativePath>
</ResolvedFileToPublish>
</ItemGroup>
</Project>
Then you can deploy your app. You can find TextFile.txt under D:\home\site\wwwroot>.
I have a c# project that needs to load some yaml resources to work, I have set them with "build action":"content" and "copy to Output directory":"Copy if newer"
To load them I use simply a relative path, and everything works inside the project.
I realized xunit project that loads the library and everything still works fine.
Now I released the library on nuget and I am trying to reference on an Asp.Net Core, but when I try to load my files, the application tries to find them in the wrong folder:
The files are searched in relative path from the root asp.net project, but files are in C:\Users\[name]\.nuget\packages\[pakage name]\[version]\content
So I am wondering how can I get the "content" folder of the assembly in C#?
UPDATE: I TRY TO EXPLAIN BETTER
I have a visual studio solution, we call it foo.sln and a C# project that we call foo.csproj
The project is a netstandard 2.0 library and it's built to be consumed by other applications: cli, asp.net, core, wpf, etc...
The library is built to be multiplatform, so could run on Windows, Mac and on Linux
Inside the root of foo project there is a folder we call it YamlDefinitions
Inside of that folder we have a lot of .yaml files that needs to be loaded and parsed by my foo library.
So foo.csproj has something like:
<Content Include="YamlDefinitions\Definition1.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="YamlDefinitions\Definition2.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="YamlDefinitions\Definition3.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="YamlDefinitions\Definition4.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
These files are immutable, so I load them as content instead as resource
Now inside the c# code of my foo library, I have:
var filePaths = Directory.GetFiles("YamlDefinitions", "*.yaml", SearchOption.TopDirectoryOnly)
foreach (var filePath in filePaths)
{
using (var reader = new StreamReader(filePath))
{
// Load the stream
var yamlStream = new YamlStream();
yamlStream.Load(reader);
yaml = yamlStream.Documents.FirstOrDefault();
}
}
That code search, loads and parse all yaml files that can find in the specified folder.
Everything seems ok, so now to test I create an xunit prject inside the foo.sln, we call it footest.csproj
Now I reference foo.csproj inside footest.csproj
<ProjectReference Include="..\Foo\foo.csproj" />
All tests passed so I pack the library and publish the nuget package: foo.nuget
Now I want try to use my nuget package, so I create a new console application with Net Core 2.2, we call it MyConsoleApp.csproj
I install my nuget package foo.nuget:
<PackageReference Include="Foo" Version="1.0.0"/>
the package is installed and all yaml files are placed in
C:\Users\UserName\\.nuget\packages\Foo\1.0.0\content\YamlDefinitions
and in
C:\Users\UserName\\.nuget\packages\Foo\1.0.0\contentFiles\any\netstandard2.0\YamlDefinitions
The files are also visible inside MyConsoleApp.csproj as linked files, but they are not physically present in the project.
Now I launch my MyConsoleApp.exe, and I get an error, because it's unable to find these yaml files, because the application tries to load them in the same path as MyConsoleApp.exe
Workaround: from visual studio I select all yaml files that are linked inside MyConsoleApp.csproj and I set as CopyIfNewer
Now inside cproj visual studio adds these lines of codes:
<Content Include="C:\Users\User\.nuget\packages\Foo\1.0.0\contentFiles\any\netstandard2.0\YamlDefinitions\Definition1.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
I run again MyConsoleApp.exe and everything works.
This workaround is bad because the end user should be able to use the nuget library without specifying manually to copy files.
If I release a new version of my library that contains new definitions, the end user should not be aware that these must be included in the project.
Anyway, there is a bigger problem:
I create a new ASP.NET Core 2.2 MVC Application, we call it MyWebApp.csproj
I install Foo.nuget and I try to use it.
I set manually to copy all Yaml definitions in output folder, and when I build they are automatically copied in MyWebApp\bin\Release\netcoreapp2.2\YamlDefinitions
I launch the web app and I get again an error, because my foo libray tried to find files in MyWebApp\YamlDefinitions, but files are in bin folder, so nothing works.
How can I fix my library and and publish as nuget in a way that all consumer applications can use it?
I can't find a C# function that retrieves the nuget folder where content files are stored and I doubt that could exist, but I found this working approach:
In foo.csproj file you need to specify that each file should be copied in output folder, this can be done with:
<Content Include="YamlDefinitions\Definition1.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
</Content>
You need to add PackageCopyToOutput to make working with nuget, unfortunately, this can't be done from visual studio UI and you have to edit .csproj manually.
In C# you have to use an absolute path because relative path is not the same in all kind of application (in a .NET cli is different from ASP.NET)
So you have to use a code like this:
var directory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "YamlDefinitions");
var filePaths = Directory.GetFiles(directory, "*.yaml", SearchOption.TopDirectoryOnly)
Where AppDomain.CurrentDomain.BaseDirectory return the output directory where content files are copied
On a large (team and code) project, we have set up the various App.configs and Web.configs to reference an (optional) local.config file so that developers can override some things when running locally. local.config is listed in .getignore so it's ignored and not included in commits. This works very well to support local configuration overrides.
However, for console apps, local.config isn't copied to the output directory by default. I can set its properties in VS so that it's copied to the output - it gets listed in the .csproj file like this:
<None Include="local.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
This works fine in VS, even when a particular developer does not have a local.config file at all. However, it fails in the VSTS build jobs because apparently msbuild doesn't like it when a listed file is absent.
Is there any way to configure the project or msbuild so that it will tolerate/ignore the missing (optional) file instead of failing the builds?
This can be done by using an msbuild condition:
<None Include="local.config" Condition="Exists('local.config')">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>