StructureMap errors when using simple configuration based Constructor Injection - c#

I have attempted a simple program to try out configuration based constructor injection. Here is the code:
using StructureMap;
namespace StructureMapConfig
{
class Program
{
static void Main(string[] args)
{
ObjectFactory.Initialize(x =>
{
x.PullConfigurationFromAppConfig = true;
});
var result = ObjectFactory.GetInstance<IIConstructor>();
}
}
public interface IIConstructor
{
}
public class Constructor : IIConstructor
{
public Constructor(bool test)
{
}
}
}
Here is my configuration file:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="StructureMap"
type="StructureMap.Configuration.StructureMapConfigurationSection,StructureMap"/>
</configSections>
<StructureMap>
<DefaultInstance MementoStyle="Attribute"
PluginType="StructureMapConfig.IIConstructor,StructureMapConfig"
PluggedType="StructureMapConfig.Constructor,StructureMapConfig"
test="false"/>
</StructureMap>
<startup>
<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
I keep getting a large stack trace when reading the config file, boiling down to this error:
Trying to visit parameter test of type System.Boolean in the
constructor for StructureMapConfig.Constructor, StructureMapConfig,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null --->
StructureMap.StructureMapException: StructureMap Exception Code: 205
Missing requested Instance property "test" for InstanceKey
"DefaultInstanceOfStructureMapConfig.IIConstructor,
StructureMapConfig, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null
The code definitely has a constructor argument called "test" and the destination type is correct - a boolean.
Can someone please give me guidance as to where I'm going wrong?
--
Note: I want to keep this in configuration only, as it will require a re-compile if this value is changed from "false" to "true", hence defeating the point of defining it in config.

Got it,
"MementoStyle" should be on the <StructureMap> element, not on the <DefaultInstance>

Related

Changing a path for logger using Form application and implementing it to Service with C#

I am working on a File Watcher service which has a form application in it too (2 different projects in same solution). So I am getting a path for where to save the log with Forms application. Then I put that in my app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="location" value="C:\Users\user\Documents" />
<add key="logLocation" value="C:\Users\user\Documents" /> <!-- this is where it changes save it-->
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>
And I have a Variables class where I define my variable.
using System.Configuration;
namespace FileWatchingService
{
public static class Variables
{
public static string FilePath { get; set; } = ConfigurationManager.AppSettings.Get("location");
public static string LogPath { get; set; } = ConfigurationManager.AppSettings.Get("logLocation");
}
}
Then I am trying put my LogPath in here:
using System;
using System.IO;
namespace FileWatchingService
{
public static class Logger
{
public static void Log(string message)
{
try
{
string _message = String.Format("{0} {1}", message, Environment.NewLine);
//File.AppendAllText(AppDomain.CurrentDomain.BaseDirectory + "logFile.log", _message);
File.AppendAllText(Variables.LogPath + "logFile.log", _message);
}
catch (Exception ex)
{
//Implement logging on next version
}
}
}
}
Problem is that my way does not work. What can I do to change my log files path?
Looking solely at the code, it seems you're missing a \ at the end of the LogPath value.
You could also do File.AppendAllText(Variables.LogPath + "\logFile.log", _message); or just define the LogPath itself, such as:
<appSettings>
<add key="location" value="C:\Users\user\Documents" />
<add key="logLocation" value="C:\Users\user\Documents\log.txt" /> <!-- the file itself -->
</appSettings>
Nevertheless, I would advise to just use a library for logging, instead of developing your own. Go with NLog or Serilog

How do I add custom ConfigurationSection to Assembly?

