web-dev-qa-db-fra.com

Itérer dans les propriétés et les valeurs d'un objet renvoyé via une requête linq sur un modèle de domaine

J'ai une entité personnalisée dans une base de données relationnelle que j'ai mappée sur le CLR via un modèle de domaine. Donc, en utilisant l'instruction suivante, je peux extraire une entité de ma base de données en mémoire via une requête LINQ sur le modèle de domaine, comme ceci;

var inspection = (from i in dbContext.New_testinspectionExtensionBases
              where i.New_testinspectionId == currentInspection   
              select i).First();                         

Il y a des propriétés/champs sur cette entité auxquels j'ai besoin d'accéder, je dois pouvoir déterminer le nom de la propriété/champ ainsi que sa valeur. Je souhaite parcourir ces éléments en mémoire et écrire leurs noms et leurs valeurs sur la console.

J'ai essayé d'utiliser cette approche, mais je ne savais pas comment corriger la syntaxe (je ne suis pas sûr que GetProperties est la bonne méthode à utiliser, GetFields ne retournait rien pour une raison quelconque, alors j'ai supposé que c'était la voie à suivre) mais cela n'a pas vraiment d'importance puisque tout ce dont j'ai besoin est un accès en lecture à la valeur;

var inspectionReportFields = inspection.GetType().GetProperties(); 
// I called this inspectionReportfields because the entity properties correspond to 
// form/report fields I'm generating from this data.

foreach (var reportField in inspectionReportFields)
{
    var value = reportField.GetValue();
    Console.WriteLine(reportField.Name);
    Console.WriteLine(value);
}

Existe-t-il un moyen plus simple d'obtenir la valeur de la propriété/du champ lors de l'utilisation d'un modèle de domaine tel que EF ou openaccess? Si non, est-ce que j'y vais de la bonne façon? Et enfin, si oui, comment puis-je corriger la syntaxe dans la déclaration de variable de valeur?

Voici quelques exemples de champs/propriétés du code généré par le modèle de domaine, à titre de référence;

    private int? _new_systemGauges;
    public virtual int? New_systemGauges 
    { 
        get
        {
            return this._new_systemGauges;
        }
        set
        {
            this._new_systemGauges = value;
        }
    }

    private int? _new_systemAlarm ;
    public virtual int? New_systemAlarm 
    { 
        get
        {
            return this._new_systemAlarm;
        }
        set
        {
            this._new_systemAlarm = value;
        }
    }
37
bdemartino

Je suppose que vous essayez de définir un moyen générique de "vider" un objet sans rien connaître de sa structure. Si c'est le cas, vous agissez de la bonne façon. Vous utilisez la réflexion (GetType() et les méthodes de classe Type associées) pour inspecter l'objet et renvoyer ses informations.

La raison pour laquelle GetFields() n'a rien retourné est que vous n'avez probablement pas fourni les indicateurs de liaison appropriés. En particulier, si vous appelez la surcharge qui ne prend aucun paramètre, vous ne récupérez que les champs public; si vous voulez des champs privés, vous devez les demander spécifiquement.

Dans votre cas, GetFields(BindingFlags.NonPublic) vous rendrait les champs _new_systemGauges et _new_systemAlarm, tandis que GetProperties () vous renverrait les propriétés New_systemAlarm et New_systemAlarm.

L’autre élément clé que vous avez oublié est que les données que vous récupérez sont les métadonnées type ; il définit la structure de la variable class et non une instance particulière. Si vous voulez savoir quelle est la valeur d'une propriété pour une instance spécifique, vous devez demander cela:

foreach (var prop in obj.GetType().GetProperties())
{
  Console.WriteLine("{0} = {1}", prop.Name, prop.GetValue(obj, null));
}

Si vous avez l'un des éléments PropertyInfo parmi les métadonnées du type, vous pouvez demander la valeur de cette propriété sur n'importe quelle instance de ce type. Il n'est pas nécessaire que ce soit la même instance que celle que vous avez utilisée à l'origine. Par exemple:

var objs = somelist.Where(x => x.Id == 1);
foreach (var prop in objs.First().GetType().GetProperties())
{
  int x = 0;
  foreach (var obj in objs)
  {        
    if (prop.PropertyType.Name.Equals("Int32"))
    {
      int val = (int)prop.GetValue(obj, null);
      Console.WriteLine("Obj #{0}: {1} = 0x{2:x8}", x++, prop.Name, val);
    }
    else if (prop.PropertyType.Name.Equals("Decimal"))
    {
      int val = (decimal)prop.GetValue(obj, null);
      Console.WriteLine("Obj #{0}: {1} = {2:c2}", x++, prop.Name, val);
    }
    else
    {
      Console.WriteLine("Obj #{0}: {1} = '{2}'", x++, prop.Name, prop.GetValue(obj, null));
    }
  }
}

Techniquement, vous devriez vérifier le résultat de GetIndexParameters pour voir si une propriété est indexée ou non; le paramètre null à GetValue est en réalité un tableau de valeurs d'index.

Pour convertir la valeur que vous récupérez, vous pouvez utiliser des transmissions de type ou si vous voulez être un peu plus flexible, utilisez les méthodes de la classe Convert. La différence est, par exemple, si vous avez une propriété short, GetValue() renverra une boîte courte, que vous ne pouvez pas alors transtyper en tant que int; vous devez d'abord le décompresser dans une short. Utiliser Convert.ToInt32() effectuera toutes les étapes nécessaires pour obtenir une valeur int de toute propriété qui est convertible en un entier.

La conversion entre les types de référence est plus facile car vous pouvez simplement utiliser is et as pour cela; ceux qui fonctionnent exactement comme vous le souhaiteriez avec des valeurs de propriété "reflétées".

73
Michael Edenfield

GetProperties est en effet la bonne méthode.

Pour vous débarrasser de l'erreur du compilateur, changez votre code en ceci:

var value = reportField.GetValue(inspection, null);

Vous devez transmettre l'instance à partir de laquelle vous souhaitez obtenir la valeur, car un objet PropertyInfo n'est pas lié à une instance de classe spécifique.


Veuillez envisager de suivre les règles de nommage standard .NET.

Cela conduirait à ce qui suit:

NewSystemAlarm au lieu de New_systemAlarm
newSystemAlarm ou _newSystemAlarm au lieu de _new_systemAlarm
NewTestInspectionExtensionBases au lieu de New_testinspectionExtensionBases
NewTestInspectionId au lieu de New_testinspectionId

2
Daniel Hilgarth

Si vous utilisez OpenAccess, vous disposez toujours des informations complètes sur vos classes de modèles. Les informations qui y figurent sont extraites de votre mappage, ce qui signifie que vous n’avez pas besoin de réfléchir sur vos classes (pas de surcharge).

Parcourez simplement context.Metadata.PersistentTypes pour toutes les informations de mappage de vos classes. 

0
sovanesyan