He necesitado convertir un archivo de Excel con sus propias columnas definidas en uno nuevo con columnas dinámicas, y estaba un poco confundido sobre cómo hacerlo correctamente sin algunos problemas de rendimiento graves.
Llegué a un tutorial que puedes encontrar aquí que utiliza .NET ExpandoObject que prácticamente te permite crear un objeto y agregar miembros dinámicos.
ExpandoObject
La definición de Microsoft es:
Representa un objeto cuyos miembros se pueden agregar y eliminar dinámicamente en tiempo de ejecución.
Y tiene algunas observaciones:
La clase ExpandoObject le permite agregar y eliminar miembros de sus instancias en tiempo de ejecución y también establecer y obtener valores de estos miembros. Esta clase admite el enlace dinámico, que le permite utilizar una sintaxis estándar como sampleObject.sampleMember en lugar de una sintaxis más compleja como sampleObject.GetAttribute ("sampleMember").
Comportamiento actual
Tenemos un ExcelExportServiceque, al pasar un List<T>, en este caso ExcelItem, lo usaremos Reflection para crear un xlsx archivo.
Hasta ahora, nuestro código se ve así:
ExcelItem es el objeto con todas las propiedades que se utilizan para generar el archivo de Excel.
Este enfoque funciona perfectamente, el xlsxarchivo se genera sin problemas con cada columna siendo cada propiedad, usaría este GetExcelBytes método de esta manera, por ejemplo, para guardarlo en un archivo:
El problema
Como lo que estamos haciendo bien es usar todas las propiedades del ExcelItem objeto, todo lo que quiero es no usarlas todas, solo usar 2 o 3 de ellas.
También es un requisito que no quiera cambiar el código, todo lo debe hacer el administrator que decidirá qué columnas se deben mostrar y él puede o no conocer las propiedades del código.
TLDR: todo debe ser dinámico, tenemos un objeto con propiedades y debemos asegurarnos de que el archivo generado tenga N propiedades de ese objeto, pero obviamente no está codificado.
Columnas dinámicas
El cambio sería usar un dynamic objeto, porque nos gustaría establecer qué propiedades del objeto se usarán para generar la lista de columnas.
Digamos que tenemos un objeto con muchas propiedades, como una tonelada de ellas, y realmente no queremos cambiar el ExcelItem objeto cada vez que hacemos un cambio, creamos una ColumnExcelItem tabla, que se utilizará para generar eso ExcelItem.
Estructura de la base de datos
Guardamos esta definición en nuestra base de datos con algo como esto:
Tenga en cuenta que tenemos menos valores que las propiedades existentes del ExcelItem objeto, ya que tiene 6 propiedades y solo las hemos 3 incluido en la base de datos.
También tenga en cuenta que PropertyName debe coincidir con el nombre de propiedad del objeto que usaré para la asignación dinámica.
Construyendo el objeto
Ahora que tenemos la tabla de la base de datos, necesitamos construir el repositorio para obtener eso y poder usarlos en el código.
No haré un tutorial de esta parte, saltearé al comienzo de la nueva GetExcelBytes() función.
Ahora tendremos que crear un objeto con algunas de las propiedades de ExcelItem, pero completamente dinámico. En lugar de usar las 6 propiedades, solo estamos usando 3 de ellas, la que solo queremos enviar.
Ahora imaginemos que este ExcelItem objeto tiene 200 propiedades, loco, pero podría suceder.
Lo único que tengo que hacer es insertar esas propiedades que quiero representar en el archivo de Excel en esa ColumnExcelItem tabla y eso es todo.
Casos
En este caso, solo usamos un solo caso, pero supongamos que desea tener informes diferentes para algunos usuarios. Digamos que el admin rol debe obtener todas las propiedades, entonces crearía algo como esta estructura en la base de datos.
Luego, al obtener la plantilla, podría tener diferentes columnas, todas dinámicamente y sin código relacionado.
Si tenemos un usuario con el rol Admins, el archivo contendrá columnas Id, Name, Surname, Age, CreatedAt, UpdatedAt.
En el caso si es que generamos con el Users papel, que tendrá Name, Age. Surname.
Conclusión
Esta es una forma genial de aprender cómo dynamic funciona en .NET, y es una solución realmente buena cuando necesita tener diferentes roles o plantillas para diferentes usuarios, y no está realmente interesado en invertir tiempo en la codificación.