Que tal ! llevo mucho tiempo desde la ultima vez que ingrese aquí y compartí algo, con el tema de que ocurrieron muchos cambios en estos últimos meses y demás, se hizo dificil.
Les dejo un Mapper que desarrolle hace unos días atrás que es una alternativa a el Famoso "AutoMapper" que a mi recomendación es preferible que no se le use, ya que es demaciado lento.
Este codigo que adjuntare junto con su ejemplo de uso, pasara un object con sus navigations properties así mismo sean listas, a DTO o a ViewModel.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
public class MapperConfiguration
{
public string Name { get; set; }
public IList<string> AssemblyList { get; set; }
public List<Assembly> Classes { get; set; }
}
public static class Mapper
{
private static MapperConfiguration _mapperConfiguration = new MapperConfiguration { Name = "DTO" };
public static void ConfigureMapper(MapperConfiguration mapperConfiguration)
{
//_mapperConfiguration.Name = mapperConfiguration.Name;
_mapperConfiguration = mapperConfiguration;
if ( mapperConfiguration.AssemblyList != null)
{
foreach(var item in mapperConfiguration.AssemblyList)
{
var assemblyTempList = Assembly.ReflectionOnlyLoad(item);
_mapperConfiguration.Classes.Add(assemblyTempList);
}
}
}
#region Mapper con configuracion
public static U MapProperty<T,U>(this T source, IDictionary<string,string> configurationMapper)
where T : class, new()
where U : class, new()
{
if ( source == null || configurationMapper == null)
{
return default(U);
}
/*Obtenemos las properties del object source*/
var sourceProperties = source.GetType().GetProperties();
var destinyInstance = Activator.CreateInstance(typeof(U));
var destinyProperties = destinyInstance.GetType().GetProperties();
foreach(var i in sourceProperties)
{
//Buscamos si la property que quiere copiar tiene source
var firstFind = configurationMapper.FirstOrDefault(m => m.Key.Contains(i.Name));
if (string.IsNullOrEmpty(firstFind.Key) || string.IsNullOrEmpty(firstFind.Value))
continue;
var secondFind = destinyProperties.FirstOrDefault(m => m.Name.Contains(firstFind.Value));
if ( secondFind == null)
{
continue;
}
if ( i.Name.Contains("Collection"))
{
}
secondFind.SetValue(destinyInstance, i.GetValue(source));
}
return (U)destinyInstance;
}
#endregion
#region Formula 1
/*Metodo para mapear simples objects*/
public static dynamic Map<T>(this T source)
where T : class, new()
{
//Check for source
if (source == null )
return null;
//Donde almacenaremos el tipo de destino
Type destinyType = null;
//Obtenemos todas las clases
var classList = Assembly.GetExecutingAssembly().GetTypes();
//Obtenemos las properties del source
var propertiesSource = source.GetType().GetProperties();
//Si no podemos obtener clases o properties
if ( propertiesSource == null || classList == null )
{
return new Exception("Error al obtener clases o properties desde el assembly");
}
if (source.GetType().ToString().Contains("Collection"))
{
if (source.GetType().Name.Contains("ViewModel") || source.GetType().Name.Contains("DTO") || source.GetType().Name.Contains(_mapperConfiguration.Name))
{
var firstTest = Regex.Split(propertiesSource.FirstOrDefault().Name, "ViewModel")[0];
if (string.IsNullOrEmpty(firstTest))
{
firstTest = "DTO";
}
else
{
firstTest = "ViewModel";
}
destinyType = classList.FirstOrDefault(
m => m.Name.Contains(Regex.Split(propertiesSource.FirstOrDefault().Name,firstTest)[0] + _mapperConfiguration.Name));
}
else
{
destinyType = classList.FirstOrDefault(
m => m.Name.Contains(propertiesSource.FirstOrDefault().Name + _mapperConfiguration.Name));
}
}
else
{
if (source.GetType().Name.Contains("ViewModel") || source.GetType().Name.Contains("DTO") || source.GetType().Name.Contains(_mapperConfiguration.Name))
{
var firstTest = Regex.Split(source.GetType().Name, "ViewModel")[0];
if (string.IsNullOrEmpty(firstTest))
{
firstTest = "DTO";
} else
{
firstTest = "ViewModel";
}
destinyType = classList.FirstOrDefault(
m => m.Name.Contains(Regex.Split(source.GetType().Name,firstTest)[0] + _mapperConfiguration.Name));
}
else
{
destinyType = classList.FirstOrDefault(
m => m.Name.Contains(source.GetType().Name + _mapperConfiguration.Name));
}
}
try
{
//Creamos la instancia del object
var instance = Activator.CreateInstance(destinyType);
//Obtenemos las properties del object de destino
var propertyOfDestiny = instance.GetType().GetProperties();
foreach (var item in propertyOfDestiny)
{
//Buscamos la property
var result = propertiesSource.FirstOrDefault(m => m.Name == item.Name);
if (result != null)
{
//En caso de que no sea un Object de tipo distinto a los nativos de C#
if ((!(result).PropertyType.Name.Contains("String")) && (!(result).PropertyType.Name.Contains("Int"))
&& (!(result).PropertyType.Name.Contains("Bool")) && (!(result).PropertyType.Name.Contains("Float"))
&& (!(result).PropertyType.Name.Contains("Double")))
{
//En caso de ser lista
if (result.ToString().Contains("Collection"))
{
Type type = null;
if (result.PropertyType.GetGenericArguments().Single().Name.Contains("ViewModel") || result.PropertyType.GetGenericArguments().Single().Name.Contains("DTO") ||
result.PropertyType.GetGenericArguments().Single().Name.Contains(_mapperConfiguration.Name))
{
var firstTest = Regex.Split(result.PropertyType.GetGenericArguments().Single().Name, "ViewModel")[0];
if (string.IsNullOrEmpty(firstTest))
{
firstTest = "DTO";
} else
{
firstTest = "ViewModel";
}
type = classList.FirstOrDefault(m => m.FullName.Contains(Regex.Split(result.PropertyType.GetGenericArguments().Single().Name,firstTest)[0] + _mapperConfiguration.Name));
}
else
{
type = classList.FirstOrDefault(m => m.FullName.Contains(result.PropertyType.GetGenericArguments().Single().Name + _mapperConfiguration.Name));
}
if (type == null)
{
continue;
}
var listInstance = (IList)typeof(List<>).MakeGenericType(type).GetConstructor(Type.EmptyTypes).Invoke(null);
item.SetValue(instance, ((ICollection)result.GetValue(source)).MapList());
}
else
{
Type type = null;
if (result.Name.Contains("ViewModel") || result.Name.Contains("DTO") ||
result.Name.Contains(_mapperConfiguration.Name))
{
var firstTest = Regex.Split(result.Name, "ViewModel")[0];
if (string.IsNullOrEmpty(firstTest))
{
firstTest = "DTO";
}
else
{
firstTest = "ViewModel";
}
//En caso de ser un object
type = classList.FirstOrDefault(m => m.FullName.Contains(Regex.Split(result.Name,firstTest)[0] + _mapperConfiguration.Name));
}
else
{
//En caso de ser un object
type = classList.FirstOrDefault(m => m.FullName.Contains(result.Name + _mapperConfiguration.Name));
}
if (type == null)
{
continue;
}
item.SetValue(instance, result.GetValue(source).Map());
}
}
else
{
//Property normal
item.SetValue(instance, result.GetValue(source));
}
}
}
return instance;
}
catch (Exception e)
{
return e.Message;
}
}
/*Metodos para mapear listas*/
public static dynamic MapList<T>(this T source)
where T : ICollection
{
//Check for source
if (source == null)
return null;
if (source.GetType().GetGenericArguments().Count() == 0)
return null;
Type destinyType = null;
bool isCollection = false;
if (source.GetType().ToString().Contains("Collection"))
{
foreach(var i in source)
{
if ( i.GetType().Name.Contains("ViewModel") || i.GetType().Name.Contains("DTO") || i.GetType().Name.Contains(_mapperConfiguration.Name))
{
var firstTest = Regex.Split(i.GetType().Name, "ViewModel")[0];
if (string.IsNullOrEmpty(firstTest))
{
firstTest = Regex.Split(i.GetType().Name, "DTO")[0];
}
destinyType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(
m => m.Name.Contains(firstTest + _mapperConfiguration.Name));
}
else
{
destinyType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(
m => m.Name.Contains(i.GetType().Name + _mapperConfiguration.Name));
}
isCollection = true;
break;
}
}
if (!isCollection)
return new Exception("Este object no es una lista de tipo coleccion");
var listType = source.GetType().GetGenericArguments().Single();
//Check if the type of object is or not a collection list.
if (source.GetType().ToString().Contains("Collection"))
{
try
{
dynamic list;
//Create instance of list
if ( isCollection)
{
list = (IList)typeof(List<>).MakeGenericType(destinyType).GetConstructor(Type.EmptyTypes).Invoke(null);
} else
{
list = Activator.CreateInstance(destinyType);
}
//Loop in the list
foreach (var i in source)
{
if (i.GetType().ToString().Contains("Collection"))
{
var mapped = ((ICollection)i).MapList();
foreach (var item in mapped)
{
((IList<object>)list).Add(item);
}
}
else
{
((IList)list).Add(i.Map());
}
}
return list;
}
catch (Exception e)
{
return e.Message;
}
}
return null;
}
#endregion
#region Formula 2
/*Metodo para mapear simples objects*/
public static dynamic MapToEntity<T>(this T source)
where T : class, new()
{
//Check for source
if (source == null)
return null;
Type destinyType = null;
//Obtenemos todas las clases
var classList = Assembly.GetExecutingAssembly().GetTypes();
//Obtenemos las properties del source
var propertiesSource = source.GetType().GetProperties();
//Si no podemos obtener clases o properties
if ( propertiesSource == null || classList == null )
{
return new Exception("Error al obtener clases o properties desde el assembly");
}
if (source.GetType().ToString().Contains("Collection"))
{
destinyType = classList.FirstOrDefault(
m => m.Name.Contains(Regex.Split(propertiesSource.FirstOrDefault().Name,_mapperConfiguration.Name)[0]));
}
else
{
destinyType = classList.FirstOrDefault(
m => m.Name.Contains(Regex.Split(source.GetType().Name,_mapperConfiguration.Name)[0]));
}
try
{
//Creamos la instancia del object
var instance = Activator.CreateInstance(destinyType);
//Obtenemos las properties del object de destino
var propertyOfDestiny = instance.GetType().GetProperties();
foreach (var item in propertyOfDestiny)
{
//Buscamos la property
var result = propertiesSource.FirstOrDefault(m => m.Name == item.Name);
if (result != null)
{
//En caso de que no sea un Object de tipo distinto a los nativos de C#
if ((!(result).PropertyType.Name.Contains("String")) && (!(result).PropertyType.Name.Contains("Int"))
&& (!(result).PropertyType.Name.Contains("Bool")) && (!(result).PropertyType.Name.Contains("Float"))
&& (!(result).PropertyType.Name.Contains("Double")))
{
//En caso de ser lista
if (result.ToString().Contains("Collection"))
{
var type = classList.FirstOrDefault(m => m.FullName.Contains(Regex.Split(result.PropertyType.GetGenericArguments()
.Single().Name,_mapperConfiguration.Name)[0]));
if (type == null)
{
continue;
}
var listInstance = (IList)typeof(List<>).MakeGenericType(type).GetConstructor(Type.EmptyTypes).Invoke(null);
item.SetValue(instance, ((ICollection)result.GetValue(source)).MapToEntityList());
}
else
{
//En caso de ser un object
var type = classList.FirstOrDefault(m => m.FullName.Contains(Regex.Split(result.Name,_mapperConfiguration.Name)[0]));
if (type == null)
{
continue;
}
item.SetValue(instance, result.GetValue(source).MapToEntity());
}
}
else
{
//Property normal
item.SetValue(instance, result.GetValue(source));
}
}
}
return instance;
}
catch (Exception e)
{
return e.Message;
}
}
/*Metodos para mapear listas*/
public static dynamic MapToEntityList<T>(this T source)
where T : ICollection
{
//Check for source
if (source == null)
return null;
if (source.GetType().GetGenericArguments().Count() == 0)
return null;
Type destinyType = null;
bool isCollection = false;
if (source.GetType().ToString().Contains("Collection"))
{
foreach (var i in source)
{
destinyType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(
m => m.Name.Contains(Regex.Split(i.GetType().Name,_mapperConfiguration.Name)[0]));
isCollection = true;
break;
}
}
if (!isCollection)
return new Exception("Este object no es una lista de tipo coleccion");
var listType = source.GetType().GetGenericArguments().Single();
//Check if the type of object is or not a collection list.
if (source.GetType().ToString().Contains("Collection"))
{
try
{
dynamic list;
//Create instance of list
if (isCollection)
{
list = (IList)typeof(List<>).MakeGenericType(destinyType).GetConstructor(Type.EmptyTypes).Invoke(null);
}
else
{
list = Activator.CreateInstance(destinyType);
}
//Loop in the list
foreach (var i in source)
{
if (i.GetType().ToString().Contains("Collection"))
{
var mapped = ((ICollection)i).MapToEntityList();
foreach (var item in mapped)
{
((IList<object>)list).Add(item);
}
}
else
{
((IList)list).Add(i.MapToEntity());
}
}
return list;
}
catch (Exception e)
{
return e.Message;
}
}
return null;
}
#endregion
}
}
Ejemplo de uso
using ConsoleApplication1.DTO;
using ConsoleApplication1.Entidades;
using System;
using System.Collections.Generic;
public class Uno
{
public string Nombre { get; set; }
public string Apellido { get; set; }
public void Saludar(string message, string another)
{
Console.WriteLine(message + "\n" + another);
return;
}
}
public class Dos
{
public string Name { get; set; }
public string SurName { get; set; }
public void Saludar(string message)
{
Console.WriteLine(message);
return;
}
}
namespace ConsoleApplication1
{
public static class RemoteCall
{
/*
* Assembly assembly = Assembly.LoadFile("...Assembly1.dll");
* Type type = assembly.GetType("TestAssembly.Main");
*/
public static T Instance<T>()
where T : class, new()
{
return (T)Activator.CreateInstance(typeof(T));
}
public static T Invoke<T>(string methodName, object[] parameters)
where T : class, new()
{
var classSource = Instance<T>();
var methods = classSource.GetType().GetMethods();
foreach(var i in methods)
{
if ( i.Name == methodName)
{
i.Invoke(classSource, parameters);
}
}
return default(T);
}
}
class Program
{
static void Main(string[] args)
{
var objeto = new Usuario
{
Id = 1,
Nombre = "Nicolas",
Perfil = new List<Perfil>
{
new Perfil
{
Tipo = "Normal",
Entrega = new Entrega
{
Proveedor = "Nicolas"
}
},
new Perfil
{
Tipo = "Punchi",
Entrega = new Entrega
{
Proveedor = "Jebus"
}
}
}
};
//var instanciado = RemoteCall.Instance<Uno>();
RemoteCall.Invoke<Dos>("Saludar", new object[] { "hola amigos" } );
var source = new Uno { Nombre = "Nicolas", Apellido = "Buzzi" };
var dic = new Dictionary<string, string>();
dic.Add("Nombre", "Name");
dic.Add("Apellido", "SurName");
var mapeo = source.MapProperty<Uno,Dos>(dic);
var listOfObjects = new List<Usuario>();
listOfObjects.Add(objeto);
listOfObjects.Add(objeto);
//Inicializamos el mapper con nuestra configuracion para adaptarla a viewModel
Mapper.ConfigureMapper(new MapperConfiguration { Name = "ViewModel" });
/*Convetimos Entidad a DTO*/
List<UsuarioViewModel> resultadosViewModel = listOfObjects.MapList();
/*Convertimos los ViewModel a Entidades*/
List<Usuario> entidadesDeViewModel = resultadosViewModel.MapToEntityList();
//Inicializamos el mapper con nuestra configuracion para adaptarla a DTOS
Mapper.ConfigureMapper(new MapperConfiguration { Name = "DTO" });
//Convertimos el resultado de ViewModels a DTO
List<UsuarioDTO> resultadosDTO = resultadosViewModel.MapList();
//Convetimos los DTO a entidad
var resultEntity = resultadosDTO.MapToEntityList();
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Todo correcto");
Console.ReadLine();
}
}
}
Les dejare el link por si quieren descargarse el proyecto completo.
https://github.com/xxneeco83xx/Mapper (https://github.com/xxneeco83xx/Mapper)
Espero que les sea de ayuda, y cualquier duda, sugerencia o comentario, estoy a la espera.
Saludos.