How to get Unity Container from external configuration file? - asp.net

In asp.net web api using Unity, I could register my services in UnityConfig.cs:
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<ITestService, TestService>();
//above code needs to be read from config file
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
Now, I have a configuration file (located in the root of the project) where stores all those types to be registered:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity>
<alias alias="ITestService" type="IMyServices.ITestService, IMyServices" />
<container>
<register type="ITestService" mapTo="MyServices.TestService,MyServices" />
</container>
</unity>
</configuration>
How can I get container from this file?
Below is a similar question but has not been resolved:
ASP.NET - Unity - Read configuration section from external configuration file

I find the solution to my problem which uses ExeConfigurationFileMap to specify the file path and load the specified configuration file explicitly.
public static class UnityConfig
{
public static void RegisterComponents()
{
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap();
fileMap.ExeConfigFilename = "my/file/path";
Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
var section = (UnityConfigurationSection)configuration.GetSection("unity");
IUnityContainer unityContainer = new UnityContainer();
unityContainer.LoadConfiguration(section);
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(unityContainer);
}
}

Related

How to setup configuration in .NET Framework for Serilog

Our team just moved one of our ASP.NET solutions from logging in log4net to Serilog (using iLogger) for logging. Our solution is .NET Framework 4.6. I can see Serilog configuration documentation online for setting up configuration in code as well as some documentation in appsettings.json. We have Web.config configuration files. Our old log4net configuration resided completely in the csproj files.
Is there a place for configuration for Serilog and its sinks in .NET Framework (specifically in Web.config or its own XML configuration file)? Do we have to put the configuration into the code (when we create the logger object)? Can we specify the configuration for specific controllers and models we have, and, if so, where is there documentation? I know we could specify locations, log levels, etc. for log4net for specific groups or controllers and models in log4net, but unsure how to do that for Serilog. If you got links for any of this, please point me in the right direction. Thanks.
After more investigating (and I can't believe I missed this earlier), the documentation here states you can edit the web.config file. In case anyone is looking for the configuration for web.config, there you go.
There are two ways one can configure Serilog in .net framework 4.7.2:
By using code only
By using app.config
1st Way (By Using code only):
Make a static serilogclass:
public static class SerilogClass
{
public static readonly Serilog.ILogger _log;
static SerilogClass()
{
_log = new LoggerConfiguration().
MinimumLevel.Debug().
WriteTo.File(#Environment.GetEnvironmentVariable("LocalAppData") + "\\Logs\\Logs1.log").
CreateLogger();
}
}
Note: #Environment.GetEnvironmentVariable("LocalAppData") will save logfile into appdata folder
Initialize and Use the SerilogClass in program.cs
class Program
{
static readonly Serilog.ILogger log = SerilogClass._log;
static void Main(string[] args)
{
log.Debug("This is serialog Example");
log.Debug("This is serialog Example2");
}
}
2nd Way(By using app.config):
Make a static serilogclass:
public static class SerilogClass
{
public static readonly Serilog.ILogger _log;
static SerilogClass()
{
_log = new LoggerConfiguration().
ReadFrom.AppSettings().
CreateLogger();
}
}
Initialize and Use the SerilogClass in program.cs
class Program
{
static readonly Serilog.ILogger log = SerilogClass._log;
static void Main(string[] args)
{
log.Debug("This is serialog Example using app.config");
log.Debug("This is serialog Example2 using app.config");
}
}
We need too add <appSettings></appSettings> section to define all settings which we were doing via code in 1st way
App.config:
<configuration>
<configSections></configSections>
<appSettings>
<add key="serilog:minimum-level" value="Debug"/>
<add key="serilog:using:File" value="Serilog.Sinks.File" />
<add key="serilog:write-to:File.path" value="C:\Logs\LogSerilog.txt" />
<add key="serilog:write-to:File.shared" value="true" />
<add key="serilog:write-to:File.rollOnFileSizeLimit" value="true" />
<add key="serilog:write-to:File.fileSizeLimitBytes" value="2000" />
</appSettings>
<startup></startup>
<runtime></runtime>
</configuration>

Multi-platform compilation: System.Configuration.ConfigurationManager.GetSection throws error on .NetCore

Background:
We are in the process of migrating .Net application to .Net Core.
As a strategy, we would like to keep the existing functionality intact on Full framework while migrating portion of the application to .Net Core. Full application would support .services over Net remoting and REST API whereas .Net core application will only support REST API services.
We have decided to keep the same code base for entire application and support compilation on multiple platforms (NetcoreApp2.1 and Net472).
There is a single application configuration file. Most of the components are dependent on the information stored in this file. Thus we would like to retain the single configuration file for both platforms.
I used System.Configuration.ConfigurationManager package to access configuration information.
Issue:
ConfigurationManager.GetSection(string) throws exception on .Net core platform whereas it works fine on Net472.
Error Message: Configuration system failed to initialize ---> System.Configuration.ConfigurationErrorsException: Unrecognized configuration section system.runtime.remoting
Work around tried so far:
ConfigurationManager.OpenExeConfiguration(configurationUserLevel).GetSection(string) works perfect on both the platforms for fetching the same section
Sample Code:
static MyConfigurationSection myConfigurationSettings { get; set; }
static void Main(string[] args)
{
LoadSettings();
}
private static void LoadSettings()
{
try
{
//Net472 : Works
//NetCoreApp2.1: Throws exception
myConfigurationSettings = ConfigurationManager.GetSection("myCustomSettings") as MyConfigurationSection;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
//Works on both platform
myConfigurationSettings = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).GetSection("myCustomSettings") as MyConfigurationSection;
Console.WriteLine(myConfigurationSettings.Applications.Count);
Console.ReadLine();
}
Here is configuration file
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="myCustomSettings" type="TestConfigManager.MyConfigurationSection, TestConfigManager" />
</configSections>
<myCustomSettings>
<applications/>
</myCustomSettings>
<system.runtime.remoting>
<application>
<channels>
<channel ref="tcp" port="1111" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
Unfortunately, accessing configuration works slightly differently in the Core Framework (and also .NET 5 and 6). Even with the help of the links below, it took me some time to find it out.
This is how it worked for me:
As preparation, go to NUGET package manager and import
Microsoft.Extensions.Configation,
Microsoft.Extensions.Configation.Json,
Microsoft.Extensions.Configation.Xml
and (optional) Microsoft.Windows.Compatibility
Depending on the type of config file, access it as follows:
App.Config
Example:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="myKey" value="myValue"/>
</appSettings>
</configuration>
Declare
public static AppSettingsSection AppConfig { get; private set; } = null;
Initialize it via
AppConfig = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
.AppSettings;
Read any keys via:
var myValue = AppConfig.Settings["myKey"].Value;
appconfig.json
Example:
{
"AppSettings": {
"myKey": "myValue"
}
}
Declare
public static IConfigurationSection JsonConfig { get; private set; } = null;
Initialize it via
JsonConfig = new ConfigurationBuilder().AddJsonFile("appconfig.json",
optional: true, reloadOnChange: true).Build().GetSection("AppSettings");
Read any keys via:
var myValue = JsonConfig["myKey"];
Helpful links:
cant read app config in c-sharp
how to read appsettings values from json
Comparision between appSettings and ApplicationSettings

spring boot - How to load context configuration file

I am trying to convert spring mvc app to spring boot. I used to deploy this application in tomcat and test. Now with spring boot I am trying to do the same thing but I am facing issues to load xml file configuration.
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- Data sources -->
<Environment name="/source/schema" value="${schema}" type="java.lang.String" />
<Resource auth="Container" driverClass="org.postgresql.Driver"
factory="org.apache.naming.factory.BeanFactory"
idleConnectionTestPeriod="30" jdbcUrl="${url}"
maxAdministrativeTaskTime="0" maxConnectionAge="30" maxIdleTime="9" maxPoolSize="3" minPoolSize="2"
name="/source/DataSource" password="${password}"
preferredTestQuery="select 1" testConnectionOnCheckout="true" type="com.mchange.v2.c3p0.ComboPooledDataSource" user="${user}"/>
</Context>
This is my configuration file which I am trying to load. When I put
#ImportResource({"classpath:applicationContext.xml", "classpath:context.xml"})
I am able to load all the bean configuration from applicationcontext.xml but while loading context.xml it is giving
Caused by: org.xml.sax.SAXParseException: cvc-elt.1: Cannot find the declaration of element 'Context'.
How should I load these entries when deploying spring boot app in tomcat?
By default, JNDI is disabled in embedded Tomcat. You need to call Tomcat.enableNaming() to enable it.
If you can live by Java config,you can try below snippet to add JNDI and other configurations from context.xml using the java config.
#Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
return new TomcatEmbeddedServletContainerFactory() {
#Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
tomcat.enableNaming();
return super.getTomcatEmbeddedServletContainer(tomcat);
}
};
}
Example :
#Bean
public TomcatEmbeddedServletContainerFactory tomcatFactory() {
return new TomcatEmbeddedServletContainerFactory() {
#Override
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
Tomcat tomcat) {
tomcat.enableNaming();
return super.getTomcatEmbeddedServletContainer(tomcat);
}
#Override
protected void postProcessContext(Context context) {
ContextResource resource = new ContextResource();
resource.setName("jdbc/myDataSource");
resource.setType(DataSource.class.getName());
resource.setProperty("driverClassName", "your.db.Driver");
resource.setProperty("url", "jdbc:yourDb");
context.getNamingResources().addResource(resource);
}
};
}
#Bean(destroyMethod="")
public DataSource jndiDataSource() throws IllegalArgumentException, NamingException {
JndiObjectFactoryBean bean = new JndiObjectFactoryBean();
bean.setJndiName("java:comp/env/jdbc/myDataSource");
bean.setProxyInterface(DataSource.class);
bean.setLookupOnStartup(false);
bean.afterPropertiesSet();
return (DataSource)bean.getObject();
}
Have a look at this github link for related sample
context.xml should go into the /META-INF/ directory in your war files. It is instructions to the Tomcat server, there's no need to configure anything in Spring to try to load it.

