রানটাইম এ ডিফল্ট app.config পরিবর্তন করুন


130

আমার নিম্নলিখিত সমস্যা রয়েছে:
আমাদের একটি অ্যাপ্লিকেশন রয়েছে যা মডিউলগুলি লোড করে (অনগুলি যোগ করে)। এই মডিউলগুলির জন্য app.config এ প্রবেশের প্রয়োজন হতে পারে (যেমন ডাব্লুসিএফ কনফিগারেশন)। মডিউলগুলি গতিশীলভাবে লোড হওয়ার কারণে, আমি আমার অ্যাপ্লিকেশনটির অ্যাপ্লিকেশন কনফিগ ফাইলে এই এন্ট্রি রাখতে চাই না।
আমি যা করতে চাই তা হল:

  • মেমোরিতে একটি নতুন app.config তৈরি করুন যা মডিউলগুলি থেকে কনফিগার বিভাগগুলি অন্তর্ভুক্ত করে
  • আমার অ্যাপ্লিকেশনটিকে সেই নতুন app.config ব্যবহার করতে বলুন

দ্রষ্টব্য: আমি ডিফল্ট app.config ওভাররাইট করতে চাই না!

এটি স্বচ্ছভাবে কাজ করা উচিত, যাতে উদাহরণস্বরূপ ConfigurationManager.AppSettingsযে নতুন ফাইলটি ব্যবহার করে।

এই সমস্যাটি সম্পর্কে আমার মূল্যায়নের সময়, আমি এখানে সরবরাহ করা একই সমাধানটি নিয়ে এসেছি: নুনিটের সাথে app.config পুনরায় লোড করুন
দুর্ভাগ্যক্রমে, এটি কিছু করার মতো বলে মনে হচ্ছে না, কারণ আমি এখনও সাধারণ অ্যাপকনফাইগ থেকে ডেটা পাই।

আমি এটি পরীক্ষা করার জন্য এই কোডটি ব্যবহার করেছি:

Console.WriteLine(ConfigurationManager.AppSettings["SettingA"]);
Console.WriteLine(Settings.Default.Setting);

var combinedConfig = string.Format(CONFIG2, CONFIG);
var tempFileName = Path.GetTempFileName();
using (var writer = new StreamWriter(tempFileName))
{
    writer.Write(combinedConfig);
}

using(AppConfig.Change(tempFileName))
{
    Console.WriteLine(ConfigurationManager.AppSettings["SettingA"]);
    Console.WriteLine(Settings.Default.Setting);
}

এটি একই অ্যাপ্লিকেশন কনফিগের combinedConfigচেয়ে অন্য মানগুলি ছাপিয়ে একই মানগুলিকে দু'বার মুদ্রণ করে ।


AppDomainউপযুক্ত কনফিগারেশন ফাইলের সাথে পৃথক করে মডিউলগুলি হোস্ট করা কোনও বিকল্প নয়?
জোও অ্যাঞ্জেলো

আসলেই নয়, কারণ এতে প্রচুর ক্রস-অ্যাপডোমাইন কল আসে, কারণ অ্যাপ্লিকেশনটি মডিউলগুলির সাথে বেশ ভারীভাবে ইন্টারেক্ট করে।
ড্যানিয়েল হিলগারথ

একটি নতুন মডিউল লোড করার প্রয়োজন হলে কোনও অ্যাপ্লিকেশন পুনরায় চালু হবে?
জোও অ্যাঞ্জেলো

এটি ব্যবসায়ের প্রয়োজনীয়তার সাথে একসাথে কাজ করে না। তদ্ব্যতীত, আমি app.config ওভাররাইট করতে পারি না, কারণ ব্যবহারকারীর এটি করার অধিকার নেই।
ড্যানিয়েল হিলগারথ

আপনি প্রোগ্রাম ফাইলগুলির মধ্যে একটি নয়, আলাদা অ্যাপকনফাইগটি লোড করতে পুনরায় লোড করবেন। হ্যাক ইন Reload app.config with nunitকাজ করতে পারে, নিশ্চিত নয়, কোনও কনফিগারেশন লোড হওয়ার আগে অ্যাপ্লিকেশন এন্ট্রি ব্যবহার করা হয়।
জোও অ্যাঞ্জেলো

উত্তর:


280

