No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Aplicando el principio de responsabilidad única

6/16
Recursos

Aportes 26

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Dejo mi propuesta de clase ExportHelper (clase que reemplaza a ExportStudents) usando generics de modo que se pueda reutilizar con otros modelos.
Tiene una restriccion y es que solo podria ser usada con modelos que internamente tengan como mucho colecciones de tipos basicos (listas de enteros, listas de dobles, arreglos de enteros, etc).

using System.Collections;
using System.Text;

namespace SingleResponsability
{
    public class ExportHelper<T>
    {
        public void ExportToCSV(IEnumerable<T> items)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            string header = "";
            string[] dataRows = new string[items.Count()];
            foreach (var prop in typeof(T).GetProperties())
            {
                header += $"{prop.Name};";
                for (int i = 0; i < items.Count(); i++)
                {
                    var propValue = prop.GetValue(items.ToArray()[i]);
                    var propType = propValue.GetType();
                    if(propType.Name != nameof(String) 
                        && propType.GetInterface(nameof(IEnumerable)) != null)
                    {
                        dataRows[i] += $"{String.Join("|", (propValue as IEnumerable).Cast<object>().Select(x => x.ToString()))};";

                    }
                    else
                    {
                        dataRows[i] += $"{propValue};";
                    }
                }
            }
            sb.AppendLine(header.Trim(';'));
            foreach (var dataRow in dataRows)
            {
                sb.AppendLine(dataRow.Trim(';'));
            }
            System.IO.File.WriteAllText(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"Export_{typeof(T).ToString()}.csv"), sb.ToString(), Encoding.Unicode);
        }
    }
}

La implementacion en la clase program seria asi

using SingleResponsability;

StudentRepository studentRepository = new();
ExportHelper<Student> studentExport = new();
studentExport.ExportToCSV(studentRepository.GetAll());
Console.WriteLine("Proceso Completado");

Comparto mi propuesta para ExportHelper

Básicamente lo que realice fue que fuera un extended method, y le agregue un constraint para que solo aplique para clases, adicionalmente utilice refelection para recuperar el nombre de las propiedades del genérico y así mismo obtener los valores.

using System.Text;
using System.Collections;
namespace SingleResponsability
{
    public static class ExportHelper
    {

