…meie igapäevast IT’d anna meile igapäev…

2016-01-20

Adding a service reference to ASP.NET vNext/ASP.NET Core 1.0

Recently I started a new project in ASP.NET vNext aka ASP.NET 5, or as it is known since yesterday, ASP.NET Core 1.0. Since right now the framework is still in RC stage, there is next to no documentation to speak of… but that is a topic for another day.

But I had to add several WCF service references to the project to communicate with the back-end server. While we used to have a reasonably nice dialog for service references, it no longer exists in VS 2015 for ASP.NET Core 1.0. However – there is a plugin for adding “Connected services”, read about it at http://blogs.msdn.com/b/webdev/archive/2015/12/15/wcf-connected-service-visual-studio-extension-preview-for-asp-net-5-projects.aspx.

And I cannot stress this enough – while this plugin is in preview/current stage, do not use it. It is not capable of re-using code in referenced assemblies, which means it generates new classes instead of re-using old ones – and it cannot re-create generic classes. E.g. Response<User> becomes ResponseOfUserFq_SrmLzS, which is really not what is expected or wanted.

Instead, use good old svcutil from command line (you should probably create a cmd file, since you’ll probably want to update the service reference repeatedly):

svcutil.exe /l:cs /t:code /n:*,Your.Project.Desired.NameSpace /r:"fullpath-to-dto-assembly.dll" /r:"fullpath-to-model-assembly.dll" /r:"fullpath-to-vendor-assembly.dll" /o:Reference.cs http://localhost:9000/SomeServices/OurService.svc?fullwsdl

This will fetch the service info and create the service client (Reference.cs) and output.config while re-using all of the classes from assemblies specified with the /r keys. /n key allows you to specify the namespace for the generated code.

Note that the code generated with svcutil will require full .NET framework, not just .NET Core 1.0.

After I had figured out all of the above, I put my service configuration to wwwroot\web.config and expected it to work.

Nope. Enpoint configuration not found. No matter what I did, I could not get my development server (Kestrel) to read the <system.serviceModel> wwwroot\web.config. However, invalid XML in there gave an instant error… explain that?!

Googling was not all that helpful. There seems to be no way for ASP.NET Core to read service configuration from JSON files. But then I stumbled upon a lead – release announcement for ASP.NET 5 beta 7 had a single line announcing support of app.config when running on a full .NET framework.

And this is what I needed to do – instead of using good ol’ web.config (oh, how I hate thee!), you have to create an app.config in the root folder of your website (not wwwroot!), same place where project.json lives. You can just move the output.config there and rename it to app.config – and System.ServiceModel will now happily read the configuration from there.

2013-09-26

T4 templates for Web.config/App.config AppSettings

Filed under: Programmeerimine — Sander @ 11:37:47
Tags: , , ,

I’ve written before about generic methods to access configuration values. As nice as such generics are, there is still one major issue – you’ll still have to use strings as configuration value keys. Which means there is a possibility to get an invalid key name by accident – or even worse, wrong value from the configuration. Such problems will often come out only after the project has been shipped.

One – and preferred – solution is to use Settings instead of AppSettings. These are strongly typed and can be accessed via keys (Properties.Settings.Default.SomeNiceSetting) – while the values are still in the configuration file (section <applicationSettings>).

Unfortunately, we cannot always use Settings, for several reasons.

So, I got fed up by having to memorize or copy the keys, do casting and so forth – and decided to figure out some better way to get the AppSettings values. After some other ideas, I ended up with two T4 templates.

