Need a real exemlpe in using Microsoft.ApplicaitonBlock.ConfigurationManager

The help file is not very clear.

I would like to read write to a XML Config File.
Encrypted if it is easy to achieve.

But I really want to use the application block because it will be more complicated in the future.
For now, I just try to understand with a small config.

If you need more info let me know.
Yann de ChamplainAssociateAsked:
Who is Participating?
 
gregoryyoungConnect With a Mentor Commented:
its alot to take in but its very powerful.
0
 
Éric MoreauSenior .Net ConsultantCommented:
0
 
Yann de ChamplainAssociateAuthor Commented:
Thank you but this is not was I am looking for.
Need a real exemlpe in using Microsoft.ApplicationBlock.ConfigurationManagement.
0
Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
Éric MoreauSenior .Net ConsultantCommented:
I am not sure that this AB gives all the features you request. That's why I gave you this other link.
0
 
Yann de ChamplainAssociateAuthor Commented:
ok. Thank anyway.
0
 
Yann de ChamplainAssociateAuthor Commented:
Thank but this is not what I am looking for.
This page list the ApplicationBlock's pachage content.  So I already have that.
0
 
gregoryyoungCommented:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/cmab.asp

includes some information on use ....


yes the configuration management block does all of this ...



btw:

look where you installed it ...
"The download also contains the following QuickStart projects:

Microsoft.ApplicationBlocks.ConfigurationManagement.Quickstarts.Read
This QuickStart shows how to read data from a variety of data stores using the Configuration Management Application Block.

Microsoft.ApplicationBlocks.ConfigurationManagement.Quickstarts.Write
This QuickStart shows how to write data to a variety of data stores using the Configuration Management Application Block.

Microsoft.ApplicationBlocks.ConfigurationManagement.Quickstarts.Hashtable
This QuickStart demonstrates the special support built into the Configuration Management Application Block for application configuration data stored in Hashtable objects. "
0
 
Yann de ChamplainAssociateAuthor Commented:
Thank but again, I am lost with the exemple from the application block.
The code required to retreive a field is bigger than the whole application. That is why I am looking for a real implementation.

If I can't find, I'll delete this Class Libray from our namespace and create a regular one to read write config to a simple xml file.
0
 
gregoryyoungCommented:
// ===============================================================================
// Microsoft Configuration Management Application Block for .NET
// http://msdn.microsoft.com/library/en-us/dnbda/html/cmab.asp
//
// Form1.cs
//
// The form for the Read Quickstart
//
// For more information see the Configuration Management Application Block Implementation Overview.
//
// ===============================================================================
// Copyright (C) 2000-2001 Microsoft Corporation
// All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR
// FITNESS FOR A PARTICULAR PURPOSE.
// ==============================================================================

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Configuration;
using System.Reflection;
using Microsoft.Win32;

using Microsoft.ApplicationBlocks.ConfigurationManagement;
 
namespace Microsoft.ApplicationBlocks.ConfigurationManagement.Quickstarts.Read
{
      /// <summary>
      /// Summary description for Form1.
      /// </summary>
      public class Form1 : System.Windows.Forms.Form
      {
            private System.Windows.Forms.TextBox colorText;
            private System.Windows.Forms.TextBox sizeText;
            private System.Windows.Forms.Label label1;
            private System.Windows.Forms.Label label2;
            private System.Windows.Forms.Button button2;
            private System.Windows.Forms.Label label3;
            private System.Windows.Forms.TextBox someText;
            private System.Windows.Forms.ComboBox configSection;
            /// <summary>
            /// Required designer variable.
            /// </summary>
            private System.ComponentModel.Container components = null;
            internal System.Windows.Forms.CheckBox readOnlySection;
            private const string COLON = ":";

            public Form1()
            {
                  //
                  // Required for Windows Form Designer support
                  //
                  InitializeComponent();

                  // Initialize the registry values for the Quickstarts
                  try
                  {
                        using( RegistryKey regKey = Registry.LocalMachine.OpenSubKey( @"Software\Microsoft", true ) )
                        {
                              RegistryKey configMgmt = regKey.OpenSubKey( "ConfigurationManagement", true );
                              if( configMgmt == null )
                              {
                                    configMgmt = regKey.CreateSubKey( "ConfigurationManagement" );
                                    configMgmt.SetValue( "symmetricKey", "VToaqZjp8C27V90oSmT/CF+afvRGClc9" );
                                    configMgmt.SetValue( "initializationVector", "ou95G2/WziI=" );
                                    configMgmt.SetValue( "hashKey", "MyXuEd6f+go=" );
                                    configMgmt.SetValue( "connectionString", "server=localhost;database=cmab;Integrated Security=SSPI" );
                              }

                              using( configMgmt )
                              {
                                    RegistryKey encryptedRegistry = configMgmt.OpenSubKey( "EncryptedRegistry", true );
                                    if( encryptedRegistry == null )
                                    {
                                          encryptedRegistry = configMgmt.CreateSubKey( "EncryptedRegistry" );
                                          using( encryptedRegistry )
                                          {
                                                encryptedRegistry.SetValue( "value", "fLivcJGbFoqW+di0aSZk1vr+KxfpKDwhxOEzjk+nTOe4ztloqqE9CT9G8HDjxhblUiQfRNuxYGhuBhb1u5zceipZ8FMAkfgbT3d8m7r0Iovq+34cp4zdUo7CwIbgSB/Ucm+PYC4Y9uYRVxswSCvPI4+c2zVkn/erQO6zObpLSwaUfemiRawf1/Q42QqNhi5PCX8QPy7dIUZqjimaXw/vqgV6SdXqN53/Qt+rrcRQvrrzyZd31e4myKqB+eAxyteEK9+rVLMb+DcWztg91V9xiY+d+vA7zrybyK9us4Mut7U=" );
                                                encryptedRegistry.SetValue( "signature", "" );
                                          }
                                    }
                                    else
                                    {
                                          encryptedRegistry.Close();
                                    }
                              
                                    RegistryKey unencryptedRegistry = configMgmt.OpenSubKey( "UnencryptedRegistry", true );
                                    if( unencryptedRegistry == null )
                                    {
                                          unencryptedRegistry = configMgmt.CreateSubKey( "UnencryptedRegistry" );
                                          using( unencryptedRegistry )
                                          {
                                                unencryptedRegistry.SetValue( "value", "<CustomConfigurationData xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><Color>Goldenron</Color><SomeText>FOO FOO FOO!</SomeText><Size>42</Size></CustomConfigurationData>" );
                                                unencryptedRegistry.SetValue( "signature", "" );
                                          }
                                    }
                                    else
                                    {
                                          unencryptedRegistry.Close();
                                    }
                              }
                        }
                  }
                  catch( Exception e )
                  {
                        throw new Exception( "Error creating registry keys for the Quickstart samples: " + e.Message, e );
                  }

                  try
                  {
                        ConfigurationManager.Initialize();
                  }
                  catch( Exception ex )
                  {
                        string message = "";
                        for( Exception tempException = ex; tempException != null; tempException = tempException.InnerException )
                        {
                              message += tempException.Message + Environment.NewLine + "----------" + Environment.NewLine;
                        }
                        MessageBox.Show( message );
                  }
            }


            /// <summary>
            /// Clean up any resources being used.
            /// </summary>
            protected override void Dispose( bool disposing )
            {
                  if( disposing )
                  {
                        if (components != null)
                        {
                              components.Dispose();
                        }
                  }
                  base.Dispose( disposing );
            }