        public static void Export<T>(this IEnumerable<T> source) where T : class
        {
            var sb = new StringBuilder();
            var properties = typeof(T).GetProperties();
            var headers = string.Join(";",properties.Select(p => p.Name));
            sb.AppendLine(headers);
            foreach (var item in source)
            {
                string line = string.Empty;
                foreach (var prop in properties)
                {
                    object? value = prop.GetValue(item, null);

                    if (value is null) continue;
                    
                    if (value is not string && value is IEnumerable valuearray){
                        var values = valuearray.Cast<object>().Select(v => v); 
                        line += string.Join("|",values);
                        continue;
                    }

                    line += $"{value};";
                }
                sb.AppendLine(line);
                System.IO.File.WriteAllText(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Students.csv"), sb.ToString(), Encoding.Unicode);
            }
        }
    }
}

Al crear la clase ExportHelper me aparecía un error CS0101, estuve revisando un rato, pero el error se resolvió cerrando y volviendo a abrir el VSCode.

Espero que les sirva. 😄

Dejo mi propuesta de clase ExportHelper

using System.Collections;
using System.Text;

namespace SingleResponsability
{
    public class ExportHelper
    {
        public static void ExportCSV<T>(IEnumerable<T> items) where T : class
        {

            StringBuilder sb = new StringBuilder();
            string csvHeader = String.Join(";", typeof(T).GetProperties().Select(x => x.Name.ToString()));
            sb.AppendLine(csvHeader);

            foreach (var item in items)
            {
                string csvData = String.Join(";", typeof(T).GetProperties().Select(x => 
                {
                    object? valueProperty = x.GetValue(item);
                    if (valueProperty is null) return string.Empty;
                    else if (valueProperty is not String && valueProperty is IEnumerable valuesListProperty)
                    {
                        string line = string.Empty;
                        var valores = valuesListProperty.Cast<object>().ToList().Select(x => x);
                        line += string.Join("|", valores);
                        return line;
                    }
                    else return valueProperty;
                }));
                
                sb.AppendLine(csvData);
            }
            System.IO.File.WriteAllText(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{typeof(T).Name}.csv"), sb.ToString(), Encoding.Unicode);

        }
    }
}

Dejo mi propuesta de clase ExportHelper: ```c# using System.Collections; using System.Text; namespace SingleResponsability { public class ExportHelper<T> { public void ToCsv(IEnumerable<T> data, string name = "Students.csv") { // Create a string builder to store the file content StringBuilder sb = new StringBuilder(); typeof(T).GetProperties().ToList().ForEach(p => sb.Append(p.Name).Append(";")); sb.AppendLine(); // Iterate over the objects in the collection foreach (var item in data) { // Iterate over the properties of the object foreach (var prop in typeof(T).GetProperties()) { // Get the value of the property or an empty string if it's null object value = prop.GetValue(item)??""; // Get the type of the property Type propType = value.GetType(); // If the property is a collection, join the values with a pipe // Otherwise, just append the value if (value is IEnumerable && propType != typeof(string)) sb.Append(string.Join("|", (value as IEnumerable).Cast<object>().Select(x => x?.ToString()))).Append(";"); else sb.Append(value).Append(";"); } // Add a new line sb.AppendLine(); } // Write the file File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, name), sb.ToString(), Encoding.Unicode); } } } ```

Le pedí ayuda a Chat GPT ya que aún soy nuevo en C#:

ExportHelper.cs:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace SingleResponsability
{
    public class ExportHelper<T>
    {
        public void ExportData(IEnumerable<T> data, Func<T, string> toStringFunc)
        {
            string csv = string.Join(",", data.Select(toStringFunc).ToArray());
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("Id;Fullname;Grades");
            foreach (var item in data)
            {
                sb.AppendLine(toStringFunc(item));
            }
            File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Data.csv"), sb.ToString(), Encoding.Unicode);
        }
    }
}

Program.cs:

using SingleResponsability;

public class Program
{
    public static void Main()
    {
        StudentRepository studentRepository = new StudentRepository();
        ExportHelper<Student> exportHelper = new ExportHelper<Student>();
        exportHelper.ExportData(studentRepository.GetAll(), x => x.ToString());
        Console.WriteLine("Proceso Completado");
    }
}

Clone el repositorio y al tratar de ejecutar el proyecto con dotnet run me da el siguiente error: No se ha podido encontrar un proyecto para ejecutar. Asegúrese de que exista uno en C:\Users\distu\OneDrive\Documentos\Plazit\CursoC#\curso-principios-solid-csharp o pase la ruta de acceso al proyecto mediante --project.
using System.Text;

namespace SingleResponsability
{
    public static class ExportHelper
    {
        public static void ExportData<T>(IEnumerable<T> data, string fileName, Func<T, string> dataToString)
        {
            StringBuilder sb = new();
            sb.AppendLine("Id;FullName;Grades");
            foreach (var item in data)
            {
                sb.AppendLine(dataToString(item));
            }
            File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName), sb.ToString(), Encoding.Unicode);
        }
    }
}
namespace SingleResponsability
{
    public static class ExportStudent
    {
        public static void Export(IEnumerable<Student> students)
        {
            ExportHelper.ExportData(students, "Students.csv", student => $"{student.Id};{student.FullName};{string.Join("|", student.Grades)}");
        }
    }
}

using SingleResponsability;
StudentRepository _ = new();
ExportStudent.Export(StudentRepository.GetAll());
Console.WriteLine("Proceso Completado");

Puede que algunos al hacerle pull y ver los codigos les aparezca error en todo, esto se resuelve yendose al .csproj y en <TargetFramework> </TargetFramework> ponerle la versiond e netcore que estan usando. Despues de esto hagan dotnet build en la consola y ya esta todo listo

