• Avalanche.Core
Search Results for

    Show / Hide Table of Contents
    • Avalanche.Accessor
      • Introduction
      • IAccessor
        • IAccessor
        • IListAccessor
        • IMapAccessor
        • IRecordAccessor
        • IContentAccessor
        • IOneOfAccessor
        • IAnyAccessor
      • .Net
        • Introduction
        • IList<T>
        • IDictionary<K,V>
        • FieldInfo
        • OneOfAttribute
        • StructLayoutAttribute
        • Class
      • Protobuf
        • Introduction
      • Articles
        • Dependency Injection
        • AccessorMessages
    • Avalanche.Binding
      • Introduction
    • Avalanche.Configuration
      • Introduction
      • Configuration Binding
      • ConfigurationExtensions
      • MemoryConfiguration
      • PrintTree
      • Saving IOptions
      • Yaml
    • Avalanche.Converter
      • Introduction
      • EnumConverter
      • Func<,>
      • HexConverter
      • PrimitiveConverter
      • StringConverter
    • Avalanche.Core
      • License
    • Avalanche.DataType
      • Introduction
      • DataType
        • IDataType
        • IListType
        • IMapType
        • IRecordType
        • IFieldType
        • IOneOfType
        • IAnyType
        • IStringType
        • IValueType
        • IIntegerType
        • IEnumerationType
        • IRealType
      • .Net
        • Introduction
        • IList<T>
        • IDictionary<K,V>
        • FieldInfo
        • Enum
        • OneOfAttribute
        • StructLayoutAttribute
        • Class
      • Protobuf
        • Introduction
      • Articles
        • DataTypeRequest
        • PrintTree
        • DataTypeMessages
    • Avalanche.Emit
      • Introduction
      • TypeBuilder
      • ConstructorBuilder
      • MethodBuilder
      • PropertyBuilder
      • FieldBuilder
      • Emit
      • Utilities
    • Avalanche.FileSystem
      • Introduction
      • Abstractions
        • IFileSystem
          • IFileSystemBrowse
          • IFileSystemCreateDirectory
          • IFileSystemDelete
          • IFileSystemFileAttribute
          • IFileSystemMount
          • IFileSystemMove
          • IFileSystemObserve
          • IFileSystemOpen
        • IEvent
        • IEntry
        • IOption
        • IToken
      • FileSystem
      • VirtualFileSystem
      • MemoryFileSystem
      • EmbeddedFileSystem
      • HttpFileSystem
      • Decoration
      • IFileProvider
      • Events
      • Utilities
        • Dispose
        • File Scanner
        • Visit Tree
        • File Operation
        • FilterEnumerable
        • PollingFilterWatchToken
    • Avalanche.Identity
      • Introduction
      • Identity
      • IdentityParts
      • IdentityInterner
      • IdentityComparer
      • Print Tree
      • IdentityAccessors
        • Introduction
        • TypeName
    • Avalanche.Localization
      • Introduction
      • Localization
      • LocalizationFile
      • LocalizationFiles
      • LocalizationFileSystem
      • LocalizationFileFormat
      • LocalizationLine
      • LocalizationLines
      • TemplateFormat
      • CultureProvider
      • FallbackCultureProvider
      • ResourceManager
      • LocalizationError
      • Microsoft.Extensions
        • Introduction
        • DependencyInjection
        • FileProvider
        • Logging
        • ITextLocalizer
        • IFileLocalizer
        • Localization
      • Asp.Net
        • Introduction
        • Supplying localization
        • Inject to pages
        • Culture Assigned
        • Minimalistic Api
        • Diagnostics
      • Pluralization
        • Introduction
        • Multiple plural parameters
        • Custom PluralRules
        • Invariant Culture
        • Unit Prefix
        • IPluralRule
        • IPluralNumber
        • IPluralRules
        • CLDRs
        • Unicode.CLDR40
        • Unicode.CLDR41
        • Unicode.CLDR42
      • Articles
        • Alphabet localization
        • Benchmarks
        • Caching
        • Class Library
        • Demo
        • Diagnostics
        • Embedded resources
        • Emplacement
        • File localization
        • Text localization
        • Printing templates
    • Avalanche.Message
      • Introduction
      • IMessage
      • IMessageProvider
      • IMessageDescription
      • IMessageDescriptions
      • MessageLevel
      • Message printing
      • Messages and Exceptions
      • Microsoft.Extensions
        • DependencyInjection
      • Articles
        • Aggregate Messages
        • Localization
        • Logging
        • Validation
    • Avalanche.Options
      • Introduction
      • OptionsExtensions
      • OptionsMonitorCast
    • Avalanche.Service
      • Introduction
      • Service
        • Introduction
        • IService
        • IServiceDisposable
        • IServiceDecoration
        • IServiceCast
        • IServiceObservable
        • IServiceContainer
        • Construction
        • Query
        • CancellationToken
        • CachePolicy
        • Scope
      • Handler
        • Introduction
        • IHandler
        • IHandlerCast
        • IHandlerDecoration
        • IHandlerWithOrder
        • CancellationToken
        • Cyclicity
        • Delegates
        • Invokable
        • ExportAttribute
        • OrderAttribute
        • PrintTree
        • Recursion
      • Query
        • Introduction
        • IQuery
        • IQueryCast
        • IQueryDecoration
      • Entry
        • Introduction
        • IEntry
        • IEntryCast
        • IEntryDecoration
        • IEntryObservable
        • IEntryVisitable
        • EntryState
      • Request
        • Introduction
        • IRequest
        • IRequestFor
        • IRequestToBeCached
        • IRequestToBeDisposed
        • RequestAttribute
        • ContextParameterAttribute
        • Print Tree
      • Dependency Injection
        • Introduction
        • Asp.Net
        • ServiceRequest<T>
        • Decorating a service
        • Handler
        • CachePolicy
        • CancellationToken
        • QueryLogger
        • IHostBuilder
      • Examples
        • NodeCount
        • Expression
        • Mapper
      • Articles
        • Benchmarks
        • Error Handling
        • ServiceMessages
    • Avalanche.StatusCode
      • Introduction
      • HResult
        • Introduction
        • HResult.Facilities
        • BasicMessages
        • RpcMessages
        • DispatchMessages
        • StorageMessages
        • ItfMessages
        • Win32Messages
        • WindowsMessages
        • SspiMessages
        • CertMessages
        • MediaServerMessages
        • SetupApiMessages
        • ScardMessages
        • ComPlusMessages
        • ClrMessages
        • UserModeFilterManagerMessages
        • GraphicsMessages
        • TpmServicesMessages
        • TpmSoftwareMessages
        • PlaMessages
        • FveMessages
        • FwpMessages
        • NdisMessages
        • DltMessages
      • System
        • Introduction
        • AccessControlMessages
        • AggregateMessages
        • AppDomainMessages
        • ArgumentMessages
        • ArgumentNullMessages
        • ArgumentOutOfRangeMessages
        • ArithmeticMessages
        • ArrayMessages
        • AssemblyMessages
        • BadImageFormatMessages
        • CodeContractMessages
        • CodePageMessages
        • CollectionsMessages
        • CompilerServiceMessages
        • CryptographyMessages
        • CultureMessages
        • DiagnosticsMessages
        • EventSourceMessages
        • ExecutionEngineMessages
        • FormatMessages
        • HostProtectionMessages
        • IOMessages
        • IndexOutOfRangeMessages
        • InteropServiceMessages
        • InvalidCastMessages
        • InvalidOperationMessages
        • IsolatedStorageMessages
        • LazyMessages
        • MarshalerMessages
        • MemoryMessages
        • MiscellaneousMessages
        • NotImplementedMessages
        • NotSupportedMessages
        • ObjectDisposedMessages
        • OperationCanceledMessages
        • OverflowMessages
        • PlatformMessages
        • PolicyMessages
        • PrincipalMessages
        • ProgramMessages
        • ReferenceMessages
        • ReflectionMessages
        • RegionMessages
        • RemotingMessages
        • ResourcesMessages
        • SecurityMessages
        • SerializationMessages
        • StackMessages
        • TaskMessages
        • TextMessages
        • ThreadingMessages
        • TimeZoneMessages
        • TypeMessages
        • XmlMessages
      • HttpStatusCode
      • OpcUaStatusCode
    • Avalanche.Template
      • Introduction
      • TemplateFormats
      • ITemplatePrintable
      • ITemplateFormatPrintable
      • ITemplateText
      • ITemplateBreakdown
      • ITemplateFormat
      • ITemplateFormats
      • Extract Arguments
      • Emplacement
    • Avalanche.Tokenizer
      • Introduction
      • IToken
      • ITokenizer
      • Tokenizers
    • Avalanche.Utilities
      • Introduction
      • Collections
        • Tuples
        • StructList
        • ArrayList
        • BijectionMap
        • LocakableDictionary
        • LockableList
        • MapList
        • Pipe
        • RingQueue
        • EnumerableExtensions
        • TupleUtilities
        • ArrayUtilities
      • Comparers
        • IGraphComparer
        • IGraphComparable
        • AlphaNumericComparer
        • EnumerableComparer
        • EnumerableGraphComparer
        • ReferenceComparer
        • KeyValuePairComparer
        • DefaultComparerProvider
        • RecordComparer
      • Cloners
        • ICloner
        • IGraphCloner
        • IGraphCloneable
        • ListCloner
        • DictionaryCloner
        • FieldCloner
        • PassthroughCloner
        • RecordCloner
        • ClonerProvider
      • Dispose
        • IDisposeAttachable
        • IDisposeBelatable
      • Provider
        • Introduction
        • ProviderBase
        • Delegate
        • Pipe
        • Cache
        • ResultCapture
        • AsReadOnly
        • AsService
        • IProviderEvent
      • Record
        • IRecordDescription
        • IFieldDescription
        • IConstructorDescription
        • IConstructionDescription
        • IParameterDescription
        • IRecordProviders
        • RecordDelegates
          • RecordCreate
          • RecordClone
          • RecordCopy
          • IRecordDelegates
        • FieldDelegates
          • FieldRead
          • FieldWrite
          • RecreateWith
          • IFieldDelegates
      • Reflection
        • EnumDescription
      • String
        • IEscaper
        • UnicodeString
        • Hex
      • Miscellaneous
        • IIdGenerator
        • Permutation
        • IReadOnly
        • IUserDataContainer
        • ITreeNode
        • Void
    • Avalanche.Writer
      • Introduction
      • ConstantWriter
      • Context
      • ConvertWriter
      • DefaultConstructor
      • DelegateWriter
      • PassthroughWriter
      • Referer
      • TypeCast
      • Writer
      • WriterPipe
      • WriterMessages

    Saving IOptions<T>

    Let's say you have an application that sends SMTP messages and where administrator can modify SMTP settings from GUI.

    Changes need to be saved on file and then reloaded so that changes reflect to IOptionsMonitor<SmtpSettings>.

    In this example a console application reads IOptions<SmtpSettings> from a .yaml.

    // Settings file
    string settingsFile = "appsettings-saved.yaml";
    // Create configuration
    IConfigurationRoot configuration = new ConfigurationBuilder()
        .AddYamlFile(settingsFile, optional: true, reloadOnChange: true)
        .Build();
    // Create dependency injection
    IServiceCollection serviceCollection = new ServiceCollection()
        .AddSingleton<IConfiguration>(configuration);
    serviceCollection.AddOptions<SmtpSettings>().BindConfiguration("Smtp");
    IServiceProvider services = serviceCollection.BuildServiceProvider();
    // Read stmp settings
    IOptionsMonitor<SmtpSettings> options = services.GetRequiredService<IOptionsMonitor<SmtpSettings>>();
    
    /// <summary>"Smtp" settings</summary>
    /// <example>
    /// appsettings.yaml:
    /// 
    /// Smtp:
    ///    UserName: contact@server
    ///    Server: server
    ///    Port: 465
    ///    Password: password here
    ///    FromAddress noreply@server
    ///    Tls: true
    /// </example>
    public record SmtpSettings
    {
        /// <summary></summary>
        public string? Server { get; set; }
        /// <summary></summary>
        public int Port { get; set; }
        /// <summary></summary>
        public string? FromAddress { get; set; }
        /// <summary></summary>
        public string? UserName { get; set; }
        /// <summary></summary>
        public string? Password { get; set; }
        /// <summary></summary>
        public bool? Tls { get; set; }
    }
    

    Modifications are saved to a file and then automatically reloaded. IOptionsMonitor receives a change event.

    // Create new password
    string newPassword = $"NewPassword-{new Random().Next(0, 1000)}";
    // Create modified version of settings
    SmtpSettings settingsChanged = options.CurrentValue with { Password = newPassword, UserName = "NewSmtpUserName@server" };
    // Convert to key-values
    List<KeyValuePair<string, string?>> keyValues = ConfigurationBindingExtensions.BindAsKeyValues(settingsChanged, typeof(SmtpSettings), path: "Smtp").ToList();
    
    // Create yaml stream
    YamlStream yamlStream = new YamlStream();
    // Place here the document where code-managed changes are saved
    YamlDocument? codegeneratedDocument = null!;
    // Read existing .yaml file
    if (File.Exists(settingsFile))
    {
        using Stream stream = new FileStream(settingsFile, FileMode.Open, FileAccess.Read);
        using StreamReader reader = new StreamReader(stream);
        yamlStream.Load(reader);
        codegeneratedDocument = yamlStream.Documents.FirstOrDefault(d => d.RootNode.Anchor == "AppManaged");
    }
    // Create-or-update codegenerated root
    YamlNode root = YamlNodeExtensions.ToYaml(
        properties: keyValues,
        policy: YamlNodeExtensions.CorrelatePolicy.RemoveUnusedNodes | YamlNodeExtensions.CorrelatePolicy.RemoveAmbiguentNodes,
        targetRoot: codegeneratedDocument?.RootNode,
        delimiter: ':');
    // Indicate that this root is managed by code
    root.Anchor = "AppManaged";
    // Create new document
    if (codegeneratedDocument == null)
    {
        codegeneratedDocument = new YamlDocument(root);
        yamlStream.Documents.Add(codegeneratedDocument);
    }
    
    // Add change listener (before save)
    options.OnChange((SmtpSettings newSettings) => Console.WriteLine($"Settings were modified, password = {newSettings.Password}"));
    
    // Save 
    {
        using Stream stream = new FileStream(settingsFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
        using StreamWriter writer = new StreamWriter(stream);
        yamlStream.Save(writer, assignAnchors: false);
    }
    
    // Wait for options to reload changes (token listener could also be used)
    Thread.Sleep(5000);
    WriteLine(options.CurrentValue.Password);
    
    // "Settings were modified, password = NewPassword-128"
    // "NewPassword - 128"
    

    appsettings-saved.yml:

    &AppManaged
    Smtp:
      FromAddress: ''
      Password: NewPassword-849
      Port: 0
      Server: ''
      Tls: ''
      UserName: NewSmtpUserName@server
    ...
    

    appsettings-saved.yml can contain multiple document parts. Only one part is managed by code. Other parts can be managed by user with text editor:

    # This part is managed by the application. 
    # It can be modified externally, but the structure may be modified by the application.
    &AppManaged
    Smtp:
      FromAddress: ''
      Password: NewPassword-679
      Port: 0
      Server: ''
      Tls: ''
      UserName: NewSmtpUserName@server
    ...
    ---
    # This part is not code-generated and can be edited externally.
    Key: Value
    ...
    

    Full Example

    Full example
    using Avalanche.Configuration;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Options;
    using YamlDotNet.RepresentationModel;
    using static System.Console;
    
    public class saving
    {
        public static void Run()
        {
            {
                // <01>
                // Settings file
                string settingsFile = "appsettings-saved.yaml";
                // Create configuration
                IConfigurationRoot configuration = new ConfigurationBuilder()
                    .AddYamlFile(settingsFile, optional: true, reloadOnChange: true)
                    .Build();
                // Create dependency injection
                IServiceCollection serviceCollection = new ServiceCollection()
                    .AddSingleton<IConfiguration>(configuration);
                serviceCollection.AddOptions<SmtpSettings>().BindConfiguration("Smtp");
                IServiceProvider services = serviceCollection.BuildServiceProvider();
                // Read stmp settings
                IOptionsMonitor<SmtpSettings> options = services.GetRequiredService<IOptionsMonitor<SmtpSettings>>();
                // </01>
    
                // <02>
                // Create new password
                string newPassword = $"NewPassword-{new Random().Next(0, 1000)}";
                // Create modified version of settings
                SmtpSettings settingsChanged = options.CurrentValue with { Password = newPassword, UserName = "NewSmtpUserName@server" };
                // Convert to key-values
                List<KeyValuePair<string, string?>> keyValues = ConfigurationBindingExtensions.BindAsKeyValues(settingsChanged, typeof(SmtpSettings), path: "Smtp").ToList();
    
                // Create yaml stream
                YamlStream yamlStream = new YamlStream();
                // Place here the document where code-managed changes are saved
                YamlDocument? codegeneratedDocument = null!;
                // Read existing .yaml file
                if (File.Exists(settingsFile))
                {
                    using Stream stream = new FileStream(settingsFile, FileMode.Open, FileAccess.Read);
                    using StreamReader reader = new StreamReader(stream);
                    yamlStream.Load(reader);
                    codegeneratedDocument = yamlStream.Documents.FirstOrDefault(d => d.RootNode.Anchor == "AppManaged");
                }
                // Create-or-update codegenerated root
                YamlNode root = YamlNodeExtensions.ToYaml(
                    properties: keyValues,
                    policy: YamlNodeExtensions.CorrelatePolicy.RemoveUnusedNodes | YamlNodeExtensions.CorrelatePolicy.RemoveAmbiguentNodes,
                    targetRoot: codegeneratedDocument?.RootNode,
                    delimiter: ':');
                // Indicate that this root is managed by code
                root.Anchor = "AppManaged";
                // Create new document
                if (codegeneratedDocument == null)
                {
                    codegeneratedDocument = new YamlDocument(root);
                    yamlStream.Documents.Add(codegeneratedDocument);
                }
    
                // Add change listener (before save)
                options.OnChange((SmtpSettings newSettings) => Console.WriteLine($"Settings were modified, password = {newSettings.Password}"));
    
                // Save 
                {
                    using Stream stream = new FileStream(settingsFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
                    using StreamWriter writer = new StreamWriter(stream);
                    yamlStream.Save(writer, assignAnchors: false);
                }
    
                // Wait for options to reload changes (token listener could also be used)
                Thread.Sleep(5000);
                WriteLine(options.CurrentValue.Password);
    
                // "Settings were modified, password = NewPassword-128"
                // "NewPassword - 128"
                // </02>
            }
        }
    }
    
    // <03>
    /// <summary>"Smtp" settings</summary>
    /// <example>
    /// appsettings.yaml:
    /// 
    /// Smtp:
    ///    UserName: contact@server
    ///    Server: server
    ///    Port: 465
    ///    Password: password here
    ///    FromAddress noreply@server
    ///    Tls: true
    /// </example>
    public record SmtpSettings
    {
        /// <summary></summary>
        public string? Server { get; set; }
        /// <summary></summary>
        public int Port { get; set; }
        /// <summary></summary>
        public string? FromAddress { get; set; }
        /// <summary></summary>
        public string? UserName { get; set; }
        /// <summary></summary>
        public string? Password { get; set; }
        /// <summary></summary>
        public bool? Tls { get; set; }
    }
    // </03>
    
    In This Article
    Back to top Copyright © Toni Kalajainen