            #region Windows Form Designer generated code
            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            private void InitializeComponent()
            {
                  this.colorText = new System.Windows.Forms.TextBox();
                  this.sizeText = new System.Windows.Forms.TextBox();
                  this.label1 = new System.Windows.Forms.Label();
                  this.label2 = new System.Windows.Forms.Label();
                  this.button2 = new System.Windows.Forms.Button();
                  this.label3 = new System.Windows.Forms.Label();
                  this.someText = new System.Windows.Forms.TextBox();
                  this.configSection = new System.Windows.Forms.ComboBox();
                  this.readOnlySection = new System.Windows.Forms.CheckBox();
                  this.SuspendLayout();
                  //
                  // colorText
                  //
                  this.colorText.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.colorText.Location = new System.Drawing.Point(72, 16);
                  this.colorText.Name = "colorText";
                  this.colorText.Size = new System.Drawing.Size(520, 20);
                  this.colorText.TabIndex = 1;
                  this.colorText.Text = "";
                  //
                  // sizeText
                  //
                  this.sizeText.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.sizeText.Location = new System.Drawing.Point(72, 48);
                  this.sizeText.Name = "sizeText";
                  this.sizeText.Size = new System.Drawing.Size(520, 20);
                  this.sizeText.TabIndex = 3;
                  this.sizeText.Text = "";
                  //
                  // label1
                  //
                  this.label1.Location = new System.Drawing.Point(8, 16);
                  this.label1.Name = "label1";
                  this.label1.Size = new System.Drawing.Size(168, 23);
                  this.label1.TabIndex = 4;
                  this.label1.Text = "Color";
                  //
                  // label2
                  //
                  this.label2.Location = new System.Drawing.Point(8, 48);
                  this.label2.Name = "label2";
                  this.label2.Size = new System.Drawing.Size(168, 23);
                  this.label2.TabIndex = 5;
                  this.label2.Text = "Size";
                  //
                  // button2
                  //
                  this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.button2.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
                  this.button2.Location = new System.Drawing.Point(496, 120);
                  this.button2.Name = "button2";
                  this.button2.Size = new System.Drawing.Size(120, 88);
                  this.button2.TabIndex = 0;
                  this.button2.Text = "Get";
                  this.button2.Click += new System.EventHandler(this.button2_Click);
                  //
                  // label3
                  //
                  this.label3.Location = new System.Drawing.Point(8, 80);
                  this.label3.Name = "label3";
                  this.label3.Size = new System.Drawing.Size(168, 23);
                  this.label3.TabIndex = 5;
                  this.label3.Text = "Some text";
                  //
                  // someText
                  //
                  this.someText.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.someText.Location = new System.Drawing.Point(72, 80);
                  this.someText.Name = "someText";
                  this.someText.Size = new System.Drawing.Size(520, 20);
                  this.someText.TabIndex = 3;
                  this.someText.Text = "";
                  //
                  // configSection
                  //
                  this.configSection.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                        | System.Windows.Forms.AnchorStyles.Left)
                        | System.Windows.Forms.AnchorStyles.Right)));
                  this.configSection.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
                  this.configSection.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
                  this.configSection.Items.AddRange(new object[] {
                                                                                             "EncryptedSql: Encrypted in SQL Server",
                                                                                             "UnencryptedSql: Unencrypted SQL Server",
                                                                                             "EncryptedXml: Encrypted Xml File (config)",
                                                                                             "UnencryptedXml: Unencrypted Xml File (config)",
                                                                                             "EncryptedRegistry: Encrypted Registry Reader",
                                                                                             "UnencryptedRegistry: Unencrypted Registry Reader",
                                                                                             "XmlSerializer: XmlFile using generic section handler",
                                                                                             "OtherConfigFile: Unencrypted Xml using other config file"});
                  this.configSection.Location = new System.Drawing.Point(8, 120);
                  this.configSection.Name = "configSection";
                  this.configSection.Size = new System.Drawing.Size(456, 24);
                  this.configSection.TabIndex = 6;
                  //
                  // readOnlySection
                  //
                  this.readOnlySection.Enabled = false;
                  this.readOnlySection.Location = new System.Drawing.Point(8, 152);
                  this.readOnlySection.Name = "readOnlySection";
                  this.readOnlySection.Size = new System.Drawing.Size(144, 24);
                  this.readOnlySection.TabIndex = 10;
                  this.readOnlySection.Text = "IsReadOnly section";
                  //
                  // Form1
                  //
                  this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
                  this.ClientSize = new System.Drawing.Size(632, 214);
                  this.Controls.Add(this.readOnlySection);
                  this.Controls.Add(this.configSection);
                  this.Controls.Add(this.someText);
                  this.Controls.Add(this.sizeText);
                  this.Controls.Add(this.colorText);
                  this.Controls.Add(this.label2);
                  this.Controls.Add(this.label1);
                  this.Controls.Add(this.button2);
                  this.Controls.Add(this.label3);
                  this.Name = "Form1";
                  this.Text = "Form1";
                  this.ResumeLayout(false);

            }
            #endregion

            /// <summary>
            /// The main entry point for the application.
            /// </summary>
            [STAThread]
            static void Main()
            {                  
                  Application.Run(new Form1());
            }


            private void button2_Click(object sender, System.EventArgs e)
            {
                  try
                  {
                  if( configSection.SelectedIndex != -1 )
                  {
                        string souceSection = ((string)configSection.SelectedItem).Split(COLON.ToCharArray())[0];
                        CustomConfigurationData cclass = (CustomConfigurationData)ConfigurationManager.Read( souceSection );
                        if( cclass == null )
                              throw new ApplicationException( "testapp:no config on provider." );

                        colorText.Text = cclass.Color;
                        sizeText.Text = cclass.Size.ToString( System.Globalization.CultureInfo.CurrentUICulture );
                        someText.Text = cclass.SomeText;

                        readOnlySection.Checked = ConfigurationManager.IsReadOnly( souceSection );
                  }
                  else
                  {
                        MessageBox.Show( "Must select the source section." );
                  }
                  }
                  catch( Exception ex )
                  {
                        string message = "";
                        for( Exception tempException = ex; tempException != null; tempException = tempException.InnerException )
                        {
                              message += tempException.Message + Environment.NewLine + "----------" + Environment.NewLine;
                        }
                        MessageBox.Show( message );
                  }
            }
      }
}

