Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

19 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
martes, 4 de noviembre de 2025
SLNX: el nuevo formato de archivo de solución

Si, por curiosidad o necesidad, alguna vez has abierto un archivo de solución de Visual Studio (.sln) con un editor cualquiera, habrás comprobado que se trata de un archivo de texto plano.

Su estructura, aunque no es excesivamente compleja y está bien documentada, es propietaria de Visual Studio y no es evidente a simple vista, resulta demasiado verbosa, tiene mucha información irrelevante duplicada y, en general, no es nada amigable para los desarrolladores que se ven obligados a modificarla, sobre todo cuando se trata de resolver conflictos en sistemas de control de versiones. Y, por supuesto, es algo que se agrava conforme crece el número de proyectos en la solución.

Esto llevó a Microsoft a presentar hace unos meses el nuevo formato de archivo de solución .slnx, una alternativa basada en XML mucho más ligera, concisa y fácil de entender, que será el utilizado por defecto a partir de la llegada de .NET 10.

Lo vemos en profundidad a continuación.

El nuevo formato .slnx

Para comprender bien el enfoque de este nuevo formato, lo mejor es compararlo con el formato tradicional .sln. Partamos de una solución relativamente simple, con la siguiente estructura:

MyApp.sln
|-- MyApp
|-- MyApp.Application
|-- MyApp.Domain
|-- MyApp.Infrastructure
|-- Extra files
    |-- README.txt

