Asp.net Web.config文件讀取路徑你真的清楚嗎?


我們經常都在用ConfigurationManager的AppSettings和ConnectionStrings屬性,當一個項目中有很多Web.config時它們的讀取順序究竟是怎么的了?也許我們可以通過實驗得出一些結論,但我這里僅從源代碼上來分析一下。

無論是ConfigurationManager的AppSettings還是ConnectionStrings屬性都在調用方一個共同的方法GetSection。從GetSection方法我們知道他主要是調用IInternalConfigSystem實例的GetSection方法,在這里用實例的GetSection方法之前它調用了一個PrepareConfigSystem方法。一看這個方法名稱我們就知道他是為ConfigSystem做准備工作的。而它也是在調用一個EnsureConfigurationSystem方法,這個方法有一句很重要  s_configSystem = newClientConfigurationSystem();但是我們在Web中用的難道就是ClientConfigurationSystem類嗎?不一定!讓我們 確認一下,在web的項目中,我們添加一下代碼:

   FieldInfo s_configSystem = typeof(ConfigurationManager).GetField("s_configSystem", BindingFlags.Static | BindingFlags.NonPublic);
            object clientConfigurationSystem  = s_configSystem.GetValue(null);

運行結果表明IInternalConfigSystem的實例是一個HttpConfigurationSystem類型。我們大家應該還知道http處理的開始是在HttpRuntime的

public static void ProcessRequest(HttpWorkerRequest wr)->private void ProcessRequestInternal(HttpWorkerRequest wr) ->private void EnsureFirstRequestInit(HttpContext context)->private void FirstRequestInit(HttpContext context)->private static void InitHttpConfiguration()->  HttpConfigurationSystem.EnsureInit(null, true, true).這里我想能說明IInternalConfigSystem的實例類型是HttpConfigurationSystem

internal static void EnsureInit(IConfigMapPath configMapPath, bool listenToFileChanges, bool initComplete)
{
    if (!s_inited)
    {
        lock (s_initLock)
        {
            if (!s_inited)
            {
                s_initComplete = initComplete;
                if (configMapPath == null)
                {
                    configMapPath = IISMapPath.GetInstance();
                }
                s_configMapPath = configMapPath;
                s_configSystem = (IConfigSystem) Activator.CreateInstance(Type.GetType("System.Configuration.Internal.ConfigSystem, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", true), true);
                object[] hostInitParams = new object[6];
                hostInitParams[0] = true;
                hostInitParams[1] = s_configMapPath;
                hostInitParams[3] = HostingEnvironment.ApplicationVirtualPath;
                hostInitParams[4] = HostingEnvironment.SiteNameNoDemand;
                hostInitParams[5] = HostingEnvironment.SiteID;
                s_configSystem.Init(typeof(WebConfigurationHost), hostInitParams);
                s_configRoot = s_configSystem.Root;
                s_configHost = (WebConfigurationHost) s_configSystem.Host;
                HttpConfigurationSystem internalConfigSystem = new HttpConfigurationSystem();
                if (listenToFileChanges)
                {
                    s_configRoot.ConfigChanged += new InternalConfigEventHandler(internalConfigSystem.OnConfigurationChanged);
                }
                s_configSettingsFactory = (IInternalConfigSettingsFactory) Activator.CreateInstance(Type.GetType("System.Configuration.Internal.InternalConfigSettingsFactory, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", true), true);
                s_configSettingsFactory.SetConfigurationSystem(internalConfigSystem, initComplete);
                s_httpConfigSystem = internalConfigSystem;
                s_inited = true;
            }
        }
    }
}

 這段代碼主要是實例化了一個HttpConfigurationSystem,InternalConfigSettingsFactory,其中有一句s_configSettingsFactory.SetConfigurationSystem(internalConfigSystem, initComplete)很是重要,讓我們來看看InternalConfigSettingsFactory的SetConfigurationSystem方法:

void IInternalConfigSettingsFactory.SetConfigurationSystem(IInternalConfigSystem configSystem, bool initComplete)
    {
        ConfigurationManager.SetConfigurationSystem(configSystem, initComplete);
    }

其中ConfigurationManager.SetConfigurationSystem有這么一句

internal static void SetConfigurationSystem(IInternalConfigSystem configSystem, bool initComplete)
{
        s_configSystem = configSystem;       
}
我想大家現在應該明白為什么IInternalConfigSystem這里是HttpConfigurationSystem了吧。
現在讓我們來看看HttpConfigurationSystem的GetSection方法

internal static object GetSection(string sectionName)
{
    HttpContext current = HttpContext.Current;
    if (current != null)
    {
        return current.GetSection(sectionName);
    }
    return GetApplicationSection(sectionName);
}
它實際上是調用的HttpContext 的GetSection方法:

public object GetSection(string sectionName)
{
    if (HttpConfigurationSystem.UseHttpConfigurationSystem)
    {
        return this.GetConfigurationPathData().ConfigRecord.GetSection(sectionName);
    }
    return ConfigurationManager.GetSection(sectionName);
}
最終調用的是CachedPathData的GetVirtualPathData,其調用參數依次是 CachedPathData.GetVirtualPathData(this._request.FilePathObject, false),如果這個有返回值就直接返回,否則繼續調用CachedPathData.GetVirtualPathData(this._configurationPath, true)

