Make the web.config dynamic - c#

We have an application and it is deployed across different pipeline dedicated for various release.
For ex - pipeline A - dedicated for march release
pipeline B - for June release etc
Within each pipeline we various environment like DEV, SIT etc
Now , with release , while deploying code , we need to make changes in the webcofig file , because the urls that we have in config are pipeline and environment dependent.
For Example we have a web server - box 1 for dev environment .
we have Pipeline A , Pipeline B deployed as websites. The web.config of pipeline A will look like -
<configMap hostnameList="box1" name="DevEnvironment">
<include set="Dev" />
</configMap>
<configSet name="Dev">
<add key="someUrl" value="http://somapp-piplelineA-Dev.app.com"/>
</configSet>
The web.config of pipeline B will look like -
<configMap hostnameList="box1" name="DevEnvironment">
<include set="Dev" />
</configMap>
<configSet name="Dev">
<add key="someUrl" value="http://somapp-piplelineB-Dev.app.com"/>
</configSet>
If you see this config , in the value for key someurl , the pipelineA was changed to pipelineB. These changes are tiresome when there are a lot of keys. So, we want to create a single web.config that can be used by all environment and which would not require any change.

With Octopus Deploy you can deploy your web applications semi- or fully-automatically. But also it can perform Web.config Transformation for each environment separately.

you can use config transforms ability built in inside visual studio
if you create a new asp .net web project you will see a sample inside web.debug.config and web.release.config.
you can also right click on web.config and click Add Config Transform and you will have a config transform file for each of your build configurations.
you can also use SlowCheetah. it is a very handy extension.

This is what I implemented to allow us to have the config dynamically created depending on the build type, utilizing build events.
Which will allow you to have 1 config to rule them all :)
https://xmlpreprocess.codeplex.com/
Project Description
XmlPreprocess is a command-line utility that can modify annotated XML files much like a code preprocessor. It is useful for deploying configuration files to different environments making substitutions such as connection strings. It is easily integrated into almost any script, build tool or deployment package to simplify and centralize your deployment strategy.
My build event
C:\XMLPreprocessor\XmlPreprocess.exe /i "C:\AppConfig\Core.config" /dbkind mssql /db "Server=localhost\SQLEXPRESS;Database=DB1;User Id=dbreader; Password=pass1;" /e $(ConfigurationName)
This is an example of my configuration XML file the {} params are retrieved from a SQL configuration database which contains the release type (1=Debug,2=Test,3=Release) and the values are populated accordingly.
<Nini>
<Section Name="AppSettings">
<!-- ifdef _xml_preprocess -->
<!--
<Key Name="RSAKeyStrength" Value="${RSAKeyStrength}"/>
<Key Name="EventLog_Name" Value="{EventLog_Name}"/>
<Key Name="DomainAddress" Value="${DomainAddress}"/>
<Key Name="AuthIssuer" Value="${AuthIssuer}"/>
-->
<!-- else -->
<Key Name="RSAKeyStrength" Value="2048"/>
<Key Name="EventLog_Name" Value="MyApp"/>
<Key Name="DomainAddress" Value="mydomain.com"/>
<Key Name="AuthIssuer" Value="auth.domain.com"/>
<!-- endif -->
</Section>
<Section Name="ConnectionStrings">
<!-- ifdef _xml_preprocess -->
<!--
<Key Name="IdentityUserModelEntities" Value="data source=${DB1ConnectionString};MultipleActiveResultSets=True"/>
<Key Name="DB1ModelEntities" Value="data source=${DB1ConnectionString};MultipleActiveResultSets=True"/>
<Key Name="LoggingDB1Entities" Value="data source=${LoggingDB1ConnectionString};MultipleActiveResultSets=True"/>
-->
<!-- else -->
<Key Name="IdentityUserModelEntities" Value="data source=localhost\SQLEXPRESS;initial catalog=DB1;user id=admin;password=pass1;MultipleActiveResultSets=True"/>
<Key Name="DB1ModelEntities" Value="data source=localhost\SQLEXPRESS;initial catalog=DB1;user id=admin;password=pass1;MultipleActiveResultSets=True"/>
<Key Name="LoggingDB1Entities" Value="data source=localhost\SQLEXPRESS;Initial Catalog=LoggingDB1;user id=logging_admin;Password=pass1;MultipleActiveResultSets=True"/>
<!-- endif -->
</Section>
</Nini>

Related

How modify loaded configuration by CacheManager in runtime

I'm using #Victor P solution to manage the cache in my application.
The configuration is loaded from the application settings, but we have a policy of not add sensitive information in the code, and on the other hand, the production instance of Redis requires authentication. This password is loaded from the environment variables, but I can't find the way to modify the Redis configuration in runtime.
Here is how we are doing it now
// Locad configuration of cache type: MemoryCache or RedisCache
string cacheManagerName = ConfigurationManager.AppSettings["CacheManagerName"];
// Build cache configuration from configuration section
var config = ConfigurationBuilder.LoadConfiguration(cacheManagerName);
//TODO: Modify config if the variable environment for the password is set
// This will only necessary if the cache type is Redis
//Create cachemanager instance
_kernel.Bind(typeof(ICacheManager<>)).ToMethod((ctx) => CacheFactory.FromConfiguration(ctx.GenericArguments[0], config)).InSingletonScope();
Configuration example:
<add key="CacheManagerName" value="RedisCache" />
<cacheManager xmlns="http://cachemanager.michaco.net/schemas/CacheManagerCfg.xsd">
<managers>
<cache name="MemoryCache" updateMode="None" enableStatistics="false" enablePerformanceCounters="true">
<handle name="default" ref="MemoryCacheHandle" />
</cache>
<cache name="RedisCache" updateMode="Up" enablePerformanceCounters="true"
enableStatistics="false" backplaneName="RedisConfigurationId"
backplaneType="CacheManager.Redis.RedisCacheBackplane, CacheManager.StackExchange.Redis"
serializerType="CacheManager.Serialization.Json.JsonCacheSerializer, CacheManager.Serialization.Json">
<handle name="RedisConfigurationId" ref="RedisCacheHandle" isBackplaneSource="true"/>
</cache>
</managers>
<cacheHandles>
<handleDef id="MemoryCacheHandle" type="CacheManager.SystemRuntimeCaching.MemoryCacheHandle`1, CacheManager.SystemRuntimeCaching"
defaultExpirationMode="Sliding" defaultTimeout="30m" />
<handleDef id="RedisCacheHandle" type="CacheManager.Redis.RedisCacheHandle`1, CacheManager.StackExchange.Redis"
defaultExpirationMode="Sliding" defaultTimeout="30m" />
</cacheHandles>
</cacheManager>
<cacheManager.Redis xmlns="http://cachemanager.michaco.net/schemas/RedisCfg.xsd">
<connections>
<connection id="RedisConfigurationId"
allowAdmin="true"
password=""
ssl="false"
sslHost="">
<endpoints>
<endpoint host="127.0.0.1" port="6379" />
</endpoints>
</connection>
</connections>
</cacheManager.Redis>
Removing secrets from an app/web.config was always an issue by itself I guess.
There is a documentation post which explains some options.
Regarding CacheManager. You can use the <connectionStrings> section for configuring Redis, instead of the cacheManager.Redis section, and then store that connection string in a separated "secret" file
<connectionStrings configSource="ConnectionStrings.config">
</connectionStrings>
That's still pretty stupid though in my opinion. So the best way is to configure that part entirely by code and read secrets from some secure store. Btw, environment variables are not secure at all either.
You can "trick" cachemanager and add just the redis configuration by code via RedisConfigurations. And reference the config key as usual.

Extend an element in App.config

We have an app.config we are using with Carbonator:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="carbonator" type="Crypton.Carbonator.Config.CarbonatorSection, Crypton.Carbonator"/>
</configSections>
<carbonator defaultCulture="en-US" logLevel="1" collectionInterval="1000" reportingInterval="1000" >
<statsd server="127.0.0.1" port="8125" />
<counters>
<add path="processor_information.pct_processor_time.total" category="Processor" counter="% Processor Time" instance="_Total" />
<add path="memory.available_MBytes" category="Memory" counter="Available MBytes" instance="" />
<add path="memory.pct_commited_bytes_in_use" category="Memory" counter="% Committed Bytes In Use" instance="" />
</counters>
</carbonator>
</configuration>
We want to allow users to configure their own custom counters in an external config file that we reference from the <counters> element. For example, we would like to allow the user config file to look like:
<add path="logical_disk.pct_free_space.C" category="LogicalDisk" counter="% Free Space" instance="C:" />
<add path="logical_disk.disk_read_bytes_per_sec.C" category="LogicalDisk" counter="Disk Read Bytes/sec" instance="C:" />
<add path="logical_disk.disk_write_bytes_per_sec.C" category="LogicalDisk" counter="Disk Write Bytes/sec" instance="C:" />
I don't even know if this is possible outside of an appConfig element, but any help is appreciated.
According to this answer it should be possible. Same way is also described in this article.
But I don't think it's a good idea for one reason - if a user makes a mistake in his configuration extension, it will prevent the application from executing since the application configuration became invalid.
I would rather use the configuration in the app.config file to provide default values and implement some user configuration myself. Is such case, you can use whatever configuration format you like, for example JSON, which would be also better (easier to create and edit) for users. In your application, you simply merge both configurations (app.config values are default values which will be overwritten by the user's configuration).

The configuration element is not declared

I'm doing some work in Visual Studio 2012 Express Edition. I have added an App.config XML file as follows:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>
The first thing that happens is a warning comes up that says "The 'configuration' element is not declared". Does anyone know why this is happening? It looks like elements can not be declared inside of until this is resolved.
Thanks!
This is the entire XML:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="Version" value="779" />
<add key="TimeOut" value="60000" />
<add key="LogFileName" value="Log.txt" />
<!-- your Developer Id with eBay -->
<add key="Environment.DevId" value="" />
<!-- your Application Id with eBay -->
<add key="Environment.AppId" value="" />
<!-- your Application Certificate with eBay -->
<add key="Environment.CertId" value="" />
<!-- API Server URL -->
<!-- For production site use: https://api.ebay.com/wsapi -->
<!-- For Sandbox use: https://api.sandbox.ebay.com/wsapi -->
<add key="Environment.ApiServerUrl" value="https://api.sandbox.ebay.com/wsapi" />
<!-- EPS Server URL -->
<!-- For production site use: https://api.ebay.com/ws/api.dll"/-->
<add key="Environment.EpsServerUrl" value="https://api.sandbox.ebay.com/ws/api.dll" />
<!-- eBay Signin URL -->
<!-- For production site use: https://signin.ebay.com/ws/eBayISAPI.dll?SignIn -->
<!-- https://signin.sandbox.ebay.com/ws/eBayISAPI.dll?SignIn -->
<add key="Environment.SignInUrl" value="https://signin.sandbox.ebay.com/ws/eBayISAPI.dll?SignIn" />
<!-- ViewItem URL -->
<!-- For production site use: http://cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&item={0} -->
<add key="Environment.ViewItemUrl" value="http://cgi.sandbox.ebay.com/ws/eBayISAPI.dll?ViewItem&item={0}" />
<!-- token is for both API server and EPS server -->
<add key="UserAccount.ApiToken" value="" />
<!-- eBay site ID -->
<add key="UserAccount.eBayUserSiteId" value="0" />
<add key="logexception" value="true"/>
<add key="logmessages" value="true"/>
<add key="logsdkmessages" value="true"/>
<add key="logsdk" value="true"/>
<add key="logfile" value="Log.txt"/>
<!-- Rule Name-->
<add key="RuName" value=""/>
<!-- Set this if you access eBay API server behind a proxy server-->
<add key="Proxy.Host" value =""/>
<add key="Proxy.Port" value =""/>
<!-- set proxy server username/password if necessary-->
<add key="Proxy.Username" value=""/>
<add key="Proxy.Password" value=""/>
Go to XML menu (visual studio top menu item) choose schemas and find for DotNetConfig.xsd and choose Use this schema.
Your problem will resolve for sure
<configuration xmlns="schema URL">
<!-- configuration settings -->
</configuration>
do changes,like above & try
I had the same issue. It is not an error, it is simply a warning; so your application should still compile. I used the following simple config file and the warning is still produced.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime
version="v4.0"sku=".NETFramework,
Version=v4.5"/>
</startup>
</configuration>
It is an issue that has been raised on the MSDN website, but it does not seem to have been satisfactorily resolved. See link below:
http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvcs/thread/18a1074f-668f-4fe3-a8d9-4440db797439
I had to
-> Go to XML menu (visual studio top menu item) choose schemas and select DotNetConfig.xsd AND RazorCustomSchema.xsd AND EntityFrameworkConfig_6_1_0.xsd
I just had this warning popup inside an autogenerated xml file while working on a xaml project.
Using Debug->Clean Solution and Debug->Rebuild Solution fixed it. Might want to try that before getting fancy with the schemas.
Visual Studio 2013 Express Edition is missing the DotNetConfig.xsd (https://connect.microsoft.com/VisualStudio/feedback/details/817322/dotnetconfig-xsd-files-not-present-in-vs-2013-express-for-desktop).
So to get rid of the warning in VS 2013 Express:
get a copy of DotNetConfig.xsd from another system or from the web (I used https://gist.github.com/eed3si9n/5dd7dd98ad2b3f668928b23477de35a3)
download to C:\Program Files (x86)\Microsoft Visual Studio 12.0\Xml\Schemas
add the schema following Ramakrishna's answer
The warning should be gone.
Choose use this schema. DotNetConfig.xsd
XLM Menu..... Visual Studio
Works perfectly.
I was having less space on my drive which might have resulted in incomplete loading of my application solution. This "the-configuration-element-is-not-declared" problem got solved after i created some space on my drive.
I also got the same warning. After thinking about for some time I realized my error working with SQL (MS SQL).
Warning: the 'configuration' element is not declared
Using C#
App.Config code:
<connectionStrings>
<add name="dbx" connectionString="Data Source=ServerNameHere;Initial Catalog=DatabaseNameHere;Integrated Security=True" providerName="System.Data.SqlClient"/>
</connectionStrings>
*this calls out the database name in the connectionStrings, when I plugged in my SQL code as a practice I always use the database name, schema, then table. This practice didn't carry over well in Visual Studio as I am a beginner. I removed the db name from my SQL syntax and only called from the schema, data table. This resolved the issue for me.
Form.CS:
using (SqlCommand cmd = new SqlCommand("SELECT * FROM [DatabaseName].[Schema].[TableName] WHERE [MEPeriod] = '2020-06-01'", con))
Updated to:
using (SqlCommand cmd = new SqlCommand("SELECT * FROM [Schema].[TableName] WHERE [MEPeriod] = '2020-06-01'", con))
This worked for me, I hope this is found as useful.

NHibernate works when called from one project, but not from the other

I'm creating my first application using NHibernate. Unfortunately, I encountered a problem I cannot solve:
I'm accessing the database from "DAL" project that contains CRUD methods and NHibernateHelper class.
I wrote a test project and I tried to add an object to DB - it works perfectly. But when I try to call the same method from my application's ViewModel (it's in the other project as well) it throws an error at configuration.BuildSessionFactory() in the code below:
private static ISessionFactory SessionFactory
{
get
{
if (_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(typeof(Address).Assembly);
_sessionFactory = configuration.BuildSessionFactory();
}
return _sessionFactory;
}
}
The exception:
NHibernate.HibernateException: Could not create the driver from NHibernate.Driver.OracleDataClientDriver.
System.Reflection.TargetInvocationException: {"Exception has been thrown by the target of an invocation."}
System.NullReferenceException: {"Object reference not set to an instance of an object."}
In my test project I added reference to Oracle.DataAccess.dll and created App.config as it throws the same error without it:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<qualifyAssembly partialName="Oracle.DataAccess"
fullName="Oracle.DataAccess,
Version=4.112.2.0,
Culture=neutral,
PublicKeyToken=89b483f429c47342" />
</assemblyBinding>
</runtime>
</configuration>
I made the same thing in my project containing ViewModel in which I need to get data from DB, but it didn't help. I also set Copy Local to True for that reference, but to no avail.
It's my App.config:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="dialect">
NHibernate.Dialect.Oracle10gDialect
</property>
<property name="connection.driver_class">
NHibernate.Driver.OracleDataClientDriver
</property>
<property name="connection.connection_string">
User Id=****;
Password=****;
Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=
(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))
(CONNECT_DATA=(SERVICE_NAME=xe)));
Pooling=true;
Enlist=false;
Statement Cache Size=50;
Min Pool Size=10;
Incr Pool Size=5;
Decr Pool Size=2;
</property>
<property name="show_sql">
true
</property>
</session-factory>
</hibernate-configuration>
I'm using NHibernate v 3.3.1.4000, Oracle 11g xe and 64-bit Windows 7 (I cannot choose to debug on 32 or 64-bit, there is only one possibility to select: "Active (Any CPU)" )
What can be the reason that I can use it from test project, but I cannot do it from the other?
Thank you in advance
Do you have a reference to driver's dll in your web project or in your DAL? it should be on your web project with Copy Local set to true, see this similar question
The problem is solved. I had to put app.config in main project, not in dll library from which I use the database. I didn't know that app.config placed in class library will be ignored.

Configuring NHibernate via Web.config in ASP.NET 4.0

So my unit tests are green, time to integrate this shiny new NHibernate-driven DAL in to my web app! I don't really want to maintain two configuration files so I've migrated hibernate.cfg.xml in to my Web.config file (i.e. I copypasta'd the contents of hibernate.cfg.xml in to my Web.config). Here is the relevant bits from my Web.config:
<configSections>
<section name="combres" type="Combres.ConfigSectionSetting, Combres, Version=2.0.0.0, Culture=neutral, PublicKeyToken=49212d24adfbe4b4"/>
<section name="nhibernate" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
</configSections>
<nhibernate xmlns="urn:nhibernate-configuration-2.2">
<session-factory name="">
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
<property name="connection.connection_string">Data Source=(local)\SQLExpress;Initial Catalog=MyProject;Integrated Security=True</property>
<property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
<property name='proxyfactory.factory_class'>NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
<listener class="MyProject.Sql.Listeners.SaveEventListener, MyProject" type="save"/>
<listener class="MyProject.Sql.Listeners.UpdateEventListener, MyProject" type="update"/>
</session-factory>
</nhibernate>
In Global.asax, on Application_Start, I try to initialize my configuration:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
SessionProvider.Initialize();
}
All this really does is call new Configuration().Configure().AddAssembly("MyProject"); in accordance with the configuration code above.
Interesting result: When I first hit the page, an exception is thrown:
[FileNotFoundException: Could not find file 'D:\Build\MyProject\Source\MyProject.Web\bin\hibernate.cfg.xml'.]
Well, I put the configuration in Web.config, shouldn't it be lookign there? Do I need to indicate "hey, NHibernate, pay attention -- the config data is in Web.config, dummy!" anywhere?
When I then hit F5, the page comes up. Hurray! Now I try to do something with data access and I get this exception:
[ProxyFactoryFactoryNotConfiguredException: The ProxyFactoryFactory was not configured.
Initialize 'proxyfactory.factory_class' property of the session-factory configuration section with one of the available NHibernate.ByteCode providers.
Example:
<property name='proxyfactory.factory_class'>NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
Example:
<property name='proxyfactory.factory_class'>NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>]
Huh, that's kinda weird too -- this worked just fine in test with configuration in hibernate.cfg.xml...and I am specifying this property in my Web.config...I wonder what could possibly be up?
So, anyone have any ideas? Any help in solving this mystery would be super!
*Update: I found the issue. It looks like I wasn't using the correct type in my configs section! D'oh. I have a complete write up on my blog.
Try calling the .Configure() method at the end:
new Configuration().AddAssembly("MyProject").Configure();
Or if you prefer put it into the web.config:
<nhibernate xmlns="urn:nhibernate-configuration-2.2">
<session-factory name="">
...
<mapping assembly="MyProject" />
</session-factory>
</nhibernate>
and then:
new Configuration().Configure();
Also make sure that the NHibernate.ByteCode.Castle.dll assembly is referenced in your web project.
It turns out that I was using the wrong type in the configuration section. You need to use NHibernate's section handler, not the generic .NET one. The behavior I was seeing was because it was all loaded in a singleton. On first visit, the configuration would fail. On subsequent visits it would just throw weird errors because the configuration failed originally!
There is one other caveat -- I have a complete writeup on my blog.

Categories

Resources