সংযুক্ত প্রশ্নের হ্যাক কাজ করে যদি এটি কনফিগারেশন সিস্টেমটি প্রথমবার ব্যবহারের আগে ব্যবহার করা হয়। এর পরে, এটি আর কাজ করে না।
কারণ:
একটি শ্রেণি রয়েছে ClientConfigPathsযা পথগুলিকে ক্যাশে করে। সুতরাং, পথটি পরিবর্তনের পরেও SetDataএটি পুনরায় পড়া হয় না, কারণ ইতিমধ্যে ক্যাশেড মান রয়েছে। সমাধানগুলি হ'ল এটিও সরিয়ে ফেলা:

using System;
using System.Configuration;
using System.Linq;
using System.Reflection;

public abstract class AppConfig : IDisposable
{
    public static AppConfig Change(string path)
    {
        return new ChangeAppConfig(path);
    }

    public abstract void Dispose();

    private class ChangeAppConfig : AppConfig
    {
        private readonly string oldConfig =
            AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE").ToString();

        private bool disposedValue;

        public ChangeAppConfig(string path)
        {
            AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", path);
            ResetConfigMechanism();
        }

        public override void Dispose()
        {
            if (!disposedValue)
            {
                AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", oldConfig);
                ResetConfigMechanism();


                disposedValue = true;
            }
            GC.SuppressFinalize(this);
        }

        private static void ResetConfigMechanism()
        {
            typeof(ConfigurationManager)
                .GetField("s_initState", BindingFlags.NonPublic | 
                                         BindingFlags.Static)
                .SetValue(null, 0);

            typeof(ConfigurationManager)
                .GetField("s_configSystem", BindingFlags.NonPublic | 
                                            BindingFlags.Static)
                .SetValue(null, null);

            typeof(ConfigurationManager)
                .Assembly.GetTypes()
                .Where(x => x.FullName == 
                            "System.Configuration.ClientConfigPaths")
                .First()
                .GetField("s_current", BindingFlags.NonPublic | 
                                       BindingFlags.Static)
                .SetValue(null, null);
        }
    }
}

ব্যবহার এই রকম:

// the default app.config is used.
using(AppConfig.Change(tempFileName))
{
    // the app.config in tempFileName is used
}
// the default app.config is used.

আপনি যদি নিজের অ্যাপ্লিকেশনটির পুরো রানটাইমের জন্য ব্যবহৃত অ্যাপকনফিগটি পরিবর্তন করতে চান তবে আপনার অ্যাপ্লিকেশনটির AppConfig.Change(tempFileName)শুরুতে কোথাও ব্যবহার না করেই রাখুন।


4
এটি সত্যই, সত্যিই দুর্দান্ত। এই পোস্ট করার জন্য আপনাকে অনেক ধন্যবাদ।
user981225

3
@ ড্যানিয়েল এটি দুর্দান্ত ছিল - আমি এটিকে অ্যাপ্লিকেশনসটিজিং বেসের জন্য একটি এক্সটেনশন পদ্ধতিতে কাজ করেছি, যাতে আমি সেটিংস.ডেফল্ট.রেডরেক্ট অ্যাপকনফিগ (পথ) কল করতে পারি। আমি পারলে +2 দিতাম!
জেমার্চ

2
@ ফিলিউইটিংটন: আমি যা বলছি তা হ্যাঁ।
ড্যানিয়েল হিলগারথ

2
আগ্রহের বাইরে, চূড়ান্তকরণকারীকে দমন করার কোনও কারণ নেই, যেখানে কোনও ফাইনালাইজার ঘোষণা করা হয়নি?
গুসডোর

3
এদিকে, ব্যক্তিগত ক্ষেত্রগুলিতে অ্যাক্সেসের প্রতিচ্ছবি ব্যবহার করা এখন কাজ করতে পারে তবে এটি একটি সতর্কতা ব্যবহার করতে পারে যা এটি সমর্থিত নয় এবং ভবিষ্যতে .NET ফ্রেমওয়ার্কের সংস্করণগুলিতে ভেঙে যেতে পারে।

10

আপনি রানটাইমে কনফিগারেশন ব্যবহার এবং কনফিগারেশনসেকশন যুক্ত করার চেষ্টা করতে পারেন

