J'ai un projet qui stocke des modèles dans un \Templates
dossier à côté des DLL et EXE.
Je veux déterminer ce chemin de fichier au moment de l'exécution, mais en utilisant une technique qui fonctionnera à l'intérieur d'un test unitaire ainsi qu'en production (et je ne veux pas désactiver le cliché instantané dans NUnit!)
Assembly.Location
n'est pas bon car il renvoie le chemin de l'assembly copié en ombre lors de l'exécution sous NUnit.
Environment.CommandLine
est également d'une utilité limitée car dans NUnit et al, il renvoie le chemin vers NUnit, pas vers mon projet.
Assembly.CodeBase
semble prometteur, mais c'est un chemin UNC:
file:///D:/projects/MyApp/MyApp/bin/debug/MyApp.exe
Maintenant, je pourrais transformer cela en un chemin de système de fichiers local en utilisant la manipulation de chaînes, mais je soupçonne qu'il existe une façon plus propre de le faire enfoui quelque part dans le framework .NET. Quelqu'un connaît une façon recommandée de faire cela?
(Lancement d'une exception si le chemin UNC n'est pas un file:///
L'URL est très bien dans ce contexte)
Vous devez utiliser System.Uri.LocalPath:
string localPath = new Uri("file:///D:/projects/MyApp/MyApp/bin/debug/MyApp.exe").LocalPath;
Donc, si vous voulez l'emplacement d'origine de l'assembly en cours d'exécution:
string localPath = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;
Assembly.CodeBase semble prometteur, mais c'est un chemin UNC:
Notez que c'est quelque chose qui se rapproche d'un fichier uri , pas an chemin UNC .
Vous résolvez cela en faisant la manipulation de chaînes à la main. Sérieusement.
Essayez toutes les autres méthodes que vous pouvez trouver sur SO avec le répertoire suivant (textuellement):
C:\Test\Space( )(h#)(p%20){[a&],t@,p%,+}.,\Release
Il s'agit d'un chemin Windows valide, quoique quelque peu inhabituel. (Certaines personnes le feront ont l'un ou l'autre de ces caractères dans ces chemins, et vous voudriez que votre méthode fonctionne pour tous de ceux-ci, non?)
Les propriétés de base de code disponibles ( nous ne voulons pas de Location
, non? ) sont alors (sur mon Win7 avec .NET 4):
Assembly.CodeBase -> file:///C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release
Assembly.EscapedCodeBase -> file:///C:/Test/Space(%20)(h%23)(p%20)%7B%5Ba%26%5D,t@,p%,+%7D.,/Release
Vous noterez:
CodeBase
n'est pas échappé du tout, c'est juste le chemin local régulier préfixé avec file:///
et les barres obliques inversées ont été remplacées. En tant que tel, cela ne fonctionne pas fonctionne pour alimenter ceci à System.Uri
.EscapedCodeBase
n'est pas complètement échappé (je fais pas sais s'il s'agit d'un bug ou s'il s'agit d'une lacune du schéma URI ): %20
%20
séquence aussi se traduit par %20
! (pourcentage %
n'est pas échappé du tout)Pour les fichiers locaux (Et c'est vraiment tout ce qui m'importe pour le CodeBase
stuff, car si le fichier n'est pas local, vous voudrez probablement utiliser .Location
de toute façon, ce qui suit fonctionne pour moi (notez que ce n'est pas le plus joli non plus:
public static string GetAssemblyFullPath(Assembly assembly)
{
string codeBasePseudoUrl = Assembly.CodeBase; // "pseudo" because it is not properly escaped
if (codeBasePseudoUrl != null) {
const string filePrefix3 = @"file:///";
if (codeBasePseudoUrl.StartsWith(filePrefix3)) {
string sPath = codeBasePseudoUrl.Substring(filePrefix3.Length);
string bsPath = sPath.Replace('/', '\\');
Console.WriteLine("bsPath: " + bsPath);
string fp = Path.GetFullPath(bsPath);
Console.WriteLine("fp: " + fp);
return fp;
}
}
System.Diagnostics.Debug.Assert(false, "CodeBase evaluation failed! - Using Location as fallback.");
return Path.GetFullPath(Assembly.Location);
}
Je suis sûr que l'on peut trouver de meilleures solutions, probablement on pourrait même trouver une solution qui fait un bon encodage/décodage d'URL de la propriété CodeBase
si c'est un chemin local, mais étant donné qu'on peut simplement supprimer en dehors de file:///
et en finir, je dirais que cette solution est assez bonne, si certainement vraiment moche.
Cela devrait fonctionner:
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
Assembly asm = Assembly.GetCallingAssembly();
String path = Path.GetDirectoryName(new Uri(asm.EscapedCodeBase).LocalPath);
string strLog4NetConfigPath = System.IO.Path.Combine(path, "log4net.config");
J'utilise ceci pour pouvoir me connecter à partir des bibliothèques dll en utilisant un fichier log4net.config autonome.
Une autre solution, y compris des chemins complexes:
public static string GetPath(this Assembly assembly)
{
return Path.GetDirectoryName(Assembly.GetFileName());
}
public static string GetFileName(this Assembly assembly)
{
return Assembly.CodeBase.GetPathFromUri();
}
public static string GetPathFromUri(this string uriString)
{
var uri = new Uri(Uri.EscapeUriString(uriString));
return String.Format("{0}{1}", Uri.UnescapeDataString(uri.PathAndQuery), Uri.UnescapeDataString(uri.Fragment));
}
et tests:
[Test]
public void GetPathFromUriTest()
{
Assert.AreEqual(@"C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release", @"file:///C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release".GetPathFromUri());
Assert.AreEqual(@"C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release", @"file://C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release".GetPathFromUri());
}
[Test]
public void AssemblyPathTest()
{
var asm = Assembly.GetExecutingAssembly();
var path = asm.GetPath();
var file = asm.GetFileName();
Assert.IsNotEmpty(path);
Assert.IsNotEmpty(file);
Assert.That(File .Exists(file));
Assert.That(Directory.Exists(path));
}
Puisque vous avez marqué cette question NUnit, vous pouvez également utiliser AssemblyHelper.GetDirectoryName
pour obtenir le répertoire d'origine de l'assembly en cours d'exécution:
using System.Reflection;
using NUnit.Framework.Internal;
...
string path = AssemblyHelper.GetDirectoryName(Assembly.GetExecutingAssembly())