Buen día, tengo algunas dudas 1\. Teniendo en cuenta que el Helper se creo con el objetivo de exportar, no se deberia generar un metodo generico, que reciba una colección generica y exporte el CSV? 2\. Si se va utilizar el motodo de la clase directamente, no seria mejor simplemente dejar este tipo de metodos como static dentro del Helper para no tener que instanciarlo?
Para aquellos que les aparezca el error de: Error NETSDK1004 Assets file 'C:\Users\coura\Source\Repos\curso-principios-solid-csharp\1-SingleResponsability\obj\project.assets.json' not found. Run a NuGet package restore to generate this file. C:\Users\coura\Source\Repos\curso-principios-solid-csharp\1-SingleResponsability\SingleResponsability.csproj C:\Program Files\dotnet\sdk\8.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets 266 curre cuando el archivo `project.assets.json` necesario para tu proyecto .NET no se encuentra en la ubicación esperada. Este archivo se genera durante la restauración de paquetes NuGet y contiene información sobre todas las dependencias del proyecto. Para resolver este error, **Restaurar los Paquetes NuGet:** Abre una consola de comandos o terminal y navega hasta el directorio del proyecto que está causando el problema. Una vez allí, ejecuta el siguiente comando para restaurar los paquetes NuGet: bashCopiar código`dotnet restore` Me funciono y pude correr normalmente mi program.cs en Visual Studio
Dejo mi propuesta de la clase ExportHelper: ```c# using System.Text; using System.Collections; namespace SingleResponsability { public static class ExportHelper<T> where T : class { public static void ExportData(IEnumerable<T> data, string fileName, string delimiter = ";") { var properties = typeof(T).GetProperties(); var sb = new StringBuilder(); sb.AppendLine(string.Join(delimiter, properties.Select(p => p.Name))); foreach (var item in data) { var values = properties.Select(p => { var value = p.GetValue(item); if (value is IEnumerable enumerable && !(value is string)) { return string.Join("|", enumerable.Cast<object>().Select(obj => string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", obj))); } return value?.ToString() ?? ""; }); sb.AppendLine(string.Join(delimiter, values)); } var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); File.WriteAllText(filePath, sb.ToString(), Encoding.Unicode); } } } ```quedando la implementación en Program de la siguiente forma: ```c# using SingleResponsability; StudentRepository studentRepository = new(); ExportHelper<Student>.ExportData(studentRepository.GetAll(), "Students.csv"); Console.WriteLine("Proceso Completado"); ```using SingleResponsability; StudentRepository studentRepository = new();ExportHelper\<Student>.ExportData(studentRepository.GetAll(), "Students.csv"); Console.WriteLine("Proceso Completado");
Dejo mi propuesta de la clase ExportHelper: `using System.Text;using System.Collections;` `namespace SingleResponsability{    public static class ExportHelper<T> where T : class    {        public static void ExportData(IEnumerable<T> data, string fileName, string delimiter = ";")        {            var properties = typeof(T).GetProperties();            var sb = new StringBuilder();` `            sb.AppendLine(string.Join(delimiter, properties.Select(p => p.Name)));` `            foreach (var item in data)            {                var values = properties.Select(p =>                {                    var value = p.GetValue(item);                    if (value is IEnumerable enumerable && !(value is string))                    {                        return string.Join("|", enumerable.Cast<object>().Select(obj => string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", obj)));                     }                    return value?.ToString() ?? "";                });                sb.AppendLine(string.Join(delimiter, values));            }` `            var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);            File.WriteAllText(filePath, sb.ToString(), Encoding.Unicode);        }    }}` quedando la implementación en Program de la siguiente forma: `ExportHelper<Student>.ExportData(studentRepository.GetAll(), "Students.csv"); `
```js using System.Text; namespace SingleResponsability; public class ExportHeper<T>{ public void Export(IEnumerable<T> lists ) { var idProperty = typeof(T).GetProperty("Id"); var fullnameProperty = typeof(T).GetProperty("Fullname"); var gradesProperty = typeof(T).GetProperty("Grades"); if (idProperty == null || fullnameProperty == null || gradesProperty == null) { throw new InvalidOperationException("El tipo T no tiene las propiedades necesarias."); } System.Text.StringBuilder sb = new(); sb.AppendLine("Id;Fullname;Grades"); foreach (var item in lists) { var id = idProperty.GetValue(item); var fullname = fullnameProperty.GetValue(item); var grades = (IEnumerable<double>)gradesProperty.GetValue(item)!; sb.AppendLine($"{id};{fullname};{string.Join("|", grades)}"); } System.IO.File.WriteAllText(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Students.csv"), sb.ToString(), Encoding.Unicode); } } ```using System.Text; namespace SingleResponsability;    public class ExportHeper\<T>{         public void Export(IEnumerable\<T> lists )         {                    var idProperty = typeof(T).GetProperty("Id");            var fullnameProperty = typeof(T).GetProperty("Fullname");            var gradesProperty = typeof(T).GetProperty("Grades");             if (idProperty == null || fullnameProperty == null || gradesProperty == null)            {                throw new InvalidOperationException("El tipo T no tiene las propiedades necesarias.");            }                        System.Text.StringBuilder sb = new();                        sb.AppendLine("Id;Fullname;Grades");            foreach (var item in lists)            {                var id = idProperty.GetValue(item);                var fullname = fullnameProperty.GetValue(item);                var grades = (IEnumerable\<double>)gradesProperty.GetValue(item)!;                 sb.AppendLine($"{id};{fullname};{string.Join("|", grades)}");            }            System.IO.File.WriteAllText(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Students.csv"), sb.ToString(), Encoding.Unicode);        }     }
using System.Text; namespace SingleResponsability;    public class ExportHeper\<T>{         public void Export(IEnumerable\<T> lists )         {                    var idProperty = typeof(T).GetProperty("Id");            var fullnameProperty = typeof(T).GetProperty("Fullname");            var gradesProperty = typeof(T).GetProperty("Grades");             if (idProperty == null || fullnameProperty == null || gradesProperty == null)            {                throw new InvalidOperationException("El tipo T no tiene las propiedades necesarias.");            }                        System.Text.StringBuilder sb = new();                        sb.AppendLine("Id;Fullname;Grades");            foreach (var item in lists)            {                var id = idProperty.GetValue(item);                var fullname = fullnameProperty.GetValue(item);                var grades = (IEnumerable\<double>)gradesProperty.GetValue(item)!;                 sb.AppendLine($"{id};{fullname};{string.Join("|", grades)}");            }            System.IO.File.WriteAllText(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Students.csv"), sb.ToString(), Encoding.Unicode);        }     }
Typo Error común, siempre me pasa, es "Responsibility" en lugar de "Responsability" ------------------------------------------------------------------------------
A lo que entendí: suponiendo que en el exportHelper quiero agregar mas métodos para exportar diferentes cvs. ¿en que punto habría que dividir el archivo para no tener un exportHlper de mas de mil lineas de código de puros metodos de exportación a cvs?
Por si ha alguien le sirve dejaré mi propuesta por aquí: `public static class ExportHelper<T> where T : class` `{` ` public static void ExportCSV(IEnumerable<T> list)` ` {` ` string csv = string.Join(",", list.Select(x => x.ToString()).ToArray());` ` Type typeT = typeof(T);` ` StringBuilder sb = new StringBuilder();` ` sb.AppendLine("Id;Fullname;Grades");` ` foreach (dynamic item in list)` ` {` ` switch (typeT.Name.ToUpper())` ` {` ` case "STUDENT":` ` sb.AppendLine($"{item.Id};{item.Fullname};{string.Join("|", item.Grades)}");` ` break;` ` default:` ` break;` ` }` ` }` ` File.WriteAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Students.csv"), sb.ToString(), Encoding.Unicode);` ` }` `}`
Comparto mi código genérico: using System.Text; namespace SingleResponsability{ public class ExportHelper\<T> { public void ExportStudent(IEnumerable\<T> values) { StringBuilder sb = new StringBuilder(); var headers = string.Join(";", values.GetType().GetProperties().ToList()); sb.AppendLine(headers); foreach (var item in values) { var itemValue = string.Join(";", item); sb.AppendLine(itemValue); } System.IO.File.WriteAllText(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Report.csv"), sb.ToString(), Encoding.Unicode); } }}

Si quieren crear clases e interfaces fácilmente con el namespace correcto usando vscode, pueden descargar la extensión C# Extensions de JosKreativ.

Buen día comunidad. Comparto mi solución. Aprovecho para agradecer a quienes previo a mi aporte hicieron el suyo pues fueron de gran ayuda e iluminación para mi.

 public void ExportCSV(IEnumerable<T> persona)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            String nameHeader = "";
            var propertiesTypePerson = typeof(T).GetProperties();
            foreach (var personHeader in propertiesTypePerson){
                nameHeader += personHeader.Name + ";";
            }
            nameHeader = nameHeader.Substring(0, nameHeader.Length - 1);
            sb.AppendLine(nameHeader);

            var respCollection = "";
            List<String> values = new List<string>();
            for (int i = 0; i < persona.Count(); i++)
            {
                respCollection = "";
                foreach (var person in propertiesTypePerson)
                {
                    var datos = person.GetValue(persona.ToArray()[i]);
                    var tipo = datos.GetType();
                    
                    if (tipo.FullName.Contains("Collection"))
                    {
                        respCollection = String.Join("|", (datos as IEnumerable).Cast<object>().Select(x => x.ToString()));
                        values.Add(respCollection);
                    }
                    else {
                        values.Add(datos.ToString());
                    }
                }
                sb.AppendLine(String.Join(";",values.ToArray()));
                values.Clear();

            }
            System.IO.File.WriteAllText(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Students.csv"), sb.ToString(), Encoding.Unicode);
        }

Como dato, no es necesario usar

namespace SingleResponsability 
{
	//Codigo 
}

En lugar de eso pueden simplemente

namespace SingleResponsability; 
// Codigo

Esto para que la identacion de su codigo no crezca y sea mas facil de leer 😉

Esta es mi propuesta de la case ExportHelper:

using System.Collections;
using System.Reflection;
using System.Text;

namespace SingleResponsability
{
    public class ExportHelper<T> where T : class 
    {
        public void ExportToCSV(IEnumerable<T> items )
        {
            Type type = typeof(T);
            string classNme =  type.Name;

            string csv = String.Join(",", items.Select(x => x.ToString()).ToArray());
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            //Obten las propiedades de la clase
            PropertyInfo[] properties = type.GetProperties();
            string appentLine = string.Empty;
            foreach (PropertyInfo property in properties)
            {
                appentLine+=$"{property.Name};";
            }
            sb.AppendLine(appentLine);

            foreach (var item in items)
            {
                string valueOfProperty = string.Empty;
                foreach (PropertyInfo property in properties)
                {
                    Type propertyType = property.PropertyType;
                    if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(List<>))
                    {
                        IList list = (IList)property.GetValue(item);
                        if (list != null)
                        {
                            
                            foreach (object listItem in list)
                            {
                                valueOfProperty += $"{(listItem != null ? listItem.ToString() : "null")}|";
                            }
                            valueOfProperty += ";";
                        }
                        else
                        {
                            valueOfProperty += "null";
                        }
                    }
                    else{
                        object value = property.GetValue(item);
                        valueOfProperty += $"{(value != null ? value.ToString() : "null")};";

                    }
                }
                sb.AppendLine(valueOfProperty);
                //sb.AppendLine($"{item.Id};{item.Fullname};{string.Join("|", item.Grades)}");
            }
            System.IO.File.WriteAllText(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{classNme}.csv"), sb.ToString(), Encoding.Unicode);
        }
    }
}

Y la implementación:

using SingleResponsability;

StudentRepository studentRepository = new();
ExportHelper<Student> exportStudent = new ();
exportStudent.ExportToCSV(studentRepository.GetAll());
Console.WriteLine("Proceso Completado");

Siendo un helper convertí la función en estática para no crear una instancia

public static void ExportStudents(IEnumerable<Student> students)
        {
            functionCode
        }

Y así lo invoco en program

StudentRepository studentRepository = new();
ExportHelper.ExportStudents(studentRepository.GetAll());
Console.WriteLine("Proceso Completado");

Podría ser esto una mala práctica??

Mi propuesta:

        public void Export<T>(string header, IEnumerable<T> registers) 
        {

            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            sb.AppendLine(header);
            
            foreach (var item in registers)
            {
                if (item == null) continue;
                Type type = item.GetType();
                PropertyInfo[] props = type.GetProperties();
                string lineaCsv = "";
                foreach (var prop in props)
                {
                    lineaCsv+= prop.GetValue(item) + ";";
                }

                sb.AppendLine(lineaCsv);
            }
            System.IO.File.WriteAllText(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Registers.csv"), sb.ToString(), Encoding.UTF8);
        }