Configuration applicationConfiguration = ConfigurationManager.OpenMappedExeConfiguration(
                        new ExeConfigurationFileMap(){ExeConfigFilename = path_to_your_config,
                        ConfigurationUserLevel.None
                        );

applicationConfiguration.Sections.Add("section",new YourSection())
applicationConfiguration.Save(ConfigurationSaveMode.Full,true);

সম্পাদনা: এখানে প্রতিফলনের উপর ভিত্তি করে সমাধান দেওয়া হয়েছে (যদিও খুব সুন্দর নয়)

থেকে প্রাপ্ত ক্লাস তৈরি করুন IInternalConfigSystem

public class ConfigeSystem: IInternalConfigSystem
{
    public NameValueCollection Settings = new NameValueCollection();
    #region Implementation of IInternalConfigSystem

    public object GetSection(string configKey)
    {
        return Settings;
    }

    public void RefreshConfig(string sectionName)
    {
        //throw new NotImplementedException();
    }

    public bool SupportsUserConfig { get; private set; }

    #endregion
}

তারপরে প্রতিচ্ছবিটির মাধ্যমে এটিকে প্রাইভেট ফিল্ডে সেট করুন ConfigurationManager

        ConfigeSystem configSystem = new ConfigeSystem();
        configSystem.Settings.Add("s1","S");

        Type type = typeof(ConfigurationManager);
        FieldInfo info = type.GetField("s_configSystem", BindingFlags.NonPublic | BindingFlags.Static);
        info.SetValue(null, configSystem);

        bool res = ConfigurationManager.AppSettings["s1"] == "S"; // return true

আমি দেখতে পাচ্ছি না এটি কীভাবে আমাকে সহায়তা করে। এটি নির্দিষ্ট করা ফাইলটিতে একটি বিভাগ যুক্ত করবে file_path। এটি বিভাগের ব্যবহারকারীদের জন্য উপলব্ধ অংশটি তৈরি করবে না ConfigurationManager.GetSectionকারণ GetSectionডিফল্ট অ্যাপ্লিকেশন কোডফিগ ব্যবহার করে।
ড্যানিয়েল হিলগারথ

আপনি আপনার বিদ্যমান app.config এ বিভাগগুলি যুক্ত করতে পারেন।
সবেমাত্র

আমার প্রশ্ন থেকে উদ্ধৃতি: "দ্রষ্টব্য: আমি ডিফল্ট app.config ওভাররাইট করতে চাই না!"
ড্যানিয়েল হিলগার্থ

5
কোনো সমস্যা? সরল: ব্যবহারকারীর এটিকে ওভাররাইট করার কোনও অধিকার নেই, কারণ প্রোগ্রামটি% প্রোগ্রামফায়ালস% এ ইনস্টল করা আছে এবং ব্যবহারকারী কোনও প্রশাসক নেই।
ড্যানিয়েল হিলগারথ

2
@ স্টেসিয়া: আপনার প্রচেষ্টার জন্য ধন্যবাদ। তবে দয়া করে সমস্যার আসল সমাধানের জন্য আমার উত্তরটি দেখুন।
ড্যানিয়েল হিলগারথ

5

@ ড্যানিয়েল সলিউশন ঠিক আছে। আরও ব্যাখ্যা সহ একটি অনুরূপ সমাধান সি-শার্প কোণে। সম্পূর্ণতার জন্য আমি আমার সংস্করণটি ভাগ করতে চাই: সহ using, এবং বিট পতাকা সংক্ষেপে।

using System;//AppDomain
using System.Linq;//Where
using System.Configuration;//app.config
using System.Reflection;//BindingFlags

    /// <summary>
    /// Use your own App.Config file instead of the default.
    /// </summary>
    /// <param name="NewAppConfigFullPathName"></param>
    public static void ChangeAppConfig(string NewAppConfigFullPathName)
    {
        AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", NewAppConfigFullPathName);
        ResetConfigMechanism();
        return;
    }

    /// <summary>
    /// Remove cached values from ClientConfigPaths.
    /// Call this after changing path to App.Config.
    /// </summary>
    private static void ResetConfigMechanism()
    {
        BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Static;
        typeof(ConfigurationManager)
            .GetField("s_initState", Flags)
            .SetValue(null, 0);

        typeof(ConfigurationManager)
            .GetField("s_configSystem", Flags)
            .SetValue(null, null);

        typeof(ConfigurationManager)
            .Assembly.GetTypes()
            .Where(x => x.FullName == "System.Configuration.ClientConfigPaths")
            .First()
            .GetField("s_current", Flags)
            .SetValue(null, null);
        return;
    }

4

যদি কারও আগ্রহী, এখানে একটি পদ্ধতি যা মনোতে কাজ করে।

string configFilePath = ".../App";
System.Configuration.Configuration newConfiguration = ConfigurationManager.OpenExeConfiguration(configFilePath);
FieldInfo configSystemField = typeof(ConfigurationManager).GetField("configSystem", BindingFlags.NonPublic | BindingFlags.Static);
object configSystem = configSystemField.GetValue(null);
FieldInfo cfgField = configSystem.GetType().GetField("cfg", BindingFlags.Instance | BindingFlags.NonPublic);
cfgField.SetValue(configSystem, newConfiguration);

3

ড্যানিয়েলের সমাধানটি এমনকি স্ট্রোড অ্যাসেমব্লিতেও কাজ করে বলে মনে হচ্ছে আমি অ্যাপডোমাইন.সেটডাটা আগে ব্যবহার করেছি, তবে অভ্যন্তরীণ কনফিগারেশন পতাকাগুলি কীভাবে পুনরায় সেট করতে হবে সে সম্পর্কে অসচেতন was

আগ্রহীদের জন্য সি ++ / সিএলআইতে রূপান্তরিত

/// <summary>
/// Remove cached values from ClientConfigPaths.
/// Call this after changing path to App.Config.
/// </summary>
void ResetConfigMechanism()
{
    BindingFlags Flags = BindingFlags::NonPublic | BindingFlags::Static;
    Type ^cfgType = ConfigurationManager::typeid;

    Int32 ^zero = gcnew Int32(0);
    cfgType->GetField("s_initState", Flags)
        ->SetValue(nullptr, zero);

    cfgType->GetField("s_configSystem", Flags)
        ->SetValue(nullptr, nullptr);

    for each(System::Type ^t in cfgType->Assembly->GetTypes())
    {
        if (t->FullName == "System.Configuration.ClientConfigPaths")
        {
            t->GetField("s_current", Flags)->SetValue(nullptr, nullptr);
        }
    }

    return;
}

/// <summary>
/// Use your own App.Config file instead of the default.
/// </summary>
/// <param name="NewAppConfigFullPathName"></param>
void ChangeAppConfig(String ^NewAppConfigFullPathName)
{
    AppDomain::CurrentDomain->SetData(L"APP_CONFIG_FILE", NewAppConfigFullPathName);
    ResetConfigMechanism();
    return;
}

1

যদি আপনার কনফিগারেশন ফাইলটি "অ্যাপসেটেটিংস" -তে কী / মান দিয়ে স্রেফ লেখা থাকে তবে আপনি এই জাতীয় কোড সহ অন্য কোনও ফাইলটি পড়তে পারেন:

System.Configuration.ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileMap.ExeConfigFilename = configFilePath;

System.Configuration.Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
AppSettingsSection section = (AppSettingsSection)configuration.GetSection("appSettings");

তারপরে আপনি বিভাগটি পড়তে পারেন Key কীভ্যালু কনফিগারেশন এলিমেন্টের সংগ্রহ হিসাবে সেটিংস।


1
আমি ইতিমধ্যে বলেছি, আমি ConfigurationManager.GetSectionআমার তৈরি নতুন ফাইলটি পড়তে চাই । আপনার সমাধানটি এটি করে না।
ড্যানিয়েল হিলগারথ

@ ড্যানিয়েল: কেন? আপনি "কনফিগারফিলপথ" এ যে কোনও ফাইল নির্দিষ্ট করতে পারেন। সুতরাং আপনার নতুন তৈরি করা ফাইলটির অবস্থানটি আপনার জানা উচিত। আমি কি কিছু রেখে গেলাম ? বা আপনার সত্যিই "কনফিগারেশন ম্যানেজার.গেটসেকশন" এবং অন্য কিছু ব্যবহার করার দরকার নেই?
রন

1
হ্যাঁ আপনি কিছু মিস করেন: ConfigurationManager.GetSectionডিফল্ট অ্যাপকনফিগ ব্যবহার করে। আপনি যে কনফিগার ফাইলটি দিয়ে খোলেন সে সম্পর্কে এটি বিবেচনা করে না OpenMappedExeConfiguration
ড্যানিয়েল হিলগারথ

1

বিস্ময়কর আলোচনা, আমি পদ্ধতিতে বিবৃতি / কল পিছনে যাদু বুঝতে রিসেটকনফিগমেকানিজম পদ্ধতিতে আরও মন্তব্য যুক্ত করেছি। যোগ করা ফাইল পাথ উপস্থিত চেক

using System;//AppDomain
using System.Linq;//Where
using System.Configuration;//app.config
using System.Reflection;//BindingFlags
using System.Io;

/// <summary>
/// Use your own App.Config file instead of the default.
/// </summary>
/// <param name="NewAppConfigFullPathName"></param>
public static void ChangeAppConfig(string NewAppConfigFullPathName)
{
    if(File.Exists(NewAppConfigFullPathName)
    {
      AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", 
      NewAppConfigFullPathName);
      ResetConfigMechanism();
      return;
    }
}

/// <summary>
/// Remove cached values from ClientConfigPaths.
/// Call this after changing path to App.Config.
/// </summary>
private static void ResetConfigMechanism()
{
    BindingFlags Flags = BindingFlags.NonPublic | BindingFlags.Static;
      /* s_initState holds one of the four internal configuration state.
          0 - Not Started, 1 - Started, 2 - Usable, 3- Complete

         Setting to 0 indicates the configuration is not started, this will 
         hint the AppDomain to reaload the most recent config file set thru 
         .SetData call
         More [here][1]

      */
    typeof(ConfigurationManager)
        .GetField("s_initState", Flags)
        .SetValue(null, 0);


    /*s_configSystem holds the configuration section, this needs to be set 
        as null to enable reload*/
    typeof(ConfigurationManager)
        .GetField("s_configSystem", Flags)
        .SetValue(null, null);

      /*s_current holds the cached configuration file path, this needs to be 
         made null to fetch the latest file from the path provided 
        */
    typeof(ConfigurationManager)
        .Assembly.GetTypes()
        .Where(x => x.FullName == "System.Configuration.ClientConfigPaths")
        .First()
        .GetField("s_current", Flags)
        .SetValue(null, null);
    return;
}

0

ড্যানিয়েল, সম্ভব হলে অন্যান্য কনফিগারেশন পদ্ধতি ব্যবহার করার চেষ্টা করুন। আমরা এই রুট দিয়ে এসেছি যেখানে আমাদের পরিবেশ / প্রোফাইল / গোষ্ঠীর উপর নির্ভর করে বিভিন্ন স্ট্যাটিক / ডায়নামিক কনফিগারেশন ফাইল ছিল এবং এটি শেষে বেশ অগোছালো হয়ে যায়।

আপনি কোনও প্রকারের প্রোফাইল ওয়েব সার্ভিস চেষ্টা করে দেখতে পারেন যেখানে আপনি ক্লায়েন্টের থেকে কেবল একটি ওয়েব পরিষেবা URL নির্দিষ্ট করেছেন এবং ক্লায়েন্টের বিবরণ (আপনার গ্রুপ / ব্যবহারকারীর স্তর ওভাররাইড থাকতে পারে) এর উপর নির্ভর করে এটি প্রয়োজনীয় সমস্ত কনফিগারেশন লোড করে দেয়। এর কিছু অংশের জন্য আমরা এমএস এন্টারপ্রাইজ লাইব্রেরিও ব্যবহার করেছি।

এটিই ছিল আপনি নিজের ক্লায়েন্টের সাথে কনফিগার স্থাপন করবেন না এবং আপনি আপনার ক্লায়েন্টদের থেকে পৃথকভাবে পরিচালনা করতে পারেন


3
আপনার উত্তরের জন্য ধন্যবাদ. তবে এর পুরো কারণ হ'ল কনফিগার ফাইল ফাইলগুলি এড়ানো। মডিউলগুলির জন্য কনফিগারেশন বিশদটি একটি ডাটাবেস থেকে লোড করা হয়। তবে যেহেতু আমি মডিউল বিকাশকারীদের ডিফল্ট .NET কনফিগারেশন প্রক্রিয়ার স্বাচ্ছন্দ্য দিতে চাই, আমি সেই সময়কার সময়ে মডিউল কনফিগারেশনগুলিকে একটি কনফিগার ফাইলে অন্তর্ভুক্ত করতে এবং এটি ডিফল্ট কনফিগারেশন ফাইল তৈরি করতে চাই। কারণটি সহজ: অনেকগুলি লাইব্রেরি রয়েছে যা অ্যাপকনফাইগের মাধ্যমে কনফিগার করা যায় (যেমন ডাব্লুসিএফ, এন্টলিব, ইএফ, ...)। আমি যদি অন্য কোনও কনফিগারেশন প্রক্রিয়াটি পরিচয় করিয়ে দিই, তবে কনফিগারেশনটি (চলবে)
ড্যানিয়েল হিলগারথ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.