简体   繁体   English

如何更改.NET应用程序的预定义userconfig目录?

[英]How to change the predefined userconfig directory of my .NET application?

Currentlly the user-settings of my application are stored in this default directory: 当前,我的应用程序的用户设置存储在此默认目录中:

C:\Users\{User Name}\AppData\Roaming\{Company Name}\{Assembly Name}.vshos_Url_{Hash}\{Assembly Version}

I'm aware of the meaning of the default Microsoft naming rules, my question is: How can I change that defaut folder at execution time or by modifying the appconfig file?. 我知道默认Microsoft命名规则的含义,我的问题是:如何在执行时更改defaut文件夹或通过修改appconfig文件?

My intention is only be able to handle the directory on which the user-settings of my application will be saved, for example I would like to save the user-settings file in this directory: 我的意图是只能处理将应用程序的用户设置保存到的目录,例如,我想将用户设置文件保存在以下目录中:

C:\Users\{User Name}\AppData\Roaming\{Assembly Name}

I know this is possible to acchieve, because I've seen much .NET applications that can store its userconfig file in a custom Roaming folder that doesn't follows the Microsoft default rules with that unhandled hash and those other annonying naming rules. 我知道这是可以实现的,因为我已经看到很多.NET应用程序可以将其userconfig文件存储在自定义漫游文件夹中,该文件夹不遵循Microsoft默认规则以及未处理的哈希和其他令人讨厌的命名规则。

That naming convention exists so that NET can be sure the correct settings are be loaded. 存在该命名约定,以便NET可以确保已加载正确的设置。 Since you have yielded control of managing settings to the NET Framework/VB Application Framework, it also takes on the responsibility of making sure that an app is loading the right set of settings. 由于您已放弃了对NET Framework / VB应用程序框架的设置进行管理的控制,因此它还负责确保应用程序正在加载正确的设置集。 That being the case, the evidence hash is used to uniquely identify one WindowsApplication1 from another (among other things). 在这种情况下,证据散列用于将一个WindowsApplication1与另一个WindowsApplication1唯一地标识。

I know this is possible to acchieve, because I've seen much .NET applications that can store its userconfig file in a custom Roaming folder

It is possible, but I am not sure everything is quite the way you conclude. 有可能,但我不确定所有内容是否都符合您的结论。 I very seriously doubt that many apps go thru the hassle to implement a custom provider when they could much more easily save an XML file to that location using a custom settings class. 我非常严重地怀疑,许多应用程序使用自定义设置类可以更轻松地将XML文件保存到该位置时,会麻烦地实现自定义提供程序。

The Simple Solution 简单的解决方案

Write your own user options class, and serialize it yourself. 编写自己的用户选项类,然后自己进行序列化。 For instance a Shared/static method can be used to deserialize the class in very little code (this just happens to use JSON): 例如,可以使用Shared / static方法以很少的代码反序列化类(这恰好使用JSON):

Friend Shared Function Load() As UserOptions
    ' create instance for default on new install 
    Dim u As New UserOptions

    If File.Exists(filePath) Then
        ' filepath can be anywhere you have access to!
        Dim jstr = File.ReadAllText(filePath)
        If String.IsNullOrEmpty(jstr) = False Then
            u = JsonConvert.DeserializeObject(Of UserOptions)(jstr)
        End If
    End If

    Return u
End Function

The app implementing it: 实现它的应用程序:

UOpt = UserOptions.Load()

Among the Pros , you have total control over where the file is saved, and can use any serializer you like. Pro中 ,您可以完全控制文件的保存位置,并且可以使用任何喜欢的序列化程序。 Most of all, it is simple - much less code than presented below. 最重要的是,它很简单 -比下面介绍的代码少得多。

The Cons are that the code using it would have to manually Load and Save them (easily handled in Application events), and there is no fancy designer for it. 缺点是使用它的代码必须手动加载和保存它们(在Application事件中很容易处理),并且没有花哨的设计器。

The Long and Winding Road: a Custom SettingsProvider 漫长而曲折的道路:自定义设置提供商