are you referring to this ?
0
 
Yann de ChamplainAssociateAuthor Commented:
yes this and other files. and all the XML sections requested by the class library.
Seems that I also need CustomConfigurationData.cs, app.config, CustomSectionHandler.cs
I think I need to sleep a little and start over ;-)
0
 
gregoryyoungCommented:
ummm all of the other code is the code to the library (it includes source) you dont need to change any of that.
0
 
Yann de ChamplainAssociateAuthor Commented:
Ok. I progressed a little.  Now I get a Invalid cast from the line

private Hashtable _configData;
_configData = (Hashtable)ConfigurationManager.Read("UnencryptedXml");

Here is my app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
      <configSections>
            <section
                  name="applicationConfigurationManagement"
                  type="CompanyName.Services.ConfigurationManagement.ConfigurationManagerSectionHandler,CompanyName.Services.ConfigurationManagement, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null" />
            
            <section
                        name="EncryptedXml"  
                        type="TSBackup.CustomSectionHandler,TSBackup, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null" />
            <section
                        name="UnencryptedXml"
                        type="TSBackup.CustomSectionHandler,TSBackup, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null" />
      </configSections>

    <EncryptedXml>
        <signature>
        </signature>
        <encryptedData>fLivcJGbFoqW+di0aSZk1vr+KxfpKDwhxOEzjk+nTOe4ztloqqE9CT9G8HDjxhblUiQfRNuxYGhuBhb1u5zceipZ8FMAkfgbT3d8m7r0Iovq+34cp4zdUo7CwIbgSB/Ucm+PYC4Y9uYRVxswSCvPI4+c2zVkn/erQO6zObpLSwaUfemiRawf1/Q42QqNhi5PCX8QPy7dIUZqjimaXw/vqgV6SdXqN53/Qt+rrcRQvrrzyZd31e4myKqB+eAxyteEK9+rVLMb+DcWztg91V9xiY+d+vA7zrybyK9us4Mut7U=</encryptedData>
    </EncryptedXml>
 
      <UnencryptedXml>
                        <CustomConfigurationData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                              <Color>Burnt Orange</Color>
                              <SomeText>FROM THE CONFIG FILE, not encrypted</SomeText>
                              <Size>3245</Size>
                        </CustomConfigurationData>      
      </UnencryptedXml>
      
      <applicationConfigurationManagement defaultSection="UnencryptedXml" >
            <configSection name="EncryptedXml">
                  <configCache enabled="false" refresh="1 * * * *" />
                  <configProvider             assembly="CompanyName.Services.ConfigurationManagement,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"
                                                      type="CompanyName.Services.ConfigurationManagement.Storage.XmlFileStorage"
                                                      signed="false"
                                                      refreshOnChange="false"
                                                      encrypted="true" />
                  <protectionProvider       assembly="CompanyName.Services.ConfigurationManagement,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"
                                                      type="CompanyName.Services.ConfigurationManagement.DataProtection.BCLDataProtection"
                                                      hashKey="MyXuEd6f+go=" symmetricKey="VToaqZjp8C27V90oSmT/CF+afvRGClc9" initializationVector="ou95G2/WziI="/>
            </configSection>
                  
            <configSection name="UnencryptedXml">
                  <configCache enabled="true" refresh="1 * * * *" />
                  <configProvider             assembly="CompanyName.Services.ConfigurationManagement,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"
                                                      type="CompanyName.Services.ConfigurationManagement.Storage.XmlFileStorage"
                                                      signed="false"
                                                      refreshOnChange="true"
                                                      encrypted="false" />
                  <protectionProvider       assembly="CompanyName.Services.ConfigurationManagement,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"
                                                      type="CompanyName.Services.ConfigurationManagement.DataProtection.BCLDataProtection"
                                                      hashKey="MyXuEd6f+go=" symmetricKey="VToaqZjp8C27V90oSmT/CF+afvRGClc9" initializationVector="ou95G2/WziI="/>
            </configSection>

      </applicationConfigurationManagement>