First approach: string fields

 

  1. <#@ template debug="false" hostspecific="true" language="C#" #>
  2. <#@ assembly name="System.Configuration" #>
  3. <#@ assembly name="System.Core" #>
  4. <#@ import namespace="System.Configuration" #>
  5. <#@ import namespace="System.Linq" #>
  6. using System.Configuration;
  7.  
  8. namespace <your-namespace-here>
  9. {
  10.     public static class AppSettings {
  11.       <#
  12.     var configurationFileMap = new ExeConfigurationFileMap();
  13.     configurationFileMap.ExeConfigFilename = this.Host.ResolvePath("Web.config");
  14.     var configuration = ConfigurationManager.OpenMappedExeConfiguration(configurationFileMap, ConfigurationUserLevel.None);
  15.     foreach(string key in configuration.AppSettings.Settings.AllKeys.Where(x => !x.Contains(":")))
  16.     { #>
  17.         public static readonly string <#= key #> = ConfigurationManager.AppSettings["<#= key #>"];
  18.     <#} #>
  19.     }
  20. }

This is my first approach – generate a static AppSettings class, which has readonly static string fields for all the AppSetting keys (note the !x.Contains(“!”) bit – that is to avoid the ASP.NET MVC keys).

This has both benefits and disadvantages over the second template – namely, you’ll get string values and will have to convert them to the required data type yourself. When someone messes up configuration (i.e. instead of “40” types in “4O” then obviously the cast to integer will fail), you have the option to fail gracefully – fall back to default value or such.

You can also combine this approach with generic methods linked above – i.e. use the t4 template to create constant strings for keys and send the now-hardcoded keys to generic method for appropriate handling.

Second approach: strongly typed properties

  1. <#@ template debug="false" hostspecific="true" language="C#" #>
  2. <#@ assembly name="System.Configuration" #>
  3. <#@ assembly name="System.Core" #>
  4. <#@ import namespace="System.Configuration" #>
  5. <#@ import namespace="System.Linq" #>
  6. <#@ import namespace="System.Globalization" #>
  7. using System.Configuration;
  8.  
  9. namespace <your-namespace-here>
  10. {
  11.     public static class AppSettings {
  12. <#
  13.     var configurationFileMap = new ExeConfigurationFileMap();
  14.     configurationFileMap.ExeConfigFilename = this.Host.ResolvePath("Web.config");
  15.     var configuration = ConfigurationManager.OpenMappedExeConfiguration(configurationFileMap, ConfigurationUserLevel.None);
  16.     foreach(KeyValueConfigurationElement setting in configuration.AppSettings.Settings)
  17.     {
  18.         if (setting.Key.Contains(":")) //these are ASP.NET MVC keys
  19.             continue;
  20.  
  21.         string settingType;
  22.         int i; bool b; decimal d;
  23.         if (int.TryParse(setting.Value, out i))
  24.             settingType = "int";
  25.         else if (bool.TryParse(setting.Value, out b))
  26.             settingType = "bool";
  27.         else if (decimal.TryParse(setting.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out d))
  28.             settingType = "decimal";
  29.         else { #>
  30.         public static string <#= setting.Key #> { get { return ConfigurationManager.AppSettings["<#= setting.Key #>"]; }}
  31. <# continue; } #>
  32.         public static <#= settingType #> <#= setting.Key #> { get { return <#= settingType #>.Parse(ConfigurationManager.AppSettings["<#= setting.Key #>"]); }}
  33. <# } #>
  34.     }
  35. }

Second approach tries to guess the appsetting type by trying to cast the value into several common types (int, boolean and decimal in the code above) and create the AppSettings class property in an already correct type.

This could be improved in several ways – for example, use TryParse() in non-string properties, use IConvertible and so forth. You probably need add your own types, too.

As an example, here is my test <appSettings>:

  1. <appSettings>
  2.     <add key="TestDecimal" value="6.02214129" />
  3.     <add key="TestBoolean" value="True" />
  4.     <add key="TestInt" value="420" />
  5.     <add key="TestString" value="Alea jacta est" />        
  6. </appSettings>

and output from the template:

public static class AppSettings {
    public static decimal TestDecimal { get { return decimal.Parse(ConfigurationManager.AppSettings["TestDecimal"]); }}
    public static bool TestBoolean { get { return bool.Parse(ConfigurationManager.AppSettings["TestBoolean"]); }}
    public static int TestInt { get { return int.Parse(ConfigurationManager.AppSettings["TestInt"]); }}
    public static string TestString { get { return ConfigurationManager.AppSettings["TestString"]; }}
}

You’ll have to add the AppSettings.tt template to the root folder of your application – or modify the this.Host.ResolvePath("Web.config") accordingly.

One thing that you have to note is that the configuration XML keys can have symbols that are not allowed in C# field/property names, such as the aforementioned colons in ASP.NET MVC keys. Best approach is probably to remove such symbols from field names, but not from the key names in return statements, as latter can handle just fine code such as ConfigurationManager.AppSettings["I:Really:Suck:At:Naming!"].

Another problem is that I was unable to get the template generation to auto-run when the Web.config changes. I tried to follow an old answer from Stack Overflow, but no dice. If you have any ideas/suggestions about this or other possible improvements, please post them to the comments – meanwhile you’ll just need to right-click on AppSettings.tt in Visual Studio and choose “Run custom tool” (if that option is missing, set in AppSettings.tt file properties “Custom Tool” to “TextTemplatingFileGenerator”).

2012-04-05

Generic methods to get configuration values

Filed under: Programmeerimine — Sander @ 11:35:35
Tags: ,

CSharpLogoFor some reason, there are things in code that I just cannot remember. One of these things is getting values from configuration – web.config or app.config.

Yes, I know, it is ridiculous. I remember literally hundreds of classes and methods, but a simple, one-line “ConfigurationManager.AppSettings[key]” I have to google. So I just gave up and wrote a wrapper – and to make it a bit more useful, I did a generic version, which can get the configuration value already in a specified type.

So, here is the three-method class, imaginatively named “Config”:

  1. /// <summary>
  2. /// Shorthand class for easy access to web.config values
  3. /// </summary>
  4. public static class Config
  5. {
  6.  
  7.     /// <summary>
  8.     /// Gets the setting as string by key.
  9.     /// Return string.Empty if the key does not exist
  10.     /// </summary>
  11.     /// <param name="key">The key.</param>
  12.     /// <returns></returns>
  13.     public static string GetSettingByKey(string key)
  14.     {
  15.         return ConfigurationManager.AppSettings[key] ?? string.Empty;
  16.     }
  17.  
  18.     /// <summary>
  19.     /// Generic version of GetSettingByKey().
  20.     /// Returns default(T) if the key does not exist
  21.     /// </summary>
  22.     /// <typeparam name="T"></typeparam>
  23.     /// <param name="key">The key.</param>
  24.     /// <returns></returns>
  25.     public static T GetSettingByKey<T>(string key)
  26.     {
  27.         var value = ConfigurationManager.AppSettings[key];
  28.  
  29.         return string.IsNullOrWhiteSpace(value) ? default(T) : (T)(object)(value);
  30.     }
  31.  
  32.     /// <summary>
  33.     /// Returns defaultValue, if the key does not exist.
  34.     /// </summary>
  35.     /// <typeparam name="T"></typeparam>
  36.     /// <param name="key">The key.</param>
  37.     /// <param name="defaultValue">The default value.</param>
  38.     /// <returns></returns>
  39.     public static T GetSettingByKey<T>(string key, T defaultValue)
  40.     {
  41.         var value = ConfigurationManager.AppSettings[key];
  42.  
  43.         return string.IsNullOrWhiteSpace(value) ? defaultValue : (T)(object)(value);
  44.     }
  45. }

Use it simply Config.GetSettingByKey<int>(“RowCount”) – which would return 0, if the key “RowCount” doesn’t exist. Use Config.GetSettingByKey<int>(“RowCount”, 16) to specify a different default value.

You could also do separate properties for more frequently used configuration values, to avoid typos in string keys:

  1. public static DateTime MyDateTime
  2. {
  3.     get { return GetSettingByKey<DateTime>("MyDateTime", DateTime.Now); }
  4. }

Blog at WordPress.com.