Modify configuration section programmatically in medium trust

I have a custom ConfigurationSection in my application:
public class SettingsSection : ConfigurationSection
{
[ConfigurationProperty("Setting")]
public MyElement Setting
{
get
{
return (MyElement)this["Setting"];
}
set { this["Setting"] = value; }
}
}
public class MyElement : ConfigurationElement
{
public override bool IsReadOnly()
{
return false;
}
[ConfigurationProperty("Server")]
public string Server
{
get { return (string)this["Server"]; }
set { this["Server"] = value; }
}
}
In my web.config
<configSections>
<sectionGroup name="mySettingsGroup">
<section name="Setting"
type="MyWebApp.SettingsSection"
requirePermission="false"
restartOnExternalChanges="true"
allowDefinition="Everywhere" />
</sectionGroup>
</configSections>
<mySettingsGroup>
<Setting>
<MyElement Server="serverName" />
</Setting>
</mySettingsGroup>
Reading the section works fine. The issue I'm having is that when I read the section via
var settings = (SettingsSection)WebConfigurationManager.GetSection("mySettingsGroup/Setting");
And then I proceed to modify the Server property:
settings.Server = "something";
This doesn't modify the "Server" property in the web.config file.
Note: This needs to work under medium-trust, so I can't use WebConfigurationManager.OpenWebConfiguration which works fine. Is there an explicit way to tell the ConfigSection to save itself?
Short answer - no. .NET team were (allegedly) meant to fix this in v4, but it didn't happen.
The reason is because using WebConfigurationManager.GetSection returns nested read-only NameValueCollections, which do not persist when you change their values. Using WebConfigurationManager.OpenWebConfiguration, as you've quite rightly ascertained, is the only way to obtain read-write access to the config - but then you'll get a FileIOPermission exception thrown, as OpenWebConfiguration attempts to load all inherited configs down to your web.config - which include the machine-level web.config and machine.config files in C:\WINDOWS\Microsoft.NET\Framework, which are explicitly out-of-scope of Medium Trust.
Long answer - use XDocument / XmlDocument and XPath to get/set config values.

