Jak przechowywać kolekcję obiektów niestandardowych dla użytkownika.plik konfiguracyjny?

Chciałbym zapisać kolekcję niestandardowych obiektów w użytkowniku.plik konfiguracyjny i chciałby programowo dodawać i usuwać elementy z kolekcji, a następnie zapisać zmodyfikowaną listę z powrotem do pliku konfiguracyjnego.

Moje przedmioty mają następującą prostą formę:

class UserInfo
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }        
}

W mojej aplikacji.config utworzyłem już sekcję niestandardową:

<configuration>
  <configSections>
    <section name="userInfo" type="UserInfoConfigurationHandler, MyProgram"/>

  </configSections>
  <userInfo>
    <User firstName="John" lastName="Doe" email="[email protected]" />
    <User firstName="Jane" lastName="Doe" email="[email protected]" />
  </userInfo>

</configuration>

Jestem również w stanie odczytać w Ustawieniach implementując IConfigurationSectionHandler:

class UserInfoConfigurationHandler : IConfigurationSectionHandler
{
    public UserInfoConfigurationHandler() { }

    public object Create(object parent, object configContext, System.Xml.XmlNode section)
    {
        List<UserInfo> items = new List<UserInfo>();
        System.Xml.XmlNodeList processesNodes = section.SelectNodes("User");

        foreach (XmlNode processNode in processesNodes)
        {
            UserInfo item = new UserInfo();
            item.FirstName = processNode.Attributes["firstName"].InnerText;
            item.LastName = processNode.Attributes["lastName"].InnerText;
            item.Email = processNode.Attributes["email"].InnerText;
            items.Add(item);
        }
        return items;
    }
}

Zrobiłem to wszystko po tym Artykuł. Jednak przy użyciu tego podejścia jestem w stanie tylko odczytać ustawienia z aplikacji.config do kolekcji List<UserInfo>, ale musiałbym również napisać zmodyfikowaną listę z powrotem.

Przeszukałem dokumentację bez powodzenia, a teraz utknąłem. Co przegapiłem?

Author: Dirk Vollmar, 2009-04-09

5 answers

Nie przechowywałbym takich danych w aplikacji.config, przynajmniej nie jeśli ma być aktualizowana programowo. Koncepcyjnie dotyczy to ustawień konfiguracyjnych, a nie danych aplikacji, więc być może chcesz zapisać swoją nazwę użytkownika i hasło w oddzielnym pliku XML (zakładając, że nie możesz lub nie chcesz korzystać z bazy danych)?

Powiedziawszy to, myślę, że najlepiej będzie przeczytać w aplikacji.config jako standardowy plik XML, przeanalizuj go, dodaj węzły, które chcesz i zapisz go z powrotem. The built w ConfigurationManager API Nie oferuje sposobu na odpisywanie nowych ustawień (co, jak przypuszczam, daje wskazówkę co do zamierzonego zastosowania Microsoftu).

 7
Author: Dana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-09 14:47:43

Aby dodać konfigurację niestandardową (jeśli potrzebujesz więcej niż tylko prostych typów), należy użyć ConfigurationSection, w ramach którego dla zdefiniowanego schematu potrzebujesz ConfigurationElementCollection (ustawionej jako domyślna kolekcja bez nazwy), która zawiera ConfigurationElement, w następujący sposób:

public class UserElement : ConfigurationElement
{
    [ConfigurationProperty( "firstName", IsRequired = true )]
    public string FirstName
    {
        get { return (string) base[ "firstName" ]; }
        set { base[ "firstName" ] = value;}
    }

    [ConfigurationProperty( "lastName", IsRequired = true )]
    public string LastName
    {
        get { return (string) base[ "lastName" ]; }
        set { base[ "lastName" ] = value; }
    }

    [ConfigurationProperty( "email", IsRequired = true )]
    public string Email
    {
        get { return (string) base[ "email" ]; }
        set { base[ "email" ] = value; }
    }

    internal string Key
    {
        get { return string.Format( "{0}|{1}|{2}", FirstName, LastName, Email ); }
    }
}

