C# how to call files relative address rather absolute address - c#

I have found myself having to rely on having to use some local folders. No problem normally, but the main issue is that if transfer the project to another machine, even during development the structure reports back an error.
The error is that is page can't be displayed on a local website and an IO error when I go to read from the file.
The directory is as follows
C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset
The folders in question are
Master Audio
Images
User Audio
The actual folder as they're used
private String folderPlay = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\Master Audio\";
private String folderRecord = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\User Audio\";
private String folderImages = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\Images\";
What I would like to do is be able to call those file relative to the code? Also if it's not too much trouble could a small note be attached so I can understand/

Use only the path in your project rather than whole physical path
private String folderPlay = #"~/Master Audio";
private String folderRecord = #"~/User Audio";
private String folderImages = #"~/Images";
and my suggestion is use path like Master_Audio, User_Audio. Don't put space between word

What I found useful in similar situation is to actually inject content root dir to the class that needs to know the location of particular resources:
E.g.
class Foo{
private string readonly _contentRoot;
public Foo (string contentRoot)
{
_contentRoot = contentRoot
}
public void DoSomethingWithImage()
{
string imgRelativePath = "/img/img1.png";
string imgPath = Path.Combine(_contentRoot, imgRelativePath);
// do something with imgPath
}
}
The benefit of this is that you can now write tests for your methods (which might be difficult otherwise). Sometimes unit testing framework copies libs to a temp location, but you still want to run the tests.
You can resolve root directory from several locations, depending how you run the code:
For unit tests, use Assembly.GetExecutingAssembly().GetName().CodeBase
For Web app HttpRuntime or HttpContext
For Windows app AppDomain.BaseDirectory or similar
Rule of thumb:
You should not have any hardcoded absolute paths (pain with testing, running on another machine, deployment)
Instead, use relative paths (usually relative to project root or root/content)

Related

Location of original assembly(not the current executing)

Suppose I have a solution which contains 4 projects, A, A_UnitTests, B, and B_UnitTests.
Project A has a data file, which is added as a link to A_UnitTests and set to copy to the output directory. When unit tests are run or when the code is executed in production, the path to that file is correctly identified using the following code snippet:
public static string GetFullPath(string relativePath)
{
string retVal = string.Empty;
if (System.Web.HttpContext.Current == null)
{
string locationBeforeShadowCopy = typeof(A.SomeClassInA).Assembly.CodeBase;
UriBuilder uri = new UriBuilder(locationBeforeShadowCopy);
string locationWithoutUriPrefixes = Uri.UnescapeDataString(uri.Path);
string dir = Path.GetDirectoryName(locationWithoutUriPrefixes);
retVal = Path.Combine(dir, relativePath);
}
else
{
// stuff that doesn't matter
}
return retVal;
}
However, I have a new testcase in B_UnitTests which attempts to use this code path to find the file location. However, even though I call typeof(A.SomeClassInA).Assembly.CodeBase, it is being called from B_UnitTests, using its referenced DLLs. This means the path return is the B_UnitTests output directory + a relative path. So it doesn't find the data file.
Without resorting to hard coded settings and build scripts, what could I use to specify the correct path?
Update (clarification)
The real issue is with typeForClassInA.Assembly.CodeBase returning the path of the executing assembly rather than A itself. It seems very wrong to provide a type that comes from some assembly but instead of returning the original assembly location, it returns the path to the executing assembly which happens to have a reference to it.
If there is a reference to 'typeForClassInA', then its assembly will be being copied into the output directory of B_UnitTests. So when you ask for CodeBase of that type's assembly from a test in B_UnitTests, it is (correctly) pointing at the version of assembly A in the B_UnitTests output folder, because that's where it's being loaded from.
I admit that I avoid using Shadow Copy to avoid exactly these kinds of problems of locating resources which are alongside the assembly, since ShadowCopy doesn't understand that they are needed, and they don't get shadow copied.
Another thing which can help is to build all the projects into the same output folder by changing all the project output folders to be "..\bin". For example, this would mean that A_UnitTests would not need the link to the resource file (once shadow copy is off).
I have a method similar to the one you've shown which goes "up" from the assembly's location (which for me is the shared bin folder) to the solution's location; and my relative paths are 'rooted' at that folder.
If that all sounds too complex, you could just use the same approach that A_UnitTests did, of including a link to it from B_UnitTests.

Access image file sitting within VS Solution

I have the following method code:
private Image GetMapIcon()
{
//TODO Fix path
return Image.FromFile("D:\\Work\\FindandCompare\\Main\\NHSChoices.FindAndCompare.Web\\Content\\img\\mapIcon.png");
}
As you can see the path will only work on my machine. I tried all sorts to get it to work without being so specific, but I couldn't! For example:
private Image GetMapIcon()
{
//TODO Fix path
return Image.FromFile(Url.Content("~/Content/img/mapIcon.png"));
}
The method is sitting within an MVC Controller.
I do not want to add a whole new Class Library project to the VS Solution to hold a resource. Is there an easier way that will work once this is deployed to the server?
You can use Server.MapPath:
private Image GetMapIcon()
{
return Server.MapPath("~/Content/img/mapIcon.png")
}
To include the image in the publish simply highlight the image in the solution explorer then change the 'Build action' to 'Content'.

Proper way to hide API keys in Git

