0:00 0 0
Agregar propiedades a clases csharp dinámicamente en tiempo de ejecución

Agregar propiedades a clases csharp dinámicamente en tiempo de ejecución

  DrUalcman |  abril 142021

El problema que nos ocupa

Se necesita poder modificar las propiedades de un objeto en tiempo de ejecución ya que necesitamos agregar una o varias nuevas propiedades.

El secreto ExpandoObject

Primero aclarar que no he encontrado como poder agregar directamente la propiedad a nuetro objecto clase, pero utilizando dynamic, un objeto clase especial con el que contamos, y System.Dinamic.ExpandoObject podemos intentar encontrar la solución al problema.

Utilizando ExpandoObject podemos agregar propiedades, métodos y eventos en tiempo de ejecución, por lo que si tenemos otro objeto con las propiedades, podriamos copiar su escrutctura dentro del nuevo objeto dinámico, y agregar aquellas nuevas que necesitemos.

dynamic expando = new ExpandoObject();
expando.Name = "DrUalcman";

Utilizando un poco de Reflection

Como ejemplo vamos a exponer el caso por el cual necesite de hacer la modificación de las propiedades dinámicamente. Desde una SPA Blazor WebAsembly, el cual está programado en C# pero se ejecuta del lado del cliente.

Tenemos un modelo para manejar una base de datos, el cual se va a serializar a Json para enviar a una aplicación web cliente y manejarlo mediante JAVASCRIPT. Si por algún motivo no hay comunicación con la API para almacenar en la base de datos, el modelo debe de agregar una propiedad OffLine de forma dinámica para saber qué registro es el que no se ha podido guardar en la base de datos. De esta forma podremos saber que datos son los que se necesita enviar a la base de datos cuando la aplicación tenta conexión de nuevo.

Desde C# serializamos a Json el objeto, se guarda en alguno de los recursos disponibles en el navegador de forma permanente, localStorage o IndexDB (en mi caso utilizo IndexDB). Por lo que si no hay conexión con la API para guardar en la base de datos llamaremos al método AddOffline(T toCopy).

        public static dynamic AddOffline(T toCopy)
{
dynamic expando = new ExpandoObject();
// ExpandoObject supports IDictionary so we can extend it like this
Type t = toCopy.GetType();

// ExpandoObject supports IDictionary so we can extend it like this
IDictionary expandoDict = expando as IDictionary;

//read all properties
PropertyInfo[] properties = t.GetProperties(BindingFlags.Public | //get public names
BindingFlags.Instance); //get instance names

foreach (var item in properties)
{
expandoDict.Add(item.Name, item.GetValue(toCopy));
}
expando.OffLine = true;
return expando;
}

Para poder copiar el objeto enviado haremos uso de System.Reflection para recorrer todas las propiedades y agregarla a nuetro objeto dynamic y devolver la nueva copia. En mi caso necesito modificar una lista de objetos, por lo que he hecho esta sobrecarga.

        public static List AddOffline(List toCopy)
{
List result = new List();
foreach (T father in toCopy)
{
result.Add(AddOffline(father));
}
return result;
}

De esta manera modifico el objeto que se va a serializar a Json y guardarlo correctamente en el navegador web.

Manejo de métodos y eventos

Con ExpandoObject también podemos agregar métodos usandl el Func que representa una llamada a un método. Un ejemplo para agregar un método podría ser algo como:

// Add method to expando
expando.IsValid = (Func)(() =>
{
// Check that they supplied a name
if(string.IsNullOrWhiteSpace(expando.Name))
return false;
return true;
});

Para definir eventos podemos usar Action. Un ejemplo utiliando un método anómino puede ser:

// You can also add event handlers to expando objects
var eventHandler =
new Action((sender, eventArgs) =>
{
dynamic exp = sender as ExpandoObject;
var nameArgs = eventArgs as NameChangedEventArgs;
Console.WriteLine($"Setting Name to : {nameArgs?.Name}");
exp.Name = nameArgs?.Name;
});

IDictionary expandoDict = expando as IDictionary;
// Add a LanguageChanged event and predefined event handler
expandoDict.Add("NameChanged", eventHandler);

Finalmente, ExpandoObject admite INotifyPropertyChanged el cuál es la base del enlace de datos de las propiedas en .NET. Conectamos el controlador de eventos cuando Name se cambia y disparamos el evento NameChanged.

((INotifyPropertyChanged)expando).PropertyChanged += 
new PropertyChangedEventHandler((sender, ea) =>
{
dynamic exp = sender as dynamic;
var pcea = ea as PropertyChangedEventArgs;
if(pcea?.PropertyName == "Name")
exp.NameChanged(exp, new NameChangedEventArgs() { Country = exp.Name });
});

Conclusión

ExpandoObject le permite escribir código más legible que utilizando Relection con la sintaxis GetProperty("field"). Puede ser útil, como en el caso que os he planteado, cuando se traba de manejo de datos con XML o JSON para configurar rápidamente un tipo en lugar de tener que crear siempre objetos de transferencia de datos.

La capacidad de admitir el enlace a datos INotifyPropertyChanged es una gran ventaja para cualquiera que utilice WPF, MVC, Blazor o cualquier otro marco de enlace en .NET, que que te permite utilizar estos "objetos en tiempo de ejecución", así como clases tipadas estáticamente.

Pueden ver un ejemplo de uso en el GitHub de mi projecto BlasorIndexDb para el manejo que la base de datos integrada en el navegador desde Blazor.

Este documento es una traduction y adaptación de los autores Jay Hilyard y Stephen Teilhet que a su vez es un extracto del contenido del libreo C# 6.0 Cookbook
This document is a translation and adaptation of the authors Jay Hilyard and Stephen Teilhet which in turn is an extract from the content of the C# 6.0 Cookbook

Happy coding.

#CSHARP #blazor #JSON #MVC


0 Comentarios

 
 
 

Archivo