[ConfigurationCollection( typeof(UserElement), AddItemName = "user", CollectionType = ConfigurationElementCollectionType.BasicMap )]
public class UserElementCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new UserElement();
    }

    protected override object GetElementKey( ConfigurationElement element )
    {
        return ( (UserElement) element ).Key;
    }

    public void Add( UserElement element )
    {
        BaseAdd( element );
    }

    public void Clear()
    {
        BaseClear();
    }

    public int IndexOf( UserElement element )
    {
        return BaseIndexOf( element );
    }

    public void Remove( UserElement element )
    {
        if( BaseIndexOf( element ) >= 0 )
        {
            BaseRemove( element.Key );
        }
    }

    public void RemoveAt( int index )
    {
        BaseRemoveAt( index );
    }

    public UserElement this[ int index ]
    {
        get { return (UserElement) BaseGet( index ); }
        set
        {
            if( BaseGet( index ) != null )
            {
                BaseRemoveAt( index );
            }
            BaseAdd( index, value );
        }
    }
}

public class UserInfoSection : ConfigurationSection
{
    private static readonly ConfigurationProperty _propUserInfo = new ConfigurationProperty(
            null,
            typeof(UserElementCollection),
            null,
            ConfigurationPropertyOptions.IsDefaultCollection
    );

    private static ConfigurationPropertyCollection _properties = new ConfigurationPropertyCollection();

    static UserInfoSection()
    {
        _properties.Add( _propUserInfo );
    }

    [ConfigurationProperty( "", Options = ConfigurationPropertyOptions.IsDefaultCollection )]
    public UserElementCollection Users
    {
        get { return (UserElementCollection) base[ _propUserInfo ]; }
    }
}

Utrzymałem klasę UserElement w prostocie, chociaż naprawdę powinna ona postępować zgodnie ze schematem deklarowania każdej właściwości w pełni, jak opisano w ten doskonały artykuł CodeProject . Jak możesz zobacz, że reprezentuje on elementy "użytkownika" w konfiguracji, którą podałeś.

Klasa UserElementCollection obsługuje po prostu posiadanie więcej niż jednego elementu" user", w tym możliwość dodawania/usuwania / usuwania elementów z kolekcji, jeśli chcesz ją zmodyfikować w czasie wykonywania.

Na koniec jest UserInfoSection, które po prostu pokazuje, że ma domyślną kolekcję elementów "user".

Następna jest próbka aplikacji.plik konfiguracyjny:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup>
      <section
        name="userInfo"
        type="ConsoleApplication1.UserInfoSection, ConsoleApplication1"
        allowDefinition="Everywhere"
        allowExeDefinition="MachineToLocalUser"
      />
    </sectionGroup>
  </configSections>

  <userInfo>
    <user firstName="John" lastName="Doe" email="[email protected]" />
    <user firstName="Jane" lastName="Doe" email="[email protected]" />
  </userInfo>
</configuration>

Jak widać, w tym przykładzie mam dodano kilka elementów userInfo/user W aplikacji.config. Dodałem również ustawienia, aby powiedzieć, że można je zdefiniować na poziomie maszyna/aplikacja/użytkownik/roaming-użytkownik.

Następnie musimy wiedzieć, jak je zaktualizować w czasie wykonywania, poniższy kod pokazuje przykład:

Configuration userConfig = ConfigurationManager.OpenExeConfiguration( ConfigurationUserLevel.PerUserRoamingAndLocal );

var userInfoSection = userConfig.GetSection( "userInfo" ) as UserInfoSection;

var userElement = new UserElement();

userElement.FirstName = "Sample";
userElement.LastName = "User";
userElement.Email = "[email protected]";

userInfoSection.Users.Add( userElement );

userConfig.Save();

Powyższy kod utworzy nowego użytkownika.plik konfiguracyjny w razie potrzeby zakopany głęboko w folderze "Ustawienia lokalne\Dane aplikacji" dla użytkownika.

Jeśli zamiast tego chcesz, aby Nowy użytkownik został dodany do aplikacji.plik konfiguracyjny wystarczy zmienić parametr metody OpenExeConfiguration () do ConfigurationUserLevel.Brak

Jak widzisz, jest to dość proste, chociaż znalezienie tych informacji wymagało trochę kopania.

 49
Author: Timothy Walters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-13 03:38:45

Użyj nowego systemu .Konfiguracja API z. Net Framework 2. (Montaż: System.Konfiguracja) IConfigurationSectionHandler jest przestarzały.

Można znaleźć wiele bardzo dobrych próbek i opisów na http://www.codeproject.com/KB/dotnet/mysteriesofconfiguration.aspx

Istnieje również przykład kodu, o tym, jak można modyfikować i zapisywać wartości.


EDIT: odpowiednia część dokumentacji w codeproject

Zapisuje tylko zmodyfikowane wartości, jeśli istnieją jakiekolwiek zmiany

Configuration.Save() 

Zapisuje określony poziom zmian, jeśli istnieją jakiekolwiek zmiany

Configuration.Save(ConfigurationSaveMode) 

