I am using .NET Core 3.1. Let's say that I have the following solution structure:
MySolution
ClassLibrary
Files
a.txt
b.txt
GetFile.cs
Project1
Project2
...
And let's say that GetFile.cs has a function ReadFile which reads the file from Files and returns its content.
public class FileReaderService : IFileReaderService
{
private readonly IHostEnvironment _env;
public FileReaderService(IHostEnvironment env)
{
_env = env;
}
public string ReadFile(string fileName)
{
var currentPath = _env.ContentRootPath; // not correct
return "";
}
}
However, when I try to get the current directory in ReadFile with _env.ContentRootPath, it returns the directory of calling project. I don't want it to be dependent on calling project.
How can I achieve that each project will be able to call ReadFile and that correct file from Files will be returned? I need to be able to add, remove and change these files while the app is running.
I have found some questions on SO but they all seem to be outdated. I need a solution which will work on .NET Core.
Instead of using the environment to determine the root file path, set it at the configuration level (ex appSettings.json ) and inject that path into the service itself. Either project can set its own path, use the same one, or both retrieve from some external configuration instead of appSettings.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1
Related
Let's say I have some projects, a library Foo and two projects Bar and Baz, which depend on Foo. Foo contains some configuration that will be shared between Bar and Baz, but Bar and Baz will also do some configuration that is different between them.
In Foo, I have a configuration file:
/* /dev/Foo/fooConfig.json */
{
"lorem": "ipsum",
"dolor": "set"
}
and a method that does the initial configuration:
/* /dev/Foo/configuration.cs */
public static IConfigurationBuilder BuildBaseConfiguration()
{
return new ConfigurationBuilder()
.AddJsonFile("fooConfig.json")
}
Then in Bar, I have something similar:
/* /dev/Bar/barConfig.json */
{
"semper": "suspendisse"
}
/* /dev/Bar/Program.cs */
public static void main()
{
BuildBaseConfiguration()
.AddJsonFile("barConfig.json")
.Build();
}
Normally, Foo is distributed as a NuGet package, but during development, I reference it locally by including the following Bar.csproj:
<Reference Include="Foo">
<HintPath>../Foo/bin/Debug/net6.0/Foo.dll</HintPath>
</Reference>
I've made sure that fooConfig.json is being copied to the output directory, and that it is indeed appearing after successfully running a build.
However, after running Bar, I get the following error:
System.IO.FileNotFoundException: The configuration file 'fooConfig.json' was not found and is not optional. The expected physical path was '/dev/Bar/bin/Debug/net6.0/fooConfig.json'.
It would seem that .NET Core is looking for the config file using a relative file path based on the working directory at runtime (/dev/Bar/bin/Debug/net6.0), rather than where the file is actually kept (../Foo/bin/Debug/net6.0/fooConfig.json).
How do I correct this behavior, so that .NET Core references the real location of fooConfig.json?
By default ConfigurationBuilder is using AppContext.BaseDirectory (see the source code) as root for the file search. You can try something like the following to override this behaviour:
// for development environment:
var compositeFileProvider = new CompositeFileProvider(new[]
{
new PhysicalFileProvider(AppContext.BaseDirectory),
new PhysicalFileProvider(Path.GetDirectoryName(typeof(SomeTypeFromFooDll).Assembly.Location))
});
var cfgBuilder = new ConfigurationBuilder()
.SetFileProvider(compositeFileProvider);
// ... rest of cfg setup
P.S.
Personally I think that library should not come with it's config files and I would refactor the code in such way that it exposes the settings type (with some default values) and it is up to the consuming project to handle those.
this question follows my previous question.
I have a c# ASP.NET application and i want to provide support for plugins. Plugins can be custom c# classes, javascript, html, css, images, etc.
I see no problem as long as my application is extended with c# classes because all the user has to do is create a new "class library" project in visual studio and implement the interfaces, i provide. Then build a dll out of it and upload it to my server. The plugin-developer can add static files (html, js, css, etc.) into this project as well but i found some problems with that:
Every static file i add to the plugin project gets the build action "content" and it seems i cannot read those files from my server. (see my previously answered question). I have to manually select "Embedded Resource" on each file, so it is packed with the plugin dll.
I want to support Typescript for the plugins. The Typescript compiler generates javascript files in the same directory as the typescript-files. But the javascript files are not included in the project and therefore i have to include these in the plugin project and then set the correct build action. I don't want the plugin developers to do that all the time.
If the static files have the build action "enbedded resources", then the server can pickup these files by using the assembly.GetManifestResourceNames() method. This method returns the resources as a string. The path is not separated by \ or / but with a dot instead. So i am not able to distinguish between file path (this is relevant) or filename (also relevant to pickup the correct files), because the original filename can also have dots.
So i am starting to question the "class library" project type is right for my needs. Is there a way to get around of my issues or do i have to use another project type?
Thank you for any help!
Edit: Changed the question a little bit so it is better to understand.
You could make a zip package with the plugin dll and files. NuGet also uses this mechanism. A .nupkg is also just a zip file.
I would start by looking at MEF (Managed Extensibility Framework).
MSDN information can be found here: https://msdn.microsoft.com/en-us/library/dd460648(v=vs.110).aspx
From that link you can get more information and I believe there is a tutorial as well.
Oh, for me it seems very simple.
Let the developer create the plugin freestyle and put all the additional files in a directory, let's call it extras
To implement the your interface they will need your assembly so I guess you will ship it via nuget, or just some link. No matter what the case, provide them with some powershell script what will be required to run before the final build
The script would create zip archive from the extras directory and add it to the ClassLibrary project as EmbeddedResource.
As you mentioned earlier, you can access EmbeddedResource. So all you would do is to unpack it and you would have the exact directory tree.
The best idea would be to provide project template with script included, and also the empty zip archive added as embedded resource (it will be easier to just pack the files in the script and replace the file), and pre-build action set to run the script.
Am I missing something?
What about this.
In your web application, you could add a function that loop into your plugin directory and find DLL implementing an Iplugin (name is up to you) interface.
The interface is defined in a class library that both your web application and plugins have to implement.
You can use the Httpcontext Server mappath to read javascript and other files.
Here is a very basic implementation
First, you have the plugin interface (a class library implemented both by the web application and the individual plugins) I implemented sample properties and methods...
using System.Web;
public interface IPlugin
{
string Name { get; set; }
string Output { get; set; }
void Load(ref httpcontext Context);
void Dispose();
void Display();
}
Next, you have the Actual plugin class library we want to implement.
using System.Web;
using IPlugins;
public class AwesomePlugin : IPlugins.IPlugin
{
private string _Name = "AwesomePlugin";
private HttpContext _Context;
public string Name {
get { return _Name; }
set { _Name = value; }
}
public string Output {
get { return "Yay !!!"; }
set {
throw new NotImplementedException();
}
}
public void Display()
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public void Load(ref Web.HttpContext Context)
{
}
}
Finally, you dynamically load your plugins so you can use them in your application.
private Dictionary<string, IPlugins.IPlugin> _Plugins = new Dictionary<string, IPlugins.IPlugin>();
public void LoadPlugins()
{
lock (static_LoadPlugins_IpluginType_Init) {
try {
if (InitStaticVariableHelper(static_LoadPlugins_IpluginType_Init)) {
static_LoadPlugins_IpluginType = typeof(IPlugins.IPlugin);
}
} finally {
static_LoadPlugins_IpluginType_Init.State = 1;
}
}
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
dynamic Plugins = io.Directory.GetFiles(ServerPath);
foreach (string PluginPath in Plugins) {
dynamic Assembly = system.Reflection.Assembly.LoadFile(PluginPath);
Type PluginClass = Assembly.GetTypes.Where(T => T.GetInterface("IPlugin") != null).First;
IPlugins.IPlugin MyPlugin = Activator.CreateInstance(PluginClass);
MyPlugin.Load(httpcontext.Current);
_Plugins.#add(PluginClass.ToString, MyPlugin);
}
}
static bool InitStaticVariableHelper(Microsoft.VisualBasic.CompilerServices.StaticLocalInitFlag flag)
{
if (flag.State == 0) {
flag.State = 2;
return true;
} else if (flag.State == 2) {
throw new Microsoft.VisualBasic.CompilerServices.IncompleteInitialization();
} else {
return false;
}
}
That way, you can implement whatever you want in your plugin.
I believe you could load your plugins in a separate appdomain with restricted permissions to everything.
The files (Javascript / CSS / Html) should be available by accessing the full path of the file.
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
If the resources is embedded into the plugin DLL, you could read the stream from the loaded assembly or let the plugin manage its own embedded files.
For question Number 2, you can use
MS Build
to change the contenttype during build process.
You have to make yourself confident with MS Build
I have an ASP.Net website that references a class library. In the class library I need to read a file into memory.
At the top level of my class library there is a folder called EmailTemplateHtml containing the file MailTemplate.html that I want to read in.
How can I do this?
In Visual Studio, you can configure your library such that the file is copied into the build directory of any project that depends upon it. Then you can get the path to the build directory at runtime in order to read your file.
Step by step instructions, starting from a fresh solution:
Create your application project and your class library project.
Add a reference to the class library project from the application project via Properties->Add->Reference from the application's context menu in Solution Explorer:
Create the file in your class library project that you need to read, then set its Copy to Output Directory property to either Copy always or Copy if newer via the Properties pane in Solution Explorer:
From within either the class library project or your application (either will work with exactly the same code), reference your file relative to Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location). For example:
using System.Reflection;
using System.IO;
namespace MyLibrary
{
public class MyClass
{
public static string ReadFoo()
{
var buildDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var filePath = buildDir + #"\foo.txt";
return File.ReadAllText(filePath);
}
}
}
(Note that back before .NET Core, you could use a file path relative to System.IO.Directory.GetCurrentDirectory() instead, but this doesn't work in a .NET Core application because the initial working directory for .NET Core apps is the source directory instead of the build directory, apparently because this was needed by ASP.NET Core.)
Go ahead and call your library code from your application code, and everything will work fine. e.g.:
using Microsoft.AspNetCore.Mvc;
using MyLibrary;
namespace AspCoreAppWithLib.Controllers
{
public class HelloWorldController : Controller
{
[HttpGet("/read-file")]
public string ReadFileFromLibrary()
{
return MyClass.ReadFoo();
}
}
}
using System.IO;
using System.Reflection;
public static string ExecutionDirectoryPathName()
{
var dirPath = Assembly.GetExecutingAssembly().Location;
dirPath = Path.GetDirectoryName(dirPath);
return Path.GetFullPath(Path.Combine(dirPath, "\EmailTemplateHtml\MailTemplate.html"));
}
If you want to find the path where the assembly is located; from within the assembly then use the following code:
public static string ExecutionDirectoryPathName
{
get
{
var dirPath = Assembly.GetExecutingAssembly().Location;
dirPath = Path.GetDirectoryName(dirPath);
return dirPath + #"\";
}
}
I am not sure what you mean by a folder in the class library but you can use the current working directory if you wish to build a path as follows:
System.IO.Directory.GetCurrentDirectory()
You can then use the Path.Combine() method to build file paths.
You can add a Static class in your class library with some static methods/properties to set. Set the values from Global.ascx.cs on start app method. Now you can get the values of class library.
Hope this makes clear.
Happy coding
I'm working on a plugin for a existing C# .NET Program. It's structured in a manner where you put your custom .dll file in Program Root/Plugins/your plugin name/your plugin name.dll
This is all working well, but now I'm trying to use NAudio in my project.
I've downloaded NAudio via Nuget, and that part works fine, but the problem is that it looks for the NAudio.dll in Program Root, and not in the folder of my plugin.
This makes it hard to distribute my plugin, because it would rely on users dropping the NAudio.dll in their Program Root in addition to putting the plugin into the "Plugins" folder.
Source:
SettingsView.xaml:
<Button HorizontalAlignment="Center"
Margin="0 5"
Width="120"
Command="{Binding SoundTestCommand,
Source={StaticResource SettingsViewModel}}"
Content="Sound Test" />
SettingsViewModel.cs:
using NAudio.Wave;
.
.
.
public void SoundTest()
{
IWavePlayer waveOutDevice;
WaveStream mainOutputStream;
WaveChannel32 inputStream;
waveOutDevice = new WaveOut();
mainOutputStream = new Mp3FileReader(#"E:\1.mp3");
inputStream = new WaveChannel32(mainOutputStream);
inputStream.Volume = 0.2F;
waveOutDevice.Init(mainOutputStream);
waveOutDevice.Play();
}
How can I get C# to look for NAudio in Program Root/Plugins/my plugin name/NAudio.dll instead of looking for it in Program Root/NAudio.dll ?
I'm using VS Express 2013, Target Framework is 4.5 and Output type is Class Library.
Edit:
I found 2 ways to make this work ( I'm not sure what the pros and cons of both methods are - if anyone knows I would appreciate additional information ).
Using the NuGet Package Costura.Fody.
After installing the NuGet package, I simply had to set all other References "Copy Local" to "False" and then set "Copy Local" for NAudio to "True".
Now when I build, the NAudio.dll is compressed and added to my own DLL.
Using the AssemblyResolver outlined below.
It didn't work right away though, so here is some additional information that may help anyone facing the same issue:
I put Corey's code as he posted it into the Helpers folder.
My entry point is Plugin.cs, the class is public class Plugin : IPlugin, INotifyPropertyChanged
In there, the entry method is public void Initialize(IPluginHost pluginHost), but simply putting PluginResolver.Init(path) did not work.
The host program uses WPF and is threaded and I had to use a dispatcher helper function of the host program to get it to work: DispatcherHelper.Invoke(() => Resolver.Init(path));
As mentioned, I'm currently unsure which method to use, but I'm glad I got it to work. Thanks Corey!
You can use the PATH environment variable to add additional folders to the search path. This works for native DLLs, but I haven't tried to use it for .NET assemblies.
Another option is to add a hook to the AssemblyResolve event on the current application domain and use a custom resolver to load the appropriate assembly from wherever you find it. This can be done at the assembly level - I use it in NAudio.Lame to load an assembly from a resource.
Something along these lines:
public static class PluginResolver
{
private static bool hooked = false;
public static string PluginBasePath { get; private set; }
public static void Init(string BasePath)
{
PluginBasePath = BasePath;
if (!hooked)
{
AppDomain.CurrentDomain.AssemblyResolve += ResolvePluginAssembly;
hooked = true;
}
}
static Assembly ResolvePluginAssembly(object sender, ResolveEventArgs args)
{
var asmName = new AssemblyName(args.Name).Name + ".dll";
var assemblyFiles = Directory.EnumerateFiles(PluginBasePath, "*.dll", SearchOption.AllDirectories);
var asmFile = assemblyFiles.FirstOrDefault(fn => string.Compare(Path.GetFileName(fn), asmName, true) == 0);
if (string.IsNullOrEmpty(asmFile))
return null;
return Assembly.LoadFile(asmFile);
}
}
(Usings for the above: System.IO, System.Reflection, System.Linq)
Call Init with the base path to your plugins folder. When you try to reference an assembly that isn't loaded yet it will search for the first file that matches the base name of the assembly with dll appended. For instance, the NAudio assembly will match the first file named NAudio.dll. It will then load and return the assembly.
No checking is done in the above code on the version, etc. and no preference is given to the current plugin's folder.
okay, here is the question. I have two projects one is C# Console and other is Class library.
I am accessing/calling Class library method from the console app.
There is a folder called Files within the class library project.
I need to get the path of the Class library's files folder but whenever I use
System.IO.Directory.GetCurrentDirectory();
and
Environment.CurrentDirectory;
it is giving me path of the Console project which I am using to call the method.
Above methods are giving me path like
C:\\ConsolePro\\bin\\Debug
but I need the path of Class library project
C:\\ClassLibPro\\bin\\Debug
Please advise
Once the code is compiled and running, 'Project Path' has no meaning. All you can determine are the file locations of the compiled assemblies. And you can only do what you are asking if your Console project references the built 'class library' DLL directly, rather than via a Project Reference.
Then, you can make use of Reflection to get Assembly paths like;
string path = Assembly.GetAssembly(typeof (SomeClassInOtherProject)).Location;
You should be able to use Directory.GetParent(Directory.GetCurrentDirectory()) a few times to get higher level directories and then add the path of the lib directory to the end of that.
I believe the problem is:
Since the Console project has the DLL file reference it is using DLL to call any methods.
At this time it is returning the class library projct's DLL location which is located in console project's bin directory and it doesn't know about the physical location of class library project.
so essentially it is returning the same project path. I will have to move both projects in same directory in order to solve this issue.
If you loading the class library from another assembly.
string Path = System.Reflection.Assembly.GetAssembly(typeof({LibraryClassName})).Location;
string PathToClassLibPro = Path.GetDirectoryName( Path);
Replace {LibraryClassName} with the class name of your library.
I hope I understand u corretly:
Path.GetDirectoryName(typeof(Foo.MyFooClass).Assembly.Location);
I would recommend one of two options.
If the files are small include them in the class library and stream them to a temp location when needed
Other option is to copy the files during the build to the output directory and use them that way. In cases of multiple shared projects it is best to have a common bin folder that you copy assemblies to and run from that location.
Despite i cant find a good solution i use this trick :
as long as you want to come back to your ideal path u should add Directory.GetParent() instead of ...
Directory.GetParent(...(Directory.GetParent(Directory.GetCurrentDirectory()).ToString()...).ToString()
I use the following approach to get the current project path at runtime:
public static class ProjectInfo {
public static string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
public static string projectPath = appDirectory.Substring(0, appDirectory.IndexOf("\\bin"));
}
I had this exact issue as well where I couldn't access the file in my namespace's bin/debug folder. My solution was to manipulate the string using Split() then construct a new string which is the absolute path to the json file I have in my namespace.
private static string GetFilePath()
{
const char Escape = '\\'; //can't have '\' by itself, it'll throw the "Newline in constant" error
string directory = Environment.CurrentDirectory;
string[] pathOccurences = directory.Split(Escape);
string pathToReturn = pathOccurences[0] + Escape; //prevents index out of bounds in upcoming loop
for(int i = 1; i < pathOccurences.Length; i++)
{
if (pathOccurences[i] != pathOccurences[i - 1]) //the project file name and the namespace file name are the same
pathToReturn += pathOccurences[i] + Escape;
else
pathToReturn += typeof(thisClass).Namespace + Escape; //In the one occurrence of the duplicate substring, I replace it with my class Namespace name
}
return pathToReturn + "yourFile.json";
}
I personally don't like this solution, but it was the only answer I could think of.