c # error when traversing objects dynamically

3

Good morning everyone.

I have a webapi project assembled with the repository pattern and I need to make a modification to the answer that the _repository.getByFilter(...) method offers me. This returns a IQueryable and I need to go through all the objects in this list checking if any of the properties has a custom attribute ( EncryptedData ). If so, encrypt the value.

The problem is that these objects can have properties that are ICollection or other objects of own classes.

To try to solve this, I have mounted these three functions:

private IQueryable<T> EncryptDecrypt(IQueryable<T> queryable, bool encrypt)
{
    var encriptedProperty = typeof(T).GetProperties().Where(
        p => p.CustomAttributes.Any(at => at.AttributeType == typeof(Konecta.Core.Attributes.EncryptedData))
    );
    var collectionProperty = typeof(T).GetProperties().Where(p => p.PropertyType.Name.Contains("ICollection"));

    if (encriptedProperty.Count() != 0 || collectionProperty.Count() != 0)
    {
        foreach (T obj in queryable)
        {
            if (encriptedProperty.Count() > 0)
            {
                foreach (var ep in encriptedProperty)
                {
                    ep.SetValue(obj, Convert.ChangeType(DecryptDB(ep.GetValue(obj, null).ToString()), ep.PropertyType), null);
                }
            }
            if (collectionProperty.Count() > 0)
            {
                foreach (var cp in collectionProperty)
                {
                    EncryptDecrypt((ICollection<cp.PropertyType>)cp.GetValue(obj, null), true);
                }
            }
        }
    }

    return queryable;
}

private ICollection<T> EncryptDecrypt(ICollection<T> collection, bool encrypt)
{
    var encriptedProperty = typeof(T).GetProperties().Where(
        p => p.CustomAttributes.Any(at => at.AttributeType == typeof(Konecta.Core.Attributes.EncryptedData))
    );
    var collectionProperty = typeof(T).GetProperties().Where(p => p.PropertyType.Name.Contains("ICollection"));

    if (encriptedProperty.Count() == 0 && collectionProperty.Count() == 0)
    {
        foreach (T obj in collection)
        {
            if (encriptedProperty.Count() > 0)
            {
                foreach (var ep in encriptedProperty)
                {
                    ep.SetValue(obj, Convert.ChangeType(DecryptDB(ep.GetValue(obj, null).ToString()), ep.PropertyType), null);
                }
            }
            if (collectionProperty.Count() > 0)
            {
                foreach (var cp in collectionProperty)
                {
                    EncryptDecrypt((ICollection<cp.PropertyType>)cp.GetValue(obj, null), true);
                }
            }
        }
    }

    return collection;
}

private T EncryptDecrypt(T entity, bool encrypt)
{
    return entity;
}

In my business logic layer I have the following line:

EncryptDecrypt(_repository.SelectByFilter(where), true);

So I get that, the result of SelectByFilter between the first of my functions (still halfway), I check if any property has my custom attribute and, if so, the encrypted.

Then I check if any property is a collection, and send the value to the second function.

The problem is that the line

EncryptDecrypt((ICollection<cp.PropertyType>)cp.GetValue(obj, null), true); 

of the first function gives me an error:

  

can not convert ICollection<cp.PropertyType> to IQueryable<T> .

Can someone tell me what I'm doing wrong?

I would also like to know if someone can tell me some more efficient way to do all this so as not to need so many functions.

Greetings and thanks.

    
asked by Alejandro 06.09.2017 в 16:06
source

1 answer

4

The conversion error occurs because IQueryable has not yet executed the query, while ICollection works with objects that you already have, that is, they are already in memory. Execute the ToList() method on the IQueryable object to be able to cast successfully.

And as soon as the recommendation is that you do not use the attributes as a way of encryption because reflection is very slow. Try to create an interface that will be responsible for marking an object that can be encrypted:

public interface IObjetoEncriptable
{
   void Encriptar(Encriptador encriptador);
   void Desencriptar(Encriptador encriptador);
}

Then you implement the interface in each object that supports the encryption and define the encryption / decryption logic:

public class Paciente : IObjetoEncriptable
{ 
  public string Nombre {get;  set;}
  public string Direccion {get;set;}

  public void Encriptar(Encriptador e)
  {
    this.Nombre = e.Encriptar(this.Nombre);
    this.Direccion = e.Encriptar(this.Direccion);
  }

  publi void Desencriptador(Encriptador e)
  {
    this.Nombre = e.Desencriptar(this.Nombre);
    this.Direccion = e.Desencriptar(this.Direccion); 
  }
}

public class RecordMedico : IObjetoEncriptable
{
   public string TipoSange { get; set;}
   public string Telefono { get; set;}
   public Paciente Paciente { get; set;}

   public void Encriptar(Encriptador e)
   {
      this.TipoSangre = e.Encriptar(this.TipoSangre); 
     this.Telefono = e.Encriptar(this.Telefono); 
   }

   public void Encriptar(Encriptador e)
   {
      this.TipoSangre = e.Desencriptar(this.TipoSangre); 
      this.Telefono = e.Desencriptar(this.Telefono); 

      // encriptamos los objetos que requieren encriptacion/desencriptacion
      this.Paciente.Encriptar(e);
   }
}

So now when in the repository to decrypt and encrypt you should only send the instance of the encryptor for each encryptable instance:

public IEnumerable<RecordMedico> ObtenerRecordMedicoPacientes()
{
    var record = obtenerRecords();
    Encriptador encriptador = new Encriptador();
    foreach(RecordMedico record : record)
    {
      record.Desencriptar(encriptador);
      yield return record;
    }

}

public void GuardarRecordMedico(RecordMedico record)
{
   Encriptador encriptador = new Encriptador();
   record.Encriptar(encriptador);
   //...
}

So you do not have to look for which property is encrypted.

    
answered by 06.09.2017 / 16:12
source