El archivo de solución en formato .sln correspondiente a esta estructura sería el siguiente:

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36616.10 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApp", "MyApp\MyApp.csproj", "{BEF31430-FCBF-4293-911F-668D40006860}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApp.Domain", "MyApp.Domain\MyApp.Domain.csproj", "{B89A57B4-ACC1-42C1-A17E-6A0A8CC9DD6E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApp.Application", "MyApp.Application\MyApp.Application.csproj", "{1687322D-3B21-448B-83CD-859E4A46A420}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApp.Infrastructure", "MyApp.Infrastructure\MyApp.Infrastructure.csproj", "{2EF1B4B4-A954-40E6-A7C5-00151236431F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extra files", "Extra files", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
    ProjectSection(SolutionItems) = preProject
        README.txt = README.txt
    EndProjectSection
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
        Debug|Any CPU = Debug|Any CPU
	Release|Any CPU = Release|Any CPU
    EndGlobalSection
    GlobalSection(ProjectConfigurationPlatforms) = postSolution
	{BEF31430-FCBF-4293-911F-668D40006860}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
	{BEF31430-FCBF-4293-911F-668D40006860}.Debug|Any CPU.Build.0 = Debug|Any CPU
	{BEF31430-FCBF-4293-911F-668D40006860}.Release|Any CPU.ActiveCfg = Release|Any CPU
	{BEF31430-FCBF-4293-911F-668D40006860}.Release|Any CPU.Build.0 = Release|Any CPU
	{B89A57B4-ACC1-42C1-A17E-6A0A8CC9DD6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
	{B89A57B4-ACC1-42C1-A17E-6A0A8CC9DD6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
	{B89A57B4-ACC1-42C1-A17E-6A0A8CC9DD6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
	{B89A57B4-ACC1-42C1-A17E-6A0A8CC9DD6E}.Release|Any CPU.Build.0 = Release|Any CPU
	{1687322D-3B21-448B-83CD-859E4A46A420}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
	{1687322D-3B21-448B-83CD-859E4A46A420}.Debug|Any CPU.Build.0 = Debug|Any CPU
	{1687322D-3B21-448B-83CD-859E4A46A420}.Release|Any CPU.ActiveCfg = Release|Any CPU
	{1687322D-3B21-448B-83CD-859E4A46A420}.Release|Any CPU.Build.0 = Release|Any CPU
	{2EF1B4B4-A954-40E6-A7C5-00151236431F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
	{2EF1B4B4-A954-40E6-A7C5-00151236431F}.Debug|Any CPU.Build.0 = Debug|Any CPU
	{2EF1B4B4-A954-40E6-A7C5-00151236431F}.Release|Any CPU.ActiveCfg = Release|Any CPU
	{2EF1B4B4-A954-40E6-A7C5-00151236431F}.Release|Any CPU.Build.0 = Release|Any CPU
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
	HideSolutionNode = FALSE
    EndGlobalSection
    GlobalSection(ExtensibilityGlobals) = postSolution
	SolutionGuid = {7882EB29-7FE2-4245-8787-3CFDC076B009}
    EndGlobalSection
EndGlobal

Aunque no es fácil de leer, pueden distinguirse los distintos proyectos que componen la solución, su configuración, archivos adicionales e identificadores únicos de todos los elementos.

En el nuevo formato .slnx, el mismo archivo de solución se representaría así:

<Solution>
  <Folder Name="/Extra files/">
    <File Path="README.txt" />
  </Folder>
  <Project Path="MyApp.Application/MyApp.Application.csproj" />
  <Project Path="MyApp.Domain/MyApp.Domain.csproj" />
  <Project Path="MyApp.Infrastructure/MyApp.Infrastructure.csproj" />
  <Project Path="MyApp/MyApp.csproj" />
</Solution>

¡Uau! Mucho mejor, ¿eh? Han desaparecido los GUIDs (que no aportaban demasiado), las duplicidades y el contenido se centra exclusivamente en lo relevante: la estructura de la solución y las rutas a los proyectos y archivos adicionales.

Con esto, se consiguen los objetivos que pretendía Microsoft:

  • Es fácilmente legible y editable por humanos. Ha desaparecido la información redundante, los GUIDs y secciones innecesarias, pasando en algunos casos a ser configuraciones implícitas.
  • Al ser mucho más conciso, su carga y guardado son más rápidos.
  • Y al simplificarse su estructura, reduce la probabilidad de conflictos en sistemas de control de versiones.
  • Se abandona el formato propietario en favor de XML, un estándar ampliamente conocido.
  • Está alineado con otros formatos, como los usados por MSBuild.
  • Es compatible con versiones de Visual Studio desde la 17.14 en adelante.

Cómo crear soluciones en formato .slnx

El nuevo formato .slnx será el utilizado por defecto para las nuevas soluciones a partir de .NET 10 y Visual Studio 2026, así que no habrá que hacer nada especial para comenzar a utilizarlo cuando comencemos nuestros proyectos. Desde el IDE, se creará un archivo de este tipo al utilizar el menú File > New > Project/Solution, y desde la línea de comandos, la siguiente orden creará directamente el archivo .slnx:

dotnet new sln

De hecho, si quisiéramos crear la solución en el formato anterior también se podría, pero tendríamos que especificarlo explícitamente:

dotnet new sln --format sln

Migrar soluciones existentes al nuevo formato

Los archivos .slnx serán compatibles con Visual Studio 2022 (actualizado a la versión 17.14 o superior), así como con Visual Studio Code y otros editores y herramientas que trabajen con soluciones de .NET. Sin embargo, dado que el nuevo formato mejora bastantes aspectos del anterior, es recomendable migrar las soluciones existentes, incluso aunque estemos usando proyectos en versiones anteriores de .NET.

Esto podemos conseguirlo fácilmente desde Visual Studio 2026 o 2022 (17.14 o superior) abriendo la solución en el IDE y utilizando el menú File > Save As..., seleccionando el nuevo formato .slnx en el cuadro de diálogo:

Convertir la solución .sln a .slnx

También es posible realizar la conversión desde la línea de comandos utilizando el comando dotnet sln migrate, que generará el archivo .slnx correspondiente en la misma carpeta donde se encuentra el archivo .sln original:

D:\Projects\Stuff\MyApp>dir
 El volumen de la unidad D es Datos
 El número de serie del volumen es: 8CBC-81E3

 Directorio de D:\Projects\Stuff\MyApp

    <DIR>          .
    <DIR>          ..
    <DIR>          MyApp
    <DIR>          MyApp.Application
    <DIR>          MyApp.Domain
    <DIR>          MyApp.Infrastructure
    <DIR>          MyApp.Shared
             3.081 MyApp.sln
                 0 README.txt
  2 archivos          3.081 bytes
  7 dirs  436.579.930.112 bytes libres

D:\Projects\Stuff\MyApp>dotnet sln migrate
.slnx file D:\Projects\Stuff\MyApp\MyApp.slnx generated.

D:\Projects\Stuff\MyApp>dir
 El volumen de la unidad D es Datos
 El número de serie del volumen es: 8CBC-81E3

 Directorio de D:\Projects\Stuff\MyApp

    <DIR>          .
    <DIR>          ..
    <DIR>          MyApp
    <DIR>          MyApp.Application
    <DIR>          MyApp.Domain
    <DIR>          MyApp.Infrastructure
    <DIR>          MyApp.Shared
             3.081 MyApp.sln
               479 MyApp.slnx
                 0 README.txt
  3 archivos          3.560 bytes
  7 dirs  436.579.930.112 bytes libres

D:\Projects\Stuff\MyApp>_

Es importante tener en cuenta que si en la misma carpeta existen ambos archivos, .sln y .slnx, habrá que especificar explícitamente cuál de ellos se desea usar. Por ejemplo, el siguiente comando

D:\Projects\Stuff\MyApp>dotnet build
MSBUILD : error MSB1011: Specify which project or solution file to use because this folder contains more than one project or solution file.

D:\Projects\Stuff\MyApp>dotnet build .\MyApp.slnx
Restore complete (0,8s)
  MyApp.Domain net10.0 succeeded (0,3s) → MyApp.Domain\bin\Debug\net10.0\MyApp.Domain.dll
  MyApp.Infrastructure net10.0 succeeded (0,3s) → MyApp.Infrastructure\bin\Debug\net10.0\MyApp.Infrastructure.dll
  MyApp.Application net10.0 succeeded (0,2s) → MyApp.Application\bin\Debug\net10.0\MyApp.Application.dll
  MyApp net10.0 succeeded (0,5s) → MyApp\bin\Debug\net10.0\MyApp.dll

Build succeeded in 1,6s

D:\Projects\Stuff\MyApp>_

Sinceramente, no veo ningún motivo para mantener el archivo .sln en la carpeta una vez migremos a .slnx, pero bueno. La cuestión es que si los mantenemos ahí, este problema ocurrirá con todos los comandos de .NET CLI que trabajen con soluciones, como los usados para añadir o eliminar proyectos de una solución. Por ejemplo, para añadir un nuevo proyecto a una solución en formato .slnx si tenemos ambas en la carpeta, habría que hacer lo siguiente:

D:\Projects\Stuff\MyApp>dotnet sln .\MyApp.slnx add Utils\Utils.csproj

¡Espero que te resulte útil!

Publicado en Variable not found.

Aún no hay comentarios, ¡sé el primero!