In my C# project have APIKeys.cs file which have const strings with API keys.
I want those strings to be empty in Git server but have actual API keys in my local computer.
So peoples who pull project can compile it without problem and still my local computer gonna have API keys in same file.
If I try to upload APIKeys.cs file with empty strings then I can't have local file with API keys because when I try to push it, it will overwrite empty APIKeys.cs file. Also I can't ignore this file too because it will remove empty APIKeys.cs file from Git server.
So what is best automated approach for this problem which will allow class file with empty strings in server, so project will be compileable when people pull it and have real class file in local computer?
I figured another solution now which is not perfect but still good enough for me, example:
APIKeys.cs file:
public static partial class APIKeys
{
public static readonly string ImgurClientID = "";
public static readonly string ImgurClientSecret = "";
public static readonly string GoogleClientID = "";
public static readonly string GoogleClientSecret = "";
public static readonly string PastebinKey = "";
...
}
APIKeysLocal.cs file:
public static partial class APIKeys
{
static APIKeys()
{
ImgurClientID = "1234567890";
ImgurClientSecret = "1234567890";
GoogleClientID = "1234567890";
GoogleClientSecret = "1234567890";
PastebinKey = "1234567890";
...
}
}
Ignore APIKeysLocal.cs file in Git and people who don't have this file can still be able to compile project if they remove this file from solution explorer.
I also automatically create empty APIKeysLocal.cs file if it is not already exists using project pre build event:
cd $(ProjectDir)APIKeys\
if not exist APIKeysLocal.cs (
type nul > APIKeysLocal.cs
)
That way user don't need to do anything to be able to compile project.
Accept that you cannot hide unencrypted private keys in a public space.
What you can do is move the keys to a private space, and then reference that private space from code.
Your private space might be environment variables or the Windows registry, it should be something outside the source code of your app.
Another approach is to create a new config file (e.g. keys.config) specifically for storing private keys, and then exclude this file from source control.
This means you don't share your private keys, but it also means that you need to document (perhaps in readme.md) that users will need to recreate their own keys.config. Even better (thanks #Joey) is to include a sample config file (keys.sample.config) in the solution, illustrating what's needed.
Here is an example
You've got two options:
Tell Git to ignore changes to APIKeys.cs on your local machine:
git update-index --skip-worktree APIKeys.cs
This will cause local changes not to get committed. If you ever do want to commit changes to the file, you'll have to undo this with the --no-skip-worktree flag.
Rename the APIKeys.cs file to something like APIKeys.template.cs, containing the blank strings that you want to share. Keep this file in your repository. Copy that file to APIKeys.cs. Add APIKeys.cs to your .gitignore. Add instructions to copy the template file and modify with local settings.
git mv APIKeys.cs APIKeys.template.cs
$EDITOR APIKeys.template.cs
git commit
cat APIKeys.cs >> .gitignore
cp APIKeys.template.cs APIKeys.cs
Isn't this very similar to the problem of injecting a build number into your component? The way I do that is to have a pre-build step that generates a file called AssemblyVersionInfo.cs based on some environment variable. You could do the same thing with your API Keys.
In the pre-build step for the component that compiles in the API keys put something like this:-
if not defined API_KEY set API_KEY=DEFAULT_KEY
echo public class ApiKeys>"$(SolutionDir)src\ApiKeys.cs"
echo {>>"$(SolutionDir)src\ApiKeys.cs"
echo public const string Key="%API_KEY%";>>"$(SolutionDir)src\ApiKeys.cs"
echo }>>"$(SolutionDir)src\ApiKeys.cs"
Then you set either a user or system environment variable on your local machine with the real key in it.
setx API_KEY THE_REAL_KEY
To avoid Git wanting to commit the file just add it to the .gitignore.

How to get Current Project Directory path using C#

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.

httpcontext.current.server.mappath Object reference not set to an instance of an object

I am using the following code within a class:
string filePath = HttpContext.Current.Server.MapPath("~/email/teste.html");
The file teste.html is in the folder
But when it will open the file the following error is being generated:
Object reference not set to an instance of an object.
Don't use Server.MapPath. It's slow. Use this instead, HttpRuntime.AppDomainAppPath. As long as your web site is running, this property is always available to you.
Then use it like this:
string filePath = Path.Combine(HttpRuntime.AppDomainAppPath, "email/teste.html");
if the code is not running from within a thread is executing a httprequest then HttpContext.Current is null (for example when you method is called via BeginInvoke) - see http://forums.asp.net/t/1131004.aspx/1 .
You can always use HttpRuntime see http://msdn.microsoft.com/en-us/library/system.web.httpruntime.aspx
If there is no HttpContext (e.g. when the method is called via BeginInvoke, as Yahia pointed out), the call to HttpContext.Current.Server.MapPath() must fail. For those scenarios, there's HostingEnvironment.MapPath() in the System.Web.Hosting namespace.
string filePath = HostingEnvironment.MapPath("~/email/teste.html");
You can use something like the following piece of code. One thing to note is that I was facing an issue, where I was trying to access a .txt file from within a TestMethod and everything was failing except for this...and yeah it works for non-Unit Test Scenarios too.
string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,#"..\..") + "\\email\\teste.html";
Issue: I had an "Images" folder inside a class library project. But using the above answers, I was not able to get the physical path of the folder to read/write the files inside that folder.
Solution: The below code worked for me to get a physical path in the class library project.
string physicalPath = System.IO.Path.GetFullPath("..\\..\\Images");
I hope, it will help someone who is facing the same issue as me.

Categories