I've spent a few weeks trying to figure this out, this is a duplicate of a question I asked previously but did not get a response to, so I am refining the question here.
I've created a custom class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using System.Configuration;
namespace mssql_gui
{
public class TestConfigSection : ConfigurationSection
{
[ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
public TestConfigInstanceCollection Instances
{
get { return (TestConfigInstanceCollection)this[""]; }
set { this[""] = value; }
}
}
public class TestConfigInstanceCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new TestConfigInstanceElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((TestConfigInstanceElement)element).Key;
}
}
public class TestConfigInstanceElement : ConfigurationElement
{
[ConfigurationProperty("key", IsKey = true, IsRequired = true)]
public string Key
{
get { return (string)base["key"]; }
set { base["key"] = value; }
}
[ConfigurationProperty("value", IsRequired = true)]
public string Value
{
get { return (string)base["value"]; }
set { base["value"] = value; }
}
}
}
I've implemented it:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="testSection" type="mssql_gui.TestConfigSection"/>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
</startup>
<appSettings>
<add key="Data Source" value="localhost\SQLEXPRESS"/>
<add key="Initial Catalog" value="(empty)"/>
<add key="Integrated Security" value="SSPI"/>
</appSettings>
<testSection>
<add key ="testKey" value="tesValue"/>
</testSection>
</configuration>
and I have tried to access it, I am getting:
An error occurred creating the configuration section handler for testSection: Could not load type 'mssql_gui.TestConfigSection' from assembly 'System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
I understand that in the type, I should be declare an assembly dll, but I'm confused about that...because in the official instructions by MS, it says to create a new class for the handler:
Create a public class that inherits from the
System.Configuration.ConfigurationSection class.
Add code to define the section's attributes and elements.
Adding the class (at least through the visual studio interface) creates a .cs file, not a .dll assembly file, so how to I add that custom class to an assembly file in order to reference it in the <configSections> part of app.config?
If I understand correctly, you have problem with resolving what actually your Assembly is, since you are only creating .cs files that determine types that this file hold.
Assembly (in maybe not so accurate shorcut) is just the project you have in your solution. It will get compiled into its seperate assembly - the .dll you mentioned - later on.
When you add class to any .cs file in given project, on compile it will be included in project's assembly.
By default, if you won't provide assembly for configSection where its corresponding type should be found, App.config defaults to System.Configuration assembly - that's where you get your error from, since you've declared your section in your own assembly (== project).
Right click in Visual Studio on your project that holds App.config file and choose Properties to check its Assembly name:
Then add this name to your App.config section declaration. In my example its ConsoleApp1, so I will add it to configuration accordingly:
<configSections>
<section name="testSection" type="mssql_gui.TestConfigSection, ConsoleApp1"/>
</configSections>
Ensure that the type attribute of the section element matches the
manifest of the assembly (ensure that you specify both the correct
namespace and type name).
You need to add the name of the assembly (where the type relies) to the type attribute:
You'll get the name of the assembly from the AssemblyInfo.cs within the project where TestConfigSection class is defined.
<section name="testSection" type="mssql_gui.TestConfigSection, ASSEMBLYNAME"/>
Example asuming your assembly names mssql_gui
<section name="testSection" type="mssql_gui.TestConfigSection, mssql_gui"/>
You read it like this:
Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
TestConfigSection mySec = (TestConfigSection)config.Sections["testSection"];
See more details at MSDN
How to: Create Custom Configuration Sections Using ConfigurationSection

The data protection operation was unsuccessful on azure with autofac

I have been getting this error when trying to create or update a user.
The full error is:
The data protection operation was unsuccessful. This may have been caused by not having the user profile loaded for the current thread's user context, which may be the case when the thread is impersonating.
We use autofac in our application, so I after reading this article I created my own IdentityFactoryOptions like this:
public class IdentityFactoryOptions: IdentityFactoryOptions<UserProvider>
{
public IdentityFactoryOptions()
{
DataProtectionProvider = new DpapiDataProtectionProvider("ASP.NET Identity");
}
}
and then I created my own DataProtectionTokenProvider like this:
public class DataProtectionTokenProvider : DataProtectorTokenProvider<User>
{
public DataProtectionTokenProvider(IdentityFactoryOptions options) : base(options.DataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(6);
}
}
I registered both these as SingleInstances like this:
builder.RegisterType<IdentityFactoryOptions>().AsSelf().SingleInstance();
builder.RegisterType<DataProtectionTokenProvider>().AsSelf().SingleInstance();
and I injected the DataProtectionTokenProvider into my UserManager and assigned it in the managers constructor like this:
UserTokenProvider = dataProtectionTokenProvider;
But after doing all this, I still get the error.
I also read this article and saw you have to update your web.config too, so I added this:
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
But the error persists.
Does anyone have any solution for this? It is driving me mad....
Ok, I managed to fix this.
Using the first post I linked, I injected the IAppBuilder into my autofac module and removed my IdentityFactoryOptions class. So the registration now looks like this:
builder.Register(m => new DataProtectorTokenProvider(_app.GetDataProtectionProvider())).AsSelf().SingleInstance();
And the DataProtectorTokenProvider looks like this:
public class DataProtectorTokenProvider : DataProtectorTokenProvider<User>
{
public DataProtectorTokenProvider(IDataProtectionProvider dataProtectionProvider) : base(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(6);
}
}
Everything else I kept the same. This solved the issue.

BLToolkit using a MySQL connection