Unity [Dependency] attribute doesn't resolve

I have the following code, and when I try to run it, I can see that the BrokerProvider is not being resolved. Here is my code:
static void Main(string[] args)
{
IUnityContainer container = new UnityContainer();
UnityConfigurationSection section = (UnityConfigurationSection) ConfigurationManager.GetSection("unity");
section.Containers.Default.Configure(container);
new TestBroker().RunTestBroker();
}
class TestBroker
{
private IBrokerProvider brokerProvider;
public void RunTestBroker()
{
List<IPortfolio> portfolios = BrokerProvider.GetPortfolios();
}
[Dependency]
public IBrokerProvider BrokerProvider
{
get { return brokerProvider; }
set { brokerProvider = value; }
}
}
The related config
<unity>
<typeAliases>
<typeAlias alias="string" type="System.String, mscorlib" />
<typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity" />
<typeAlias alias="IBrokerProvider" type="PA.Common.Interfaces.IBrokerProvider, PA.Common" />
<typeAlias alias="PManager" type="PA.BrokerProviders.PManager, PA.BrokerProviders" />
</typeAliases>
<containers>
<container>
<types>
<type type="IBrokerProvider" mapTo="PManager">
<lifetime type="singleton" />
</type>
</types>
</container>
</containers>
</unity>
Another question: Do I need to repeat the same 3 lines of code that I have under main in every other class that I would like to use unity or setting it up once is enough?
That's because are creating TestBroker directly by calling operator new on it:
new TestBroker().RunTestBroker();
In order for unity to resolve your dependencies you need to call the framework like so:
var broker = container.Resolve<TestBroker>();
IUnityContainer is the interface that is going to be doing all the work for you - i.e. resolving types to instances. You only need to create it once and then just pass it around the application where ever you need it.

Resources