internal static CachedPathData GetVirtualPathData(VirtualPath virtualPath, bool permitPathsOutsideApp)
{
    if (!HostingEnvironment.IsHosted)
    {
        return GetRootWebPathData();
    }
    if (virtualPath != null)
    {
        virtualPath.FailIfRelativePath();
    }
    if ((virtualPath != null) && virtualPath.IsWithinAppRoot)
    {
        return GetConfigPathData(WebConfigurationHost.GetConfigPathFromSiteIDAndVPath(HostingEnvironment.SiteID, virtualPath));
    }
    if (!permitPathsOutsideApp)
    {
        throw new ArgumentException(SR.GetString("Cross_app_not_allowed", new object[] { (virtualPath != null) ? virtualPath.VirtualPathString : "null" }));
    }
    return GetApplicationPathData();
}

 這個方法主要是調用GetConfigPathData方法,參數一次是("machine/webroot");(WebConfigurationHost.GetConfigPathFromSiteIDAndVPath(HostingEnvironment.SiteID, virtualPath));virtualPath));,HostingEnvironment.AppConfigPath,要想知道

(WebConfigurationHost.GetConfigPathFromSiteIDAndVPath(HostingEnvironment.SiteID, virtualPath));virtualPath));

HostingEnvironment.AppConfigPath

這兩個東西在實際的項目中返回的東西可以把以下代碼放到web項目中就可以知道它們具體是說明東西了。
            MethodInfo m = Type.GetType("System.Web.Configuration.WebConfigurationHost,System.Web,Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
                .GetMethod("GetConfigPathFromSiteIDAndVPath", BindingFlags.Static | BindingFlags.NonPublic);
            PropertyInfo sitID = typeof(HostingEnvironment).GetProperty("SiteID", BindingFlags.Static | BindingFlags.NonPublic);
            object SitID = sitID.GetValue(null, new object[] { });
            PropertyInfo filePathObject = typeof(HttpRequest).GetProperty("FilePathObject", BindingFlags.NonPublic | BindingFlags.Instance);
            object FilePathObject = filePathObject.GetValue(System.Web.HttpContext.Current.Request, new object[]{});
            object obj = m.Invoke(null, new object[] { SitID, FilePathObject });

            PropertyInfo appConfigPath = typeof(HostingEnvironment).GetProperty("AppConfigPath", BindingFlags.NonPublic | BindingFlags.Static);
            object AppConfigPath = appConfigPath.GetValue(null, new object[] { });

有關CachedPathData 的
private static CachedPathData GetConfigPathData(string configPath)方法具體實現我們及忽略它吧,很是復雜。最主要是這些方法都是內部私有方法,知道了也沒多大意義。要想改變web.config的讀取路徑,理論上很簡單就是自己實現一個IInternalConfigSystem子類來修改讀取路徑不過這個類的修改非常龐大,我們退而求次來改變Request的_filePath變量來實現修改讀取路徑的方法
具體的實現代碼如下:

  void MvcApplication_PostMapRequestHandler(object sender, EventArgs e)
        {
            HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context);
            string channelName = context.Request.RequestContext.RouteData.Values["ChannelName"].ToString();
            Type typeVirtualPath = Type.GetType("System.Web.VirtualPath,System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
            Type typeCachedPathData = Type.GetType("System.Web.CachedPathData,System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
            MethodInfo Create = typeVirtualPath.GetMethod("Create", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(string) }, null);

            PropertyInfo _filePathObject = typeof(HttpRequest).GetProperty("FilePathObject", BindingFlags.NonPublic | BindingFlags.Instance);
            object filePathObject = _filePathObject.GetValue(System.Web.HttpContext.Current.Request, new object[] { });

            PropertyInfo _virtualPathString = typeVirtualPath.GetProperty("VirtualPathString");
            object virtualPathString = _virtualPathString.GetValue(filePathObject, new object[] { });
            string newPathString = virtualPathString.ToString().Replace(channelName, "Views/Channel/" + channelName);

            object newVirtualPathString = Create.Invoke(null, new object[] { newPathString });

            FieldInfo filePath = typeof(HttpRequest).GetField("_filePath", BindingFlags.NonPublic | BindingFlags.Instance);
            filePath.SetValue(System.Web.HttpContext.Current.Request, newVirtualPathString);

            object filePathData = typeCachedPathData.GetMethod("GetVirtualPathData", BindingFlags.Static | BindingFlags.NonPublic)
                .Invoke(null, new object[] { newVirtualPathString, false });

            FieldInfo _filePathData = typeof(HttpContext).GetField("_filePathData", BindingFlags.NonPublic | BindingFlags.Instance);
            _filePathData.SetValue(HttpContext.Current, filePathData);
            //string aa = ConfigurationManager.AppSettings["TestData"].Trim();
        }

 其實個人並不是很建議大家來修改默認的ConfigurationManager來實現該功能。如果需要實現建議大家用WebConfigurationManager來實現。

   var str= WebConfigurationManager.OpenWebConfiguration("/Views/Channel/men/web.config").AppSettings.Settings["TestData"].Value;

看看這個是不是很簡單啊。具體的代碼http://download.csdn.net/detail/dz45693/4774677


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM