visual valid type see remarks example cref comment c# .net nunit codebase

valid - ¿Cómo puedo convertir Assembly.CodeBase en una ruta del sistema de archivos en C#?



summary example c# (5)

Tengo un proyecto que almacena plantillas en una carpeta /Templates lado de las DLL y EXE.

Quiero determinar esta ruta de archivo en tiempo de ejecución, pero usando una técnica que funcionará tanto en una prueba unitaria como en producción (¡y no quiero deshabilitar la copia oculta en NUnit!)

Assembly.Location no es bueno porque devuelve la ruta del conjunto de shadow-copies cuando se ejecuta bajo NUnit.

Environment.CommandLine también es de uso limitado porque en NUnit et al devuelve el camino a NUnit, no a mi proyecto.

Assembly.CodeBase parece prometedor, pero es una ruta UNC:

file:///D:/projects/MyApp/MyApp/bin/debug/MyApp.exe

Ahora podría convertir esto en una ruta de sistema de archivos local usando la manipulación de cadenas, pero sospecho que hay una forma más limpia de hacerlo enterrado en el framework .NET en alguna parte. Alguien sabe una forma recomendada de hacer esto?

(Lanzando una excepción si la ruta UNC no es un file:/// URL es absolutamente correcto en este contexto)


Assembly.CodeBase parece prometedor, pero es una ruta UNC:

Tenga en cuenta que se trata de algo que se aproxima a un archivo uri , no a una ruta UNC .

Usted resuelve esto haciendo la manipulación de cuerdas a mano. Seriamente.

Pruebe todos los demás métodos que pueda encontrar en SO con el siguiente directorio (literal):

C:/Test/Space( )(h#)(p%20){[a&],t@,p%,+}.,/Release

Esta es una ruta de Windows válida, aunque algo inusual. (Algunas personas tendrán alguno de estos caracteres en sus rutas, y querrían que su método funcione para todos , ¿no?)

Las propiedades disponibles de la base de código ( no queremos Location , ¿verdad? ) Son entonces (en mi Win7 con .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

Notarás:

  • CodeBase no se escapa en absoluto, es solo la ruta local normal con el prefijo file:/// y las barras invertidas reemplazadas. Como tal, no funciona alimentar esto a System.Uri .
  • EscapedCodeBase no se escapa por completo ( no sé si esto es un error o si esto es una deficiencia del esquema de URI ):
    • Tenga en cuenta cómo el carácter de espacio ( ) se traduce a %20
    • ¡pero la secuencia %20 también se traduce en %20 ! (% de % no se escapó en absoluto)
    • ¡Nadie puede reconstruir el original de esta forma destrozada!

Para archivos locales (y eso es todo lo que me importa para las cosas de CodeBase , porque si el archivo no es local, es probable que desee utilizar .Location todas formas, lo siguiente me funciona (tenga en cuenta que no es el más bonito) ya sea:

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); }

Estoy seguro de que se pueden encontrar mejores soluciones, probablemente incluso se podría llegar a una solución que CodeBase una correcta descodificación / URL de la propiedad CodeBase si es una ruta local, pero dado que uno puede simplemente quitar el file:/// y listo, diría que esta solución es lo suficientemente buena, aunque ciertamente muy fea.


Como etiquetó esta pregunta NUnit , también puede usar AssemblyHelper.GetDirectoryName para obtener el directorio original del ensamblaje en ejecución:

using System.Reflection; using NUnit.Framework.Internal; ... string path = AssemblyHelper.GetDirectoryName(Assembly.GetExecutingAssembly())


Esto debería funcionar:

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");

Estoy usando esto para poder iniciar sesión desde las bibliotecas dll usando un archivo log4net.config independiente.


Necesita usar System.Uri.LocalPath:

string localPath = new Uri("file:///D:/projects/MyApp/MyApp/bin/debug/MyApp.exe").LocalPath;

Entonces, si desea la ubicación original del ensamblaje que se está ejecutando actualmente:

string localPath = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;


Una solución más, que incluye rutas complejas:

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)); }

y pruebas:

[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)); }