</configuration>


0
 
gregoryyoungCommented:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
      <configSections>
            <section
                  name="applicationConfigurationManagement"
                  type="Microsoft.ApplicationBlocks.ConfigurationManagement.ConfigurationManagerSectionHandler,Microsoft.ApplicationBlocks.ConfigurationManagement, Version=1.0.0.0,Culture=neutral,PublicKeyToken=null" />
            
            <section
                  name="ApplConfig1"  
                  type="Microsoft.ApplicationBlocks.ConfigurationManagement.XmlHashtableSectionHandler,Microsoft.ApplicationBlocks.ConfigurationManagement,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null" />
                                                
      </configSections>
   
  <ApplConfig1>
    <XmlSerializableHashtable xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <Entries>
        <Entry>
          <key xsi:type="xsd:string">freaky</key>
          <value xsi:type="xsd:string">chakra</value>
        </Entry>
        <Entry>
          <key xsi:type="xsd:string">killer</key>
          <value xsi:type="xsd:string">DILLER</value>
        </Entry>
        <Entry>
          <key xsi:type="xsd:string">foo</key>
          <value xsi:type="xsd:string">bar</value>
        </Entry>
        <Entry>
          <key xsi:type="xsd:string">depeche</key>
          <value xsi:type="xsd:string">mode</value>
        </Entry>
        <Entry>
          <key xsi:type="xsd:string">Bubba</key>
          <value xsi:type="xsd:string">Gump</value>
        </Entry>
      </Entries>
    </XmlSerializableHashtable>
  </ApplConfig1>
 
      
      <applicationConfigurationManagement >
            <configSection name="ApplConfig1">
                  <configCache                   enabled="true"
                                                      refresh="1 * * * *" />
                  <configProvider             assembly="Microsoft.ApplicationBlocks.ConfigurationManagement,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"
                                                      type="Microsoft.ApplicationBlocks.ConfigurationManagement.Storage.XmlFileStorage"
                                                      
                                                      refreshOnChange="false"
                                                      signed="false"
                                                      encrypted="false" />

                  <protectionProvider       assembly="Microsoft.ApplicationBlocks.ConfigurationManagement,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"
                                                      type="Microsoft.ApplicationBlocks.ConfigurationManagement.DataProtection.BCLDataProtection"
                                                      hashKeyRegistryPath="" hashKey="MyXuEd6f+go=" symmetricKey="VToaqZjp8C27V90oSmT/CF+afvRGClc9" initializationVector="ou95G2/WziI="/>
                                                      
            </configSection>
      </applicationConfigurationManagement>
</configuration>

is from the example .. notice the different usage of the hashtable ...
0
 
Yann de ChamplainAssociateAuthor Commented:
I found my problem I think.

I was waiting for a HAshTable but the exemple section UnencryptedXML return a CustomConfigurationData type.  
This is why I has to have this file to map the value in the object properties.

I'll start over using the other one.
0
 
Yann de ChamplainAssociateAuthor Commented:
I was using the read exemple not the hashtable. And in the help file they say that we need to receive a hashtable. Than I started to mix both together.
0
 
Yann de ChamplainAssociateAuthor Commented:
Ya it seems.

Could you just confirm that If I dont want to learn the whole Class Library, I am in the good direction just to use the Hashtable type Exemple and leave the others on the side?
0
 
gregoryyoungCommented:
it would be reasonable for the time being it does not sound to me like you will need the full functionality from the beginning. You can always learn the rest later :)
0
 
Yann de ChamplainAssociateAuthor Commented:
Ya sure.
thank again.
0
 
gregoryyoungCommented:
btw: if you have MSDN meetings coming through your area one of their current meetings discusses this application  block
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.