A custom SettingsProvider will allow you to change how settings are handled, saved and loaded, including changing the folder location. 自定义SettingsProvider将允许您更改设置的处理,保存和加载方式,包括更改文件夹位置。

This question is narrowly focused on changing the file location. 这个问题只集中在更改文件位置上。 The problem is that there is not a (clean, easy) way for your app to talk to your SettingsProvider in order to specify a folder. 问题在于,您的应用没有一种(干净,简单)的方式与SettingsProvider进行对话以指定文件夹。 The provider needs to be able to work that out internally and of course be consistent. 提供者需要能够在内部进行工作,当然必须保持一致。

Most people will want to do more than just change the folder name used. 除了更改所使用的文件夹名称之外,大多数人还希望做更多的事情。 For instance, in playing around, in place of XML I used a SQLite database which mirrored a structure the code uses. 例如,在游戏中,我使用了一个SQLite数据库来代替XML,该数据库镜像了代码使用的结构。 This made it very easy to load local and the correct roaming values. 这使得加载本地和正确的漫游值非常容易。 Were that approach taken all the way, the code could be greatly simplified and quite possibly the whole Upgrade process. 如果始终采用这种方法,则可以大大简化代码,甚至可以简化整个升级过程。 So this provider takes some of those wider needs into account. 因此,该提供商考虑了一些更广泛的需求。

There are 2 key considerations even though you just want to change the filename: 即使您只想更改文件名,也有两个关键注意事项:

Local vs Roaming 本地与漫游

Coding the provider to always store in AppData\\Roaming but write unqualified local settings there would be irresponsible. 编码提供程序以始终存储在AppData\\Roaming但是编写不合格的本地设置将是不负责任的。 Distinguishing between them is a capability that ought not be sacrificed in order to elide the evidence hash in the folder name. 区分它们是不应该为了消除文件夹名称中的证据哈希而牺牲的功能。

Note: Each Setting can be set as a Roaming or Local value: with a Setting selected in the Settings Editor, open the Properties pane - change Roaming to True. 注意:可以将每个Setting设置为Roaming值或Local值:在“设置编辑器”中选择一个设置,打开“属性”窗格-将“ Roaming更改为True。

There seems to be a consensus in the (very) few questions here dealing with a custom SettingsProvider to save Local and Roaming to the same file but in different sections. 在(自定义) SettingsProvider处理(本地)和漫游到同一文件但不同部分中的(很少)几个问题中似乎达成了共识。 That makes a great deal of sense - and is simpler than loading from 2 files - so the XML structure used is: 这非常有意义-比从2个文件加载更简单-因此使用的XML结构为:

<configuration>
  <CommonShared>
    <setting name="FirstRun">True</setting>
    <setting name="StartTime">15:32:18</setting>
    ...
  </CommonShared>
  <MACHINENAME_A>
    <setting name="MainWdwLocation">98, 480</setting>
    <setting name="myGuid">d62eb904-0bb9-4897-bb86-688d974db4a6</setting>
    <setting name="LastSaveFolder">C:\Folder ABC</setting>
  </MACHINENAME_A>
  <MACHINENAME_B>
    <setting name="MainWdwLocation">187, 360</setting>
    <setting name="myGuid">a1f8d5a5-f7ec-4bf9-b7b8-712e80c69d93</setting>
    <setting name="LastSaveFolder">C:\Folder XYZ</setting>
  </MACHINENAME_B>
</configuration>

The roaming items are stored in sections named after the MachineName they are used on. 漫游项存储在使用它们的MachineName命名的节中。 There might be some value in retaining the <NameSpace>.My.MySettings node, but I am not sure what purpose it serves. 保留<NameSpace>.My.MySettings节点可能会有一些价值,但是我不确定它的作用是什么。

I removed the SerializeAs element since it is not used. 由于未使用SerializeAs元素,因此删除了它。

Versions 版本号

Nothing whatsoever will happen if you invoke My.Settings.Upgrade . 如果调用My.Settings.Upgrade则不会发生任何事情。 Even though it is a Settings method, it is actually something in ApplicationSettingsBase , so your provider isn't involved. 尽管它是Settings方法,但实际上它是ApplicationSettingsBase ,因此不涉及您的提供程序。