I am making an attempt to establish a connection to a MySQL server using BLToolkit, and have installed MySql.Data (6.5.4), BLToolkit (4.1.12) and BLToolkit.MySql (4.1.12) via NuGet. I can make a connection to a MSSQL server in a single line, but have had trouble with MySQL and ended up with the following configuration file ...
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<BLToolkit>
<dataProviders>
<add type="BLToolkit.Data.DataProvider.MySqlDataProvider" />
</dataProviders>
</BLToolkit>
<configSections>
<section name="BLToolkit" type="BLToolkit.Configuration.BLToolkitSection, BLToolkit.4" />
</configSections>
<connectionStrings>
<add name="Test"
connectionString="Data Source=localhost;Port=3306;Database=bltest;User ID=root;Password=root;"
providerName="MySql.Data.MySqlClient" />
</connectionStrings>
</configuration>
I have extended the DbManager class to implement a reference to the tables, and passed the name of the connection string into the base class. This is how I implemented this behaviour, which should be telling BLToolkit to load the connectionString from the configuration file ...
class BlDb : DbManager {
public BlDb()
: base("Test") {
return;
}
public Table<Car> Car { get { return GetTable<Car>(); } }
public Table<Make> Make { get { return GetTable<Make>(); } }
}
An exception, however, is thrown. The exception is "The type initializer for 'BLToolkit.Data.DbManager' threw an exception." with the inner exception being "Configuration system failed to initialize". How should I proceed? Please note that a similar question does exist on SO, Getting BLToolkit to work with MySQL, which might be a helpful reference for you but doesn't make any sense whatsoever to me. Is installing both NuGet packages not enough?
Firts you need to add the reference to the BLToolkit.Data.DataProvider.MySql.4.dll to your project. Then modify your extended DbManager class to look as the following
class BlDb : DbManager
{
public BlDb()
: base( new BLToolkit.Data.DataProvider.MySqlDataProvider(), "Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword" )
{
}
public Table<Car> Car { get { return GetTable<Car>(); } }
public Table<Make> Make { get { return GetTable<Make>(); } }
}
you can replace the hard-coded connection string and return it from your app.config file like ConfigurationManager.ConnectionStrings["Test"].ConnectionString

.NET 4.0 application on network share causes SecurityException

Today I experienced a weird problem while trying to remotely debug an application built for the .NET 4.0 runtime.
The application resides on a network share and executed by a remote machine. However the application crashes each time during load because of a SecurityException raised by a permission demand in the System.Configuration.ConfigurationManager.GetSection() method. I have not checked if other permission demands in the base class library also cause a security exception but in all cases this shouldn't be happening with the new CLR.
The application is running in full trust (checked it while debugging and as usual this must be always true for intranet applications in CLR 4.0) so I am clueless how a permission demand can cause an exception in this case. When built against the 3.5 SP1 runtime (which first introduced full trust for network shared apps by default) everythings runs as expected.
I pasted the sample code below. Any help is greatly appreciated.
using System;
using System.Configuration;
namespace ConsoleApplication1
{
public sealed class AssetsSection : ConfigurationSection
{
private static readonly ConfigurationProperty s_propPath;
private static readonly ConfigurationPropertyCollection s_properties;
static AssetsSection()
{
s_propPath = new ConfigurationProperty("path", typeof(String));
s_properties = new ConfigurationPropertyCollection()
{
s_propPath
};
}
public static AssetsSection Get()
{
return (AssetsSection) ConfigurationManager.GetSection("test/assets");
}
protected override ConfigurationPropertyCollection Properties
{
get
{
return s_properties;
}
}
public String Path
{
get
{
return (String) base[s_propPath];
}
set
{
base[s_propPath] = value;
}
}
}
class Program
{
static void Main(String[] args)
{
Console.WriteLine(AssetsSection.Get().Path);
Console.ReadLine();
}
}
}
And the App.config file;
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="test">
<section name="assets" type="ConsoleApplication1.AssetsSection, ConsoleApplication1"/>
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/>
</startup>
<test>
<assets path="..\Assets"/>
</test>
</configuration>
Try loading the configuration first and open your section on that:
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
AssetsSection configSection = (AssetsSection)config.GetSection("test/assets");
I ran into the same issue with .NET 4 and this works for me.
This is due to a known bug in .NET 4.0 when running the application from a network share.
The follow code fails with a SecurityException. Note that it only fails when you have defined a custom type for the section like in this example AssetsSection:
ConfigurationManager.GetSection("test/assets");
One fix is the solution suggestion by Timo to use a different API. Another solution is to apply the patch provided by Microsoft.
The bug and the related hotfix is filed under KB2580188.
If you add your own class to map the section like this:
[XmlRoot("Interface")]
public class MySectionClass
{
[XmlAttribute()]
public string MyAttr1
{
get;
set;
}
public string MyAttr2
{
get;
set;
}
}
You can use this code:
ConfigurationSection configSection =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).
GetSection("MySection");
XmlSerializer xs = new XmlSerializer(typeof(MySectionClass));
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(configSection.SectionInformation.GetRawXml());
XmlNodeReader xnr = new XmlNodeReader(xdoc.DocumentElement);
MySectionClass section = (MySectionClass)xs.Deserialize(xnr);
I'm speculating here, but I suspect it's your configuration file that's not trusted.
In your case, your configuration file is referencing a type ConsoleApplication1.AssetsSection that does not have a strong name that could be used as evidence.
Can you provide more details and the exact error message.

Categories