8 messaggi dal 01 maggio 2016
Ciao a tutti, ho un problema che non sò come risolvere.
Devo leggere un file ini che mi serve per inizializzare le proprietà di classi statiche.
Il nome di ogni sezione corrisponde al nome della classe statica ed i valori in essa contenuti devo andarli ad inserire nella variabile corrispondente della classe statica.
Faccio un esempio che è meglio

[Porta]
materiale='legno'
peso=12
colore='nera'

[database]
path='C:\prova.db'
userid='root'
psw=123456


e così via... posso avere molte sezione che vanno a configurare classi diverse.
Se dovessi incontrare una sezione che non ha una classe statica corrispondente devo ignorare la sezione ed andare alla successiva e lo stesso vale anche per le proprietà all'interno delle sezioni.


Il mio problema è: quando leggo il nome della sezione come faccio a selezionare la classe statica e quindi la proprietà? Cioè se per esempio sono alla sezione [database] e leggo il contenuto di path come faccio a scrivere nel codice

codice:

database.path='C:\prova.db';



???


Esiste qualche pattern di programmazione che fa al caso mio?


vi ringrazio in anticipo
Modificato da atoxx il 01 maggio 2016 21.00 -
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao e benvenuto nel forum!
Puoi avvalerti di questi due strumenti:
  • Il pacchetto NuGet ini-parser, che ti eviterà di dover scrivere la logica di parsing dei file .ini a mano. Installalo lanciando il comando Install-Package ini-parser dalla console di gestione pacchetti di Visual Studio;
  • La reflection che ti permetterà di interagire con le classi e i loro membri dinamicamente, semplicemente conoscendone il nome a runtime.


Ecco un esempio:
//Il testo tu dovrai leggerlo da file, io qui lo assegno staticamente.
//Ho rimosso gli apici, non sono richiesti (anzi, danno fastidio)
var testo = @"
[Porta]
materiale=legno
peso=12
colore=nera

[database]
path=C:\prova.db
userid=root
psw=123456
";

//Crea un'istanza del parser
var parser = new IniParser.Parser.IniDataParser();
var dati = parser.Parse(testo);

//Qui personalizza il nome del namespace e dell'assembly in cui le classi si trovano
string assemblyName = "TuaApplicazione";
string namespaceName = "TuaApplicazione.Configurazione";

//Passo in rassegna le sezioni del file ini
foreach (var sezione in dati.Sections)
{
    //Compongo il fully qualified name della classe (cioè namespace.nomeclasse, nomeassembly)
    string fullName = string.Format("{0}.{1}, {2}", namespaceName, sezione.SectionName, assemblyName);
    Type tipo = Type.GetType(fullName, throwOnError: false, ignoreCase: true);

    //Se il tipo è null, vuol dire che la classe non è stata trovata
    //Allora la ignoro e passo alla sezione successiva
    if (tipo == null)
        continue;

    //Se esisteva, ottengo l'elenco delle proprietà statiche di questa classe
    var elencoProprietà = tipo.GetProperties(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);

    //Passo in rassegna le chiavi di questa sezione
    foreach (var chiave in sezione.Keys)
    {
        //Ottengo un riferimento a questa proprietà specifica
        var proprietà = elencoProprietà.FirstOrDefault(p => p.Name.Equals(chiave.KeyName, StringComparison.InvariantCultureIgnoreCase));
        //Ignoro questa proprietà se non esisteva
        if (proprietà == null) 
            continue;

        //Altrimenti la imposto.
        //Attenzione perché ChangeType sollverà un'eccezione se il valore
        //impostato nell'ini non è convertibile al tipo della proprietà
        var valore = Convert.ChangeType(chiave.Value, proprietà.PropertyType);
        proprietà.SetValue(null, valore);
    }
}


ciao,
Moreno
Modificato da BrightSoul il 02 maggio 2016 00.12 -

Enjoy learning and just keep making
8 messaggi dal 01 maggio 2016
Ciao Moreno, non sai quanto ti ringrazio per queste informazioni.
Proverò subito a testare la funzionalità della Reflection e ti farò sapere. PEr quanto riguarda la classe per il Parser Ini avevo già trovato qualcosa ma se mi consigli quella libreria la proverò ad usare.

Grazie ancoraaaaaaaaaaaaaaaaaa :)
8 messaggi dal 01 maggio 2016
Sto provando ma qualcosa sembra sfuggirmi.
Questo è il codice di prova che sto cercando di far funzionare.

Le sezioni vengono lette correttamente ma quando va ad eseguire
questa parte di codice la proprietà è nulla

var proprietà = elencoProprietà.FirstOrDefault(p => p.Name.Equals(chiave.KeyName, StringComparison.InvariantCultureIgnoreCase));



Forse sbaglio nel settare il valore di assembly e namespace?
string assemblyName = "ConsoleApplication1";
string namespaceName = "ConsoleApplication1.Program";




Qui c'è il codice per intero

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {

            // Program t = new Program();
            //t.Run();
            var testo = @"[Porta]
                            path='legno'
                            userid=12
                            password=nera

                            [Predefiniti_database]
                            path=C:\prova.db
                            userid=root
                            psw=123456
                            ";

            //Crea un'istanza del parser
            var parser = new IniParser.Parser.IniDataParser();
            var dati = parser.Parse(testo);

            //Qui personalizza il nome del namespace e dell'assembly in cui le classi si trovano
            string assemblyName = "ConsoleApplication1";
            string namespaceName = "ConsoleApplication1.Program";

     //Passo in rassegna le sezioni del file ini
            Console.WriteLine("Numero sezioni: " + dati.Sections.Count);

            
            foreach (var sezione in dati.Sections)
            {
                //Compongo il fully qualified name della classe (cioè namespace.nomeclasse, nomeassembly)
                string fullName = string.Format("{0}.{1}, {2}", namespaceName, sezione.SectionName, assemblyName);
                Type tipo = Type.GetType(fullName, throwOnError: false, ignoreCase: true);
                

Console.WriteLine("\nFull name: {2} \n Sezione[{1}]Tipo letto: {0}", tipo, sezione.SectionName, fullName);


                //Se il tipo è null, vuol dire che la classe non è stata trovata
                //Allora la ignoro e passo alla sezione successiva
                if (tipo == null)
                    continue;

                //Se esisteva, ottengo l'elenco delle proprietà statiche di questa classe
                var elencoProprietà = tipo.GetProperties(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);

                //Passo in rassegna le chiavi di questa sezione
                foreach (var chiave in sezione.Keys)
                {
                    //Ottengo un riferimento a questa proprietà specifica
                    var proprietà = elencoProprietà.FirstOrDefault(p => p.Name.Equals(chiave.KeyName, StringComparison.InvariantCultureIgnoreCase));
                    //Ignoro questa proprietà se non esisteva                    if (proprietà == null)
                        continue;

                    //Altrimenti la imposto.
                    //Attenzione perché ChangeType sollverà un'eccezione se il valore
                    //impostato nell'ini non è convertibile al tipo della proprietà
                    var valore = Convert.ChangeType(chiave.Value, proprietà.PropertyType);
                    proprietà.SetValue(null, valore,null);
                    Console.WriteLine("Valore scritto: {0}", valore);
                }
                                
            }
            Console.ReadLine();
        }
        

    }


    public static class Predefiniti_DataBase
    {
        public static string userId;
        public static string psw;
        public static string path;
    }
}




Modificato da atoxx il 02 maggio 2016 23.20 -
Modificato da atoxx il 02 maggio 2016 23.39 -
Modificato da atoxx il 02 maggio 2016 23.56 -

Modificato da atoxx il 03 maggio 2016 00.02 -
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao, prego!


questa parte di codice la proprietà è nulla

Ok, metti un breakpoint in quel punto e poi porta il mouse sopra la parola "elencoProprietà". Visual Studio ti mostrerà l'elenco delle proprietà ottenute dalla classe. Per caso la lista è vuota? Oppure ci sono alcune delle proprietà ma non quella che ti aspettavi di trovare?

Il problema potrebbe dipendere dal fatto che nella classe non hai proprietà con il modificatore static. Quindi si tratta di proprietà, non di campi.

Ti faccio un esempio di come dovrebbero essere definite.
    public class Porta
    {
        //Nota il modificatore static e il getter e setter che la rendono una proprietà
        public static string Materiale { get; set; }
        public static int Peso { get; set; }
        public static string Colore { get; set; }
    }


Se invece vuoi usare i campi, allora al posto del metodo .GetProperties() dovrai usare .GetFields().
var elencoCampi = tipo.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);


ciao,
Moreno
Modificato da BrightSoul il 03 maggio 2016 07.42 -

Enjoy learning and just keep making
8 messaggi dal 01 maggio 2016
Grazie per la risposta, soprattutto data alle 07 del mattino

Adesso funziona correttamente, non sò come ringraziarti
8 messaggi dal 01 maggio 2016
Scusate se riapro la discussione (se ritente che debba riaprire una thread ditemelo).

Volevo utilizzare la funzione resa funzionante grazie al vostro aiuto, in una pagina ASP.NET.
Ho quindi pensato di eseguire la funzione per l'inizializzazione delle classi statiche nella funzione Page_Load cosìcché all'avvio della pagina web si inizializzano le classi.

Non capisco perchè ma non funziona, nel senso che non appena cerco di caricare la classe tramite Type tipo = Type.GetType(fullName); mi restituisce null, nonostante il nome della classe sia corretto.

private void inizializzaClassiStatiche()
        {
            //recupero il percorso dell'eseguibile e controllo se è presente il file ini di configuraizone
            System.Reflection.Assembly a = System.Reflection.Assembly.GetEntryAssembly();
            //string baseDir = System.IO.Path.GetDirectoryName(a.Location);//
            string pathConfig = Request.PhysicalApplicationPath + "\\config.ini";
            bool esiste = System.IO.File.Exists(@pathConfig);

            if (!esiste)
            {
                //se il file non esiste non procedo con l'inizializzazione
                return;
            }



            //Crea un'istanza del parser
            var parser = new IniParser.Parser.IniDataParser();
            var dati = parser.Parse(System.IO.File.ReadAllText(@pathConfig));

            //Qui personalizza il nome del namespace e dell'assembly in cui le classi si trovano
            string assemblyName = "WebSite2";
            string namespaceName = "WebSite2";

            //Passo in rassegna le sezioni del file ini
            //MessageBox.Show("Numero sezioni: " + dati.Sections.Count);


            foreach (var sezione in dati.Sections)
            {
                //Compongo il fully qualified name della classe (cioè namespace.nomeclasse, nomeassembly)
                //string fullName = string.Format("{0}.{1}, {2}", namespaceName, sezione.SectionName, assemblyName);
                string fullName = string.Format("{0}.{1}", namespaceName, sezione.SectionName);
                //Type tipo = Type.GetType(fullName, false, true);
                Type tipo = Type.GetType(fullName);
                //Type tipo = typeof(WebSite2.Predefiniti_DataBase);
                TextBox1.Text += "Fullname: " + fullName + "\r\n";
                
                TextBox1.Text += "Sezione: " + sezione.SectionName + "\r\n";

                //Se il tipo è null, vuol dire che la classe non è stata trovata
                //Allora la ignoro e passo alla sezione successiva
                if (tipo == null)
                {
                    
                    TextBox1.Text += "Tipo null\r\n";
                    continue;
                }

                //Se esisteva, ottengo l'elenco delle proprietà statiche di questa classe
                var elencoProprietà = tipo.GetProperties(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
                var elencoCampi = tipo.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);

                //Passo in rassegna le chiavi di questa sezione
                foreach (var chiave in sezione.Keys)
                {
                    //MessageBox.Show("Chiave: " + chiave.KeyName + " Size elenco proprietà: " + elencoProprietà.Count());
                    //Ottengo un riferimento a questa proprietà specifica
                    var proprietà = elencoProprietà.FirstOrDefault(p => p.Name.Equals(chiave.KeyName, StringComparison.InvariantCultureIgnoreCase));
                    var campi = elencoCampi.FirstOrDefault(p => p.Name.Equals(chiave.KeyName, StringComparison.InvariantCultureIgnoreCase));

                    //Ignoro questa proprietà se non esisteva
                    if (proprietà != null || campi == null)
                    {
                        //MessageBox.Show("Proprietà null. o campi null ");
                        continue;
                    }

                    //Altrimenti la imposto.
                    

                    try
                    {

                        if (chiave.Value.StartsWith("'") && chiave.Value.EndsWith("'"))
                        {
                            //vuol dire che ci sono gli apici iniziali e finali e quindi devo imporre il tipo stringa
                            string valoreChiave = chiave.Value;
                            var valore = Convert.ChangeType(valoreChiave.ToString().Substring(1, valoreChiave.IndexOf("'", 1) - 1), TypeCode.String);
                            campi.SetValue(null, valore);
                        }
                        else if (!chiave.Value.Contains("'"))
                        { //dato numerico, impongo int come tipo
                            var valore = Convert.ChangeType(chiave.Value, TypeCode.Int32);
                            campi.SetValue(null, valore);
                            
                        }
                        
                  
                    }
                    catch (System.ArgumentException ex2)
                    {
                        //Questo Evento scatta quando cerco di convertire un tipo che non corrisponde con quello presente nella classe
                        //se dovesse avvenire, salto al prossimo campo.

                        
                        continue;
                    }
                catch (FormatException ex2)
                {
                    //Questo Evento scatta quando cerco di convertire un tipo che non corrisponde con quello presente nella classe
                    //se dovesse avvenire, salto al prossimo campo.

                    
                    continue;
                }

            }

            }          



        }





Non capisco quale possa essere il problema. E' corretto inserire come


string assemblyName = "WebSite2";
string namespaceName = "WebSite2"

??


Non sò se può aiutare ma vi allego la situazione dei file nel mio progetto


http://i68.tinypic.com/15xrbit.png
11.886 messaggi dal 09 febbraio 2002
Contributi
Ciao,
probabilmente è sbagliato sia il namespace che il nome dell'assembly.
Infatti, per i siti web il nome dell'assembly non è facilmente prevedibile perché il codice viene compilato dinamicamente, all'avvio del sito.
Per esempio, in un mio website demo è App_Code.48df2xr5.

Quindi dovresti ottenere i due valori (namespaceName e assemblyName) dinamicamente, magari prendendoli da una delle classi che hai nella cartella App_Code.

Prova così. Dobbiamo anche prevedere il fatto che il namespace possa essere vuoto.
//Class1 è una delle tue classi che hai dentro App_Code
var assemblyName = typeof(Class1).Assembly.FullName;
var namespaceName = typeof(Class1).Namespace;
if (!string.IsNullOrEmpty(namespaceName)) namespaceName += ".";

e poi sotto componi il fullName così:
string fullName = string.Format("{0}{1}, {2}", namespaceName, sezione.SectionName, assemblyName);


ciao,
Moreno

Enjoy learning and just keep making

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.