As a result, using the full version string as part of the folder causes a problem if you auto increment the last element. 结果,如果您自动增加最后一个元素,则使用完整版本字符串作为文件夹的一部分会导致问题。 Trivial rebuilds will create a new folder and lose and orphan the old settings. 简单的重建将创建一个新文件夹,并使旧设置丢失并孤立。 Perhaps you could look for and load the values for a previous version when there is no current file. 当没有当前文件时,也许您可​​以查找并加载先前版本的值。 Then perhaps delete that old file/folder, so there is always only one possible set of old settings. 然后也许删除该旧文件/文件夹,因此始终只有一组可能的旧设置。 Feel free to add oodles and oodles of merge code. 随意添加面条和合并代码。

For the primary purpose of just changing the data store folder, I removed the version folder segment. 为了仅更改数据存储文件夹的主要目的,我删除了版本文件夹段。 When using a global provider the code automatically accumulates settings. 使用全局提供程序时,代码会自动累积设置。 A settings which has been removed wont "leak" into the app because NET wont ask for a value for it. 已删除的设置不会“泄漏”到应用程序中,因为NET不会要求它提供值。 The only issue is that there will be a value for it in the XML. 唯一的问题是XML中将有一个值。

I added code to purge these. 我添加了清除这些代码。 This will prevent a problem should you later reuse a settings name with a different type. 如果您以后再使用其他类型的设置名称,则可以防止出现问题。 The old saved value for Foo as Decimal wont work with the new Foo as Size for instance. 例如, Foo的旧保存值( Decimal与新Foo Size使用。 Things will still go badly if you radically change a type. 如果您从根本上更改类型,事情仍然会很糟糕。 Don't do that. 不要那样做


This answer Custom path of the user.config provides a very nice starting point for a custom provider. 这个答案user.config的Custom路径为定制提供程序提供了一个很好的起点。 It has a few problems and is missing a few things but provides a quick start guide for some of the steps and boilerplate code typical of any provider. 它有一些问题,缺少一些东西,但是提供了任何提供程序特有的一些步骤和样板代码的快速入门指南。 Since many people may need to further modify the provider here, it might be worth reading (and upvoting). 由于许多人可能需要在此处进一步修改提供程序,因此可能值得阅读(并赞成)。

The code here borrows a few things from that answer and: 这里的代码从该答案中借用了一些东西,并且:

  • Adds various refinements 添加各种改进
  • Provides for a custom path 提供自定义路径
  • Detection for settings set as Roaming 检测设置为漫游的设置
  • Local and Roaming section in the file 文件中的“本地和漫游”部分
  • Proper handling of complex types such as Point or Size 正确处理PointSize等复杂类型
  • Detect and prune removed settings 检测并修剪删除的设置
  • is in VB 在VB中

1. Setup 1.设定

For the most part, you can't incrementally write/debug this - little will work until you are done. 在大多数情况下,您无法以增量方式编写/调试此文件-在完成之前几乎没有用。

  • Add a reference to System.Configuration 添加对System.Configuration的引用
  • Add a new class to your project 向您的项目添加新类

Example: 例:

Imports System.Configuration 
Public Class CustomSettingsProvider
    Inherits SettingsProvider
End Class

Next, go to the Settings designer and add some settings for testing. 接下来,转到“设置”设计器并添加一些测试设置。 Tag some as Roaming for a complete test. 将某些标记为“漫游”以进行完整测试。 Then click the <> View Code button shown here: 然后单击此处显示的<> View Code按钮:

在此处输入图片说明 Everyone loves freehand circles! 每个人都喜欢写意圈子!

There are apparently two ways to implement a custom provider. 显然,有两种方法可以实现自定义提供程序。 The code here will use yours in place of My.MySettings . 此处的代码将使用您的代码代替My.MySettings You can also specify a custom provider on a per-Setting basis by typing the provider name in the Properties pane, and skip the rest of this step. 您还可以通过在“属性”窗格中键入提供程序名称来按设置指定自定义提供程序,然后跳过此步骤的其余部分。 I did not test this, but it is how it is supposed to work. 我没有对此进行测试,但是它应该是这样工作的。

In order to use the new settings provider "you" write, it needs to be associated with MySettings using an attribute: 为了使用新的设置提供程序“您”编写,需要使用一个属性将其与MySettings关联:

Imports System.Configuration 

<SettingsProvider(GetType(ElectroZap.CustomSettingsProvider))>
Partial Friend NotInheritable Class MySettings
End Class

'ElektroZap' is your root NameSpace, and 'ElektroApp' is your app name, by the way. 顺便说一句,“ ElektroZap”是您的根NameSpace,而“ ElektroApp”是您的应用程序名称。 The code in the constructor can be changed to use the Product name or module name. 可以将构造函数中的代码更改为使用产品名称或模块名称。

We are done with that file; 我们已经完成了该文件。 save it and close it. 保存并关闭它。

2. SettingsProvider 2. SettingsProvider

First, note that this CustomProvider is generic and should work with any app by just designating it as the SettingsProvider . 首先,请注意,此CustomProvider是通用的,只需将其指定为SettingsProvider与任何应用一起使用。 But it really only does 2 things: 但这实际上只做两件事:

  • Uses a custom path 使用自定义路径
  • Merges local and roaming settings into one file 将本地和漫游设置合并到一个文件中

Typically one would have a longer ToDo list before resorting to a custom provider, so for many this may just provide the starting point for Other Things. 通常,在求助于自定义提供程序之前,一个待办事项列表会更长,因此对于许多人来说,这可能只是“其他事物”的起点。 Keep that in mind that some changes may make it specific to a project. 请记住,某些更改可能使其特定于项目。


One of the things added is support for the more complex types such as Point or Size . 添加的功能之一是支持更复杂的类型,例如PointSize These are serialized as invariant strings so that they can be parsed back. 这些被序列化为不变字符串,以便可以解析它们。 What that means is this: 这意味着什么:

Console.WriteLine(myPoint.ToString())

The result, {X=64, Y=22} cannot be directly converted back and Point lacks a Parse/TryParse method. 结果{X=64, Y=22}无法直接转换回去,并且Point缺少Parse/TryParse方法。 Using the invariant string form 64,22 allows it to be converted back to the correct type. 使用不变字符串形式64,22可以将其转换回正确的类型。 The original linked code simply used: 原始的链接代码简单地使用了:

Convert.ChangeType(setting.DefaultValue, t);

This will work with simple types, but not Point , Font etc. I can't recall for sure, but I think this is a simple mistake of using SettingsPropertyValue.Value rather than .SerializedValue . 这将适用于简单的类型,但不适用于PointFont等。我无法确定,但是我认为这是使用SettingsPropertyValue.Value而不是.SerializedValue的简单错误。

3. The Code 3.守则

Public Class CustomSettingsProvider
    Inherits SettingsProvider

    ' data we store for each item
    Friend Class SettingsItem
        Friend Name As String
        'Friend SerializeAs As String           ' not needed
        Friend Value As String
        Friend Roamer As Boolean
        Friend Remove As Boolean                ' mutable
        'Friend VerString As String             ' ToDo (?)
    End Class

    ' used for node name
    Private thisMachine As String

    ' loaded XML config
    'Private xDoc As XDocument
    Private UserConfigFilePath As String = ""
    Private myCol As Dictionary(Of String, SettingsItem)


    Public Sub New()
        myCol = New Dictionary(Of String, SettingsItem)

        Dim asm = Assembly.GetExecutingAssembly()
        Dim verInfo = FileVersionInfo.GetVersionInfo(asm.Location)
        Dim Company = verInfo.CompanyName
        ' product name may have no relation to file name...
        Dim ProdName = verInfo.ProductName

        ' use this for assembly file name:
        Dim modName = Path.GetFileNameWithoutExtension(asm.ManifestModule.Name)
        ' dont use FileVersionInfo;
        ' may want to omit the last element
        'Dim ver = asm.GetName.Version


        '  uses `SpecialFolder.ApplicationData`
        '    since it will store Local and Roaming val;ues
        UserConfigFilePath = Path.Combine(GetFolderPath(SpecialFolder.ApplicationData),
                                      Company, modName,
                                       "user.config")

        ' "CFG" prefix prevents illegal XML, 
        '    the FOO suffix is to emulate a different machine
        thisMachine = "CFG" & My.Computer.Name & "_FOO"

    End Sub

    ' boilerplate
    Public Overrides Property ApplicationName As String
        Get
            Return Assembly.GetExecutingAssembly().ManifestModule.Name
        End Get
        Set(value As String)

        End Set
    End Property

    ' boilerplate
    Public Overrides Sub Initialize(name As String, config As Specialized.NameValueCollection)
        MyBase.Initialize(ApplicationName, config)
    End Sub

    ' conversion helper in place of a 'Select Case GetType(foo)'
    Private Shared Conversion As Func(Of Object, Object)

    Public Overrides Function GetPropertyValues(context As SettingsContext,
                                                collection As SettingsPropertyCollection) As SettingsPropertyValueCollection
        ' basically, create a Dictionary entry for each setting,
        ' store the converted value to it
        ' Add an entry when something is added
        '
        ' This is called the first time you get a setting value
        If myCol.Count = 0 Then
            LoadData()
        End If

        Dim theSettings = New SettingsPropertyValueCollection()
        Dim tValue As String = ""

        ' SettingsPropertyCollection is like a Shopping list
        ' of props that VS/VB wants the value for
        For Each setItem As SettingsProperty In collection
            Dim value As New SettingsPropertyValue(setItem)
            value.IsDirty = False

            If myCol.ContainsKey(setItem.Name) Then
                value.SerializedValue = myCol(setItem.Name)
                tValue = myCol(setItem.Name).Value
            Else
                value.SerializedValue = setItem.DefaultValue
                tValue = setItem.DefaultValue.ToString
            End If

            ' ToDo: Enums will need an extra step
            Conversion = Function(v) TypeDescriptor.
                                    GetConverter(setItem.PropertyType).
                                    ConvertFromInvariantString(v.ToString())

            value.PropertyValue = Conversion(tValue)
            theSettings.Add(value)
        Next

        Return theSettings
    End Function

    Public Overrides Sub SetPropertyValues(context As SettingsContext,
                                           collection As SettingsPropertyValueCollection)
        ' this is not called when you set a new value
        ' rather, NET has one or more changed values that
        ' need to be saved, so be sure to save them to disk
        Dim names As List(Of String) = myCol.Keys.ToList
        Dim sItem As SettingsItem

        For Each item As SettingsPropertyValue In collection
            sItem = New SettingsItem() With {
                                .Name = item.Name,
                                .Value = item.SerializedValue.ToString(),
                                .Roamer = IsRoamer(item.Property)
                            }
            '.SerializeAs = item.Property.SerializeAs.ToString(),

            names.Remove(item.Name)
            If myCol.ContainsKey(sItem.Name) Then
                myCol(sItem.Name) = sItem
            Else
                myCol.Add(sItem.Name, sItem)
            End If
        Next

        ' flag any no longer used
        ' do not use when specifying a provider per-setting!
        For Each s As String In names
            myCol(s).Remove = True
        Next

        SaveData()
    End Sub

    ' detect if a setting is tagged as Roaming
    Private Function IsRoamer(prop As SettingsProperty) As Boolean
        Dim r = prop.Attributes.
                    Cast(Of DictionaryEntry).
                    FirstOrDefault(Function(q) TypeOf q.Value Is SettingsManageabilityAttribute)

        Return r.Key IsNot Nothing
    End Function

    Private Sub LoadData()
        ' load from disk
        If File.Exists(UserConfigFilePath) = False Then
            CreateNewConfig()
        End If

        Dim xDoc = XDocument.Load(UserConfigFilePath)
        Dim items As IEnumerable(Of XElement)
        Dim item As SettingsItem

        items = xDoc.Element(CONFIG).
                             Element(COMMON).
                             Elements(SETTING)

        ' load the common settings
        For Each xitem As XElement In items
            item = New SettingsItem With {.Name = xitem.Attribute(ITEMNAME).Value,
                                          .Roamer = False}
            '.SerializeAs = xitem.Attribute(SERIALIZE_AS).Value,

            item.Value = xitem.Value
            myCol.Add(item.Name, item)
        Next

        ' First check if there is a machine node
        If xDoc.Element(CONFIG).Element(thisMachine) Is Nothing Then
            ' nope, add one
            xDoc.Element(CONFIG).Add(New XElement(thisMachine))
        End If
        items = xDoc.Element(CONFIG).
                            Element(thisMachine).
                            Elements(SETTING)

        For Each xitem As XElement In items
            item = New SettingsItem With {.Name = xitem.Attribute(ITEMNAME).Value,
                                          .Roamer = True}
            '.SerializeAs = xitem.Attribute(SERIALIZE_AS).Value,

            item.Value = xitem.Value
            myCol.Add(item.Name, item)
        Next
        ' we may have changed the XDOC, by adding a machine node 
        ' save the file
        xDoc.Save(UserConfigFilePath)
    End Sub

    Private Sub SaveData()
        ' write to disk

        Dim xDoc = XDocument.Load(UserConfigFilePath)
        Dim roamers = xDoc.Element(CONFIG).
                           Element(thisMachine)

        Dim locals = xDoc.Element(CONFIG).
                          Element(COMMON)

        Dim item As XElement
        Dim section As XElement

        For Each kvp As KeyValuePair(Of String, SettingsItem) In myCol
            If kvp.Value.Roamer Then
                section = roamers
            Else
                section = locals
            End If

            item = section.Elements().
                        FirstOrDefault(Function(q) q.Attribute(ITEMNAME).Value = kvp.Key)

            If item Is Nothing Then
                ' found a new item
                Dim newItem = New XElement(SETTING)
                newItem.Add(New XAttribute(ITEMNAME, kvp.Value.Name))
                'newItem.Add(New XAttribute(SERIALIZE_AS, kvp.Value.SerializeAs))
                newItem.Value = If(String.IsNullOrEmpty(kvp.Value.Value), "", kvp.Value.Value)
                section.Add(newItem)
            Else
                If kvp.Value.Remove Then
                    item.Remove()
                Else
                    item.Value = If(String.IsNullOrEmpty(kvp.Value.Value), "", kvp.Value.Value)
                End If
            End If

        Next
        xDoc.Save(UserConfigFilePath)

    End Sub

    ' used in the XML
    Const CONFIG As String = "configuration"
    Const SETTING As String = "setting"
    Const COMMON As String = "CommonShared"
    Const ITEMNAME As String = "name"
    'Const SERIALIZE_AS As String = "serializeAs"

    ' https://stackoverflow.com/a/11398536
    Private Sub CreateNewConfig()
        Dim fpath = Path.GetDirectoryName(UserConfigFilePath)
        Directory.CreateDirectory(fpath)

        Dim xDoc = New XDocument
        xDoc.Declaration = New XDeclaration("1.0", "utf-8", "true")
        Dim cfg = New XElement(CONFIG)

        cfg.Add(New XElement(COMMON))
        cfg.Add(New XElement(thisMachine))

        xDoc.Add(cfg)
        xDoc.Save(UserConfigFilePath)
    End Sub

End Class

That's a lot of code just to elide the evidence hash from the path, but it is what MS recommends. 这是很多代码,只是为了从路径中消除证据哈希,但这是MS建议的。 It is also likely the only way: the property in ConfigurationManager which gets the file is read only and it backed by code. 这也可能是唯一的方法:获取文件的ConfigurationManager中的属性是只读的,并由代码支持。

Results: 结果:

The actual XML is as shown earlier with local/common and machine specific sections. 实际的XML如前面显示的本地/公共和计算机特定部分所示。 I used several different app names and such testing various things: 我使用了几个不同的应用程序名称,并测试了各种内容:

在此处输入图片说明

Ignore the version portion. 忽略版本部分。 As noted earlier that has been removed. 如前所述,已被删除。 Otherwise the folders are correct - as noted above you have some options when it comes to the AppName segment. 否则,文件夹是正确的-如上所述,在AppName段中,您可以使用一些选项。

Important Notes 重要笔记

  • The Load methods in your provider are not called unless and until the related app accesses a Settings property 除非且直到相关应用程序访问Settings属性,否则不会调用提供程序中的Load方法。
  • Once loaded, the Save method will be called when the app ends (using the VB Framework) whether or not the code changes anything 加载后,无论代码是否更改,应用程序结束时(使用VB Framework)将调用Save方法。
  • NET seems to only save settings which are different from the default value. NET似乎只保存与默认值不同的设置。 When using a custom provider, all the values are marked IsDirty as true and UsingDefaultValue as false. 使用自定义提供程序时,所有值都标记为IsDirty为true和UsingDefaultValue为false。
  • If/when loaded, all the values are returned and NET simply gets values from that collection thru the life of the app 如果/在加载时,将返回所有值,并且NET会在应用程序的整个生命周期内从该集合中获取值

My main concern was the correct conversion of types and local/roaming support. 我主要关心的是类型和本地/漫游支持的正确转换。 I did not check every single possible Type . 没有检查每个可能的Type In particular, custom types and enums (I know enums will need extra handling). 特别是自定义类型和枚举(我知道枚举将需要额外的处理)。


It is worth noting that using a DataTable makes this much simpler. 值得注意的是,使用DataTable可以使此过程变得更加简单。 You do not need the SettingsItem class, the collection, no XDoc (use .WriteXML / .ReadXml ). 您不需要SettingsItem类,集合,不需要XDoc(使用.WriteXML / .ReadXml )。 All the code to create and organize XElements also goes away. 创建和组织XElement的所有代码也都消失了。

The resulting XML file is different, but that is just form following function. 生成的XML文件是不同的,但这仅是表单跟随功能。 About 60 lines of code in all can be removed, and it is just simpler. 总共可以删除大约60行代码,这很简单。

Resources 资源资源

I've seen many issues around this, eg: https://stackoverflow.com/a/15726277/495455 我已经看到许多与此相关的问题,例如: https : //stackoverflow.com/a/15726277/495455

To do anything special its much easier to use XDoc or LinqXML with your own config file(s). 要执行任何特殊操作,将XDoc或LinqXML与您自己的配置文件一起使用会容易得多。

This way you can save them where ever you like and you dont encounter other issues, eg: Custom Configuration Section can only be saved/modified while running as administrator? 这样,您可以将它们保存在任意位置,而不会遇到其他问题,例如: 自定义配置部分只能在以管理员身份运行时才能保存/修改?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在MSI安装程序中选择预定义的安装目录 - How to choose predefined install directory in a msi installer 如何从 ASP.NET 核心应用程序将图像上传到 Azure blob 存储中的特定目录? - How do I upload an image to a specific directory in my Azure blob storage from my ASP.NET Core application? 在我的MVC 3 .NET应用程序中,如何在网站启动时更改地址栏中的URL? - In my MVC 3 .NET application, how do I change the URL in the address bar on website startup? 如何从我的 ASP.NET 核心应用程序访问来自 Azure Active Directory 的其他数据? - How do I access additional data from Azure Active Directory from my ASP.NET Core application? 为什么我的文件没有保存到我的 ASP.NET 核心应用程序中的指定目录? - Why are my files not saving to my specified directory in my ASP.NET Core Application? 如何使Active Directory文件夹中的用户可以在ASP.NEt应用程序中看到Web窗体? - How I can make that users from a Active Directory Folder can see a webform in my ASP.NEt Application? 如何在C#.net的Windows应用程序根目录中读取文件? - How to read file in windows application root directory in C#.net? 如何在 WPF 设计器、.NET 6.0 中获取应用程序目录? - How to get the application directory in WPF designer, .NET 6.0? 在.net应用程序中使用Active Directory安装程序时如何获取LastActivityDate - How to get LastActivityDate when using Active Directory Setup in a .net application 如何在.NET中为我的应用程序设置代理设置 - How to set proxy settings for my application in .NET
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM