I have a program that I want to publish as a single exe file which will be used by a user who can change the appsettings as they need. Some of the settings are just for development (included in appsettings.Development.json and this file is not published) and some of them are required for production (included in appsettings.json).
For better clarification consider the following code as appsettings.Development.json
{
"SettingA":
{
"ItemA" : "some value"
},
"SettingB":
{
"ItemB": ["1010","2020","3030"]
}
}
SettingA is used by both development and production and the value can be changed by user
SettingB is only used by development and the value cannot be changed by user
So the appsettings.json would be like, which is published for the user to change:
{
"SettingA":
{
"ItemA" : "some value"
}
}
In order to use this program in production, SettingB is still needed to be set as default value like the following:
class SettingB
{
public string[] ItemB {get; set;} = new string[]{"*"}
}
However, the issue is that when I want to use this in development I expect ItemB to be overriden by the values in appsettings.Development.json, instead I get this:
What I expect: ItemB = {"1010","2020","3030"}
What the result actually is: ItemB = {"*", "1010","2020","3030"}
It seems instead of being replaced, the values are added to the default one.
I have two questions here:
Is this approach of setting default values for such properties like SettingB, OK?
How can I get the expected value for ItemB?
By the way I use Options to read the settings.
I found the answer to my second question in this article. Surprisingly the arrays are merged by design!
Related
I want to assign Active Directory group name dynamically as an attribute to authorize filter.
Currently we have 2 Active Directory groups, one is for DEV and other is for Prod. However if I have access to dev while debugging in local, I need to have access to the action method. If the AD group is prod, I should not have have access to the action method.
I tried using constants in static class classA
public const string DevActiveDirectoryName = "DevdirectoryName";
public const string ProdActiveDirectoryName = "ProddirectoryName";
My action method is like this:
[Authorize(Roles = ClassA.DevActiveDirectoryName)]
public async task<Iactionresult>GetMemberID()
{
}
The only problem with above solution is if I want to deploy to prod, I need to change code and deploy it. Instead if I can pass value dynamically to attribute that would solve my problem. I have tried many ways. Please suggest the best solution for this case. Is there any workaround for this kind of problem? I appreciate your help.
In order to be able to change the group name for different environments, you need to use configuration settings instead of constants. There are many options on how to provide configuration settings to an ASP.NET Core application. This link gives an overview.
If your application uses the default host builder, it will read configuration settings from a variety of sources, e.g. appsettings.json. You can add a setting to this file (if it does not exist yet, add it to the project), e.g.:
{
"ADGroupName": "ProddirectoryName"
}
For your dev-environment there is a dedicated file appsettings.dev.json that you can use to hold your dev settings:
{
"ADGroupName": "DevdirectoryName"
}
When protecting the controller with an Authorize attribute, you need to provide a constant value to the constructor. As the configuration setting can be changed later, it is (obviously) not constant.
Therefore, you have to set up a policy with a constant name in the ConfigureServices method in Startup.cs:
var adGroupName = Configuration.GetValue<string>("ADGroupName");
services.AddAuthorization(options =>
{
options.AddPolicy("ADGroupPolicy", policy =>
{
// This requirement checks for the group
policy.RequireRole(adGroupName);
});
});
For the controller, you need to add the policy name to the Authorize attribute:
[Authorize("ADGroupPolicy")]
public async task<Iactionresult>GetMemberID()
{
}
You can add an entry in your <appSettings> of your web.Config file and use ConfigurationManager to look up the value that should be assigned to a variable ActiveDirectoryName.
<appSettings>
<add key="ActiveDirectoryName" value="DevdirectoryName" />
... // other keys
</appSettings>
and in your code, you could look up what you have in your web.Config file (Dev for development and Prod for production servers (you dont need to deploy new web.config when deploying new code unless you make changes to it.
public const string ActiveDirectoryName = ConfigurationManager.AppSettings["ActiveDirectoryName"];
If you are using Visual Studio, web.config have two different configs (web.debug.config / web.release.config). You can use debug for development and Release that works on production.
This will stay constant and only your config files are changed,
[Authorize(Roles = ClassA.ActiveDirectoryName)]
public async task<Iactionresult>GetMemberID()
{
}
We are converting an existing web application over to .NET Core. While in the process, we would like to be able to access both the web.config and the appsettings.config. In order to do so, we are creating a MySiteConfigurationManager class so that we can just do a quick find a replace for all of the ConfigurationManager.AppSettings["Datawarehouse.ConnectionString."] instances. Inside of the custom indexer property similarly called AppSettings[string key] of my MySiteConfigurationManagerclass I plan to do something similar to the following:
public string this[string key]
{
get
{
if (!key.Contains(":"))
{
return ConfigurationManager.AppSettings["key"];
}
return //some code here to access the appsettings.json value
}
}
Now I could just StreamRead the file itself and get the value needed but I am not sure if there is another way to access the appsettings.json values. As an example both of the following might appear in the project trying to access a configuration value:
MySiteConfigurationManager.AppSettings["Audit.DisableLogging"];
MySiteConfigurationManager.AppSettings["MRU:ItemLimit"]
My goal is for both to be able to work seamlessly if possible.
Hi I need to provide each user with some default settings - list of string
Then each user can add new and sav?
I am using user-scope settings.
What are my options?
UPDATE:
I cant even find default settings in file in debug folder. So the question is what do I need tp deploy to user machine
I would add class as AppSetting and make it serialize able.
Then I will add a ApplicationSettingManager it will look at a default path in such a folder like Environment.SpecialFolder.ApplicationData if the setting file was there it will use it to deserialize AppSetting if not it will save the default setting there (you can put default setting in AppSetting class or you can use app.config file beside .exe to have your default setting). whenever a user changed the setting ApplicationSettingManager will over write the setting file.
string ApplicationDataFolder
{
get
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Assembly.GetExecutingAssembly().GetName().Name);
}
}
string UserSettingFilePath
{
get
{
return Path.Combine(ApplicationDataFolder, "Setting.xml");
}
}
[Serializable]
public class AppSetting
{
public AppSetting()
{
//default instantiation
}
public int? CurrentUserId
{
get;
set;
}
}
enter code he
Look at http://msdn.microsoft.com/en-us/library/system.environment.specialfolder.aspx for different paths you can use
You can use the Settings object that is part of the Microsoft.VisualBasic library.
In C# you can access these through:
Properties.Settings.Default.FirstUserSetting = "abc";
And then save them out to disk with:
Properties.Settings.Default.Save();
This will store them in an app specific directory under Users\[UserName]\AppData\Local
Alternatively, you can manage the serialization yourself and write them out to either Environment.SpecialFolder.ApplicationData or Environment.SpecialFolder.LocalApplicationData
I'm currently developing a plugin to use in any application, and the configuration of the plugin is done through the usage of either a web.config or an app.config file.
Now, I want to unit test my plugin. I want to test the different behaviour in various conditions:
For exmaple:
Only 1 single item should be marked as 'default'.
A collection of providers can be registered in the plugin, however the name must be unique, otherwise, a specific exception is thrown.
My question:
I have a unit test project and I want to test each method with another application configuration file, is this possible?
I've tried reading the file from ExeConfiguration, but I don't get it to work.
Thanks for the feedback.
Kr,
This is what we do (same as #THG) explained in his answer.
public interface IConfiguration
{
string SettingA { get; }
string SettingB { get; }
}
public class Configuration : IConfiguration
{
public string SettingA
{
get
{
return ConfigurationManager.AppSettings["SettingA"];
}
}
public string SettingB
{
get
{
return ConfigurationManager.AppSettings["SettingB"];
}
}
}
Then in your test
var config = MockRepository.GenerateStub<IConfiguration>();
config.Stub(x => x.SettingA).Return("Fred");
My recommendation is to mock the integration with the config file instead of using it directly. This approach offers much more flexibility and removes the need to worry about multiple config files.
You can either use a mocking framework or create you own layer of abstraction on top of the code that fetches the config values.
I searched here for the answer. I'm sorry if this has been asked before (as I suspect it has).
Summary: How can I have strongly typed calls into my web.config without duplicating the property names?
Details: In my code, I try to minimize string usage, and I don't like defining something twice.
Complementing both of these wonts is my restriction of AppSettings usage (and its strings) to one class, which I reference throughout the project. The AppSettings class exposes public properties:
12 public static string DateFormatString {
13 get {
14 return ConfigurationManager.AppSettings["DateFormatString"];
15 }
16 }
How can I keep this class and prevent the duplication (lines 12 & 14) of the property name?
Alternatively, what other solution might you recommend?
I like using Custom Configuration Handlers.
http://haacked.com/archive/2007/03/12/custom-configuration-sections-in-3-easy-steps.aspx
There's no duplication in your example: One DateFormatString is a property name and the other is a string. You're just following a convention which names the property identically to the lookup key for the value.
I do see one possible improvement. You should read the config file once in a static constructor and store the values instead of reading them from AppSettings every time a property is accessed.
I'd probably recommend deserializing the web.config into an object. You can then access all configuration entries as if they were properties (can't get much more strongly typed than that!)
I create a wrapper for everything that doesn't belong to me directly. This includes cache, session, configuration, external web services, etc. This allows me to encapsulate the dirty details of using that widget. In the case of configuration I have a bare bones configuration class that exposes the various properties that I have housed my app.config or web.config. This might look something like this:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using RanchBuddy.Core.Services.Impl;
using StructureMap;
namespace RanchBuddy.Core.Services.Impl
{
[Pluggable("Default")]
public class ConfigurationService : IConfigurationService
{
internal static int GetDefaultCacheDuration_Days()
{
return Convert.ToInt32(ConfigurationManager.AppSettings["DefaultCacheDuration_Days"]);
}
...
internal static LoggingType GetLoggingType()
{
string loggingType = ConfigurationManager.AppSettings["LoggingType"].ToString();
if(loggingType.ToLower() == "verbose")
{
return LoggingType.Verbose;
}
else if (loggingType.ToLower() == "error")
{
return LoggingType.Error;
}
return LoggingType.Error;
}
...
public static string GetRoot()
{
string result = "";
if(ConfigurationManager.AppSettings["Root"] != null)
{
result = ConfigurationManager.AppSettings["Root"].ToString();
}
return result;
}
}
}
Inside here you can do more than simply get values from the config file. You can convert a string to the type that it needs to be. You can use the string to determine an enum value to be returned. Etc. But the key is that any changes that ever need to be made regarding configuration can be made here. To include if you want to swap out the mechanism for the configuration store!
Build your own ConfigurationSection with the Configuration Section Designer, this way you don't have to use AppSettings at all... but you'll have your own collection of settings that will even have Intellisense in the web.config file.
One solution could be,
public enum Settings
{
None,
DateFormatString,
DefeaultUserPrefix,
SomeOtherValue
}
and then have a helper class like,
public static class ConfigurationHelper
{
public static Get<T>(Settings setting)
{
string output = ConfigurationManager.AppSettings[setting.ToString()];
if(string.isNullOrEmpty(output))
throw new ConfigurationErrorsException("Setting " + setting + " is not defined in Configuration file.");
return (T)output; //You can probably use Convert.* functions.
}
}
Your calling code will look like,
ConfigurationHelper.Get<string>(Settings.DateFormatString);
The above approach provides some level of strong typing but you still have to make sure that the settings name in config file matches with the name of the enum.
The best choice would be to auto-generate class based on the configuration file.
If you wanted strongly typed properties, you can write
public static string DateFormatString
{
get { return ConfigurationHelper.Get<string>(Settings.DateFormatString); }
}
Also, I would advice against reading the config file in constructor (premature optimization?). If you read the config file in constructor, it means that you can not change config file while application is running.
I have written a nuget package that does this (among other things). The accompanying blog post goes in to the details, but this simplified snippet shows the gist of it as related to your question.
public string DateFormatString =>
ConfigurationManager.AppSettings[MethodBase.GetCurrentMethod().Name.Replace("get_", "")];