Zapisuje określony poziom zmian, wymuszając zapisanie, jeśli drugi parametr jest prawdziwy

Configuration.Save(ConfigurationSaveMode, bool)

Konfigurationsavemode wyliczenie ma następujące wartości:

  • Full - zapisuje wszystkie właściwości konfiguracyjne, niezależnie od tego, czy zostały zmienione czy nie
  • Modified - Zapisuje właściwości, które zostały zmodyfikowane, nawet jeśli bieżąca wartość jest taka sama jak oryginalna
  • Minimal - zapisuje tylko właściwości, które zostały zmodyfikowane i mają inne wartości niż oryginalne
 1
Author: Michael Piendl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-09 15:23:56
 private void frmLocalFileTransfer_Load(object sender, EventArgs e)
    {
        try
        {
            dt.Columns.Add("Provider");
            dt.Columns.Add("DestinationPath");
            string[] folders = null;
            dt.Columns.Add("SourcePath");

            for (int i = 1; i < System.Configuration.ConfigurationManager.ConnectionStrings.Count; i++)
            {
                string key = System.Configuration.ConfigurationManager.ConnectionStrings[i].Name;
                string constr = System.Configuration.ConfigurationManager.ConnectionStrings[i].ConnectionString;
                DataGridViewTextBoxColumn column = new DataGridViewTextBoxColumn();
                folders = constr.Split('\\');
                string newstr = (folders[folders.Length - 2]);
                if (!newstr.Contains("BackUp"))
                {
                    DataRow row = dt.NewRow();
                    row[dt.Columns[0].ToString()] = key;
                    row[dt.Columns[1].ToString()] = constr;
                    row[dt.Columns[2].ToString()] = constr;
                    dt.Rows.InsertAt(row, i - 1);
                }
            }

            foreach (DataColumn dc in dt.Columns)
            {
                DataGridViewTextBoxColumn column = new DataGridViewTextBoxColumn();
                column.DataPropertyName = dc.ColumnName;
                column.HeaderText = dc.ColumnName;
                column.Name = dc.ColumnName;
                column.SortMode = DataGridViewColumnSortMode.Automatic;
                column.ValueType = dc.DataType;
                GVCheckbox();

                gevsearch.Columns.Add(column);
            }
            if (gevsearch.ColumnCount == 4)
            {
                DataGridViewButtonColumn btnColoumn = new DataGridViewButtonColumn();
                btnColoumn.Width = 150;
                btnColoumn.HeaderText = "Change SourcePath";
                btnColoumn.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
                gevsearch.Columns.Insert(4, btnColoumn);


            }
             gevsearch.DataSource = dt;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }

private void btnAddProvider_Click(object sender, EventArgs e)
    {
        try
        {
            System.Xml.XmlDocument xDoc = new System.Xml.XmlDocument();
            path = "D:\\Pandurang\\Jitendra\\LocalFileTransfer\\LocalFileTransfer
            xDoc.Load(path);
            System.Xml.XmlElement element = xDoc.CreateElement("add");
            element.SetAttribute("name", txtProviderName.Text.Trim());
            element.SetAttribute("connectionString", txtNewPath.Text.Trim());
            System.Xml.XmlElement elementBackup = xDoc.CreateElement("add");
            elementBackup.SetAttribute("name", BackUpName);
            elementBackup.SetAttribute("connectionString", txtBackupPath.Text.Trim());
            System.Xml.XmlNode node= xDoc.ChildNodes[1].ChildNodes[0];
            node.AppendChild(element);
            node.AppendChild(elementBackup);
            xDoc.Save(path);
          }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }
 0
Author: Pandurang Kumbhar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-08 08:42:40

Powinieneś utworzyć klasę taką jak:

public class MySettings : ConfigurationSection 
{
    public MySettings Settings = (MySettings)WebConfigurationManager.GetSection("MySettings");

    [ConfigurationProperty("MyConfigSetting1")]
    public string DefaultConnectionStringName
    {
        get { return (string)base["MyConfigSetting1"]; }
        set { base["MyConfigSetting1"] = value; }
    }
}

Potem w Twojej sieci.Config use:

<section name="MySettings" type="MyNamespace.MySettings"/>
<MySettings MyConfigSetting1="myValue">

That ' s the way) Jeśli chcesz użyć nie attibutes, ale properties, po prostu utwórz klasę pochodzącą z ConfigurationElement i dołącz ją do swojej klasy wydziedziczonej z ConfigurationSettings.

 -1
Author: 0100110010101,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-09 16:48:46