SettingsProviderAttribute replacement for application-level custom SettingsProvider - c#

In a .NET application, if you have specific Settings need, such as storing them in DB, then you could replace LocalFileSettingsProvider with a custom settings provider of your, examples:
Create a Custom Settings Provider to Share Settings Between Applications
Creating a Custom Settings Provider
To declare the settings class (the one that inherits ApplicationSettingsBase) that you want to use a specific provider, you decorate it with SettingsProviderAttribute and pass your provider type as a parameter [SettingsProvider(typeof(MyCustomProvider))], otherwise it will use the default LocalFileSettingsProvider.
My question: Is there a configuration or a trick I could use to force my application to use my custom provider through-out the application without using an attribute?
The reason is that I am loading plugins via MEF and the plugins might be written via 3rd party and I don't want them to be concerned with how settings are being dealt with.

You could try the following code. It basically changes the default provider to an arbitrary one during the construction of the Settings object. Note that I never tested this code.
internal sealed partial class Settings {
public Settings() {
SettingsProvider provider = CreateAnArbitraryProviderHere();
// Try to re-use an existing provider, since we cannot have multiple providers
// with same name.
if (Providers[provider.Name] == null)
Providers.Add(provider);
else
provider = Providers[provider.Name];
// Change default provider.
foreach (SettingsProperty property in Properties)
{
if (
property.PropertyType.GetCustomAttributes(
typeof(SettingsProviderAttribute),
false
).Length == 0
)
{
property.Provider = provider;
}
}
}
}

Related

Ninject Providers: what is the correct way to resolve dependencies

In my application I can register different datasources by name. These data-sources each have a few string properties required, along with a set of other dependencies, but are otherwise the same, so take a few different standard implementations.
To construct instances of each datasource when requested, I create a binding to an instance of a Provider<T> which is initialized with the information required to access that data-source. The provider looks something like the below:
public class StandardListProvider<T> : Provider<IListExecutor<T>>
where T : new()
{
public string Name { get; set; }
public string ListMethod { get; set; }
public StandardListProvider(string name, string listMethod)
{
Name = name;
ListMethod = listMethod;
}
protected override IListExecutor<T> CreateInstance(IContext context)
{
var connector = (IInternalConnector)context.Kernel.GetService(typeof(IInternalConnector));
return new StandardListExecutor<T>(connector, Name)
{
ListMethodName = ListMethod
};
}
}
The problem is with resolving dependencies of the StandardListExecutor<T> like IInternalConnector. Obviously I can construct them manually, or request them from context.Kernel as I am in my example (and suggested by Ninject Providers -> Get another dependency inside the provider), but this results in a request with no Target information, which is not ideal if we want to perform contextual bindings for the dependencies of StandardListExecutor.
I've tried playing with context.Request.CreateChild(...), but this appears to require reflection on every activation to create a ParameterTarget. There doesn't appear to be much information about this in the Ninject docs either.
My question is: What is the correct way to resolve/request dependencies, or other services like this from within the activation process of an existing binding?
Edit
The requests themselves are made via the Ninject.Mvc hookups into the System.Web.Mvc controller activation process.
You should be able to use the extension Ninject.Extensions.ContextPreservation. Specifically the extension method IContext.ContextPreservingGet(...):
var connector = context.ContextPreservingGet<IInternalConnector>();
However, personally I think that creating specific settings types is the better choice - because it's the simpler idea.

How can share an object between an ASP.Net page and other projects

I have an object (ClientConfiguration) that I use on almost every page on my site, as well as in many methods that exist in related projects that get compiled into the website.
What I am doing now is creating and populating the object on each page load, and storing it in the HttpContext. This works great for anything in the UI project; and for anything in the dll projects, I pass the ClientConfiguration to any methods that may need to use it.
What I would rather do is have a "global" property that is shared among all of the related projects so I don't have to pass it around.
Is there a good way to accomplish this?
After you add System.Web.dll as reference in your other library projects, you can access the object in HttpContext directly, no need to pass as parameter.
This depends a bit on where initial configuration is being stored (xml file, database or something else) but you’ll see the point.
If these are global configuration settings that are same for all application users you can create a class like this
public class Config
{
public static ClientConfiguration Current
{
get
{
if (HttpContext.Current.Application["clientconfig"] == null)
{
//Fill object from database
}
return HttpContext.Current.Application["clientconfig"] as ClientConfiguration;
}
set
{
//store object in database
//invalidate what is stored in application object
//so that it will be refreshed next time it's used
HttpContext.Current.Application["clientconfig"] = null;
}
}
}
This will store the ClientConfiguration in global Application object and make it available in all pages so you don’t have to create it in page load.
You can just use it like this
private void Foo()
{
ClientConfiguration config = Config.Current;
}
If you have multiple projects that need to share same data then it’s best to store the object in database or in shared XML file and create new class library project so that you can just include reference to the Config class.

EF 5 + SQL Server CE 4: How to specify custom location for database file?

I am developing a client system that needs a small local database.
I want to avoid installation of SQL Server Express and have decided to go with SQL Server 4.
I use Entity Framework 5 for data access and have created my custom context.
Everything works fine in development where I can use app.config to either set specific file location or dynamic Data Source=|DataDirectory|\MyDatabase.sdf.
But on deploy I want the database to be located in the users documents folder:
\My Documents\ApplicationName\MyDatabase.sdf
How can I do that?
All I need is actually to be able to set custom connection string in code!
This is what I tried so far:
private MyApplicationDataContext(string connectionString)
: base(connectionString)
{
}
public static MyApplicationDataContext CreateInstance()
{
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
//var connectionString = string.Format("provider=System.Data.SqlServerCe.4.0;provider connection string=\"Data Source={0}\"", path);
var connectionString = string.Format("Data Source={0}", path);
return new MyApplicationDataContext(connectionString);
}
As you can see I tried two kinds of connection strings but both caused exceptions.
Keyword not supported: 'provider'.
and
The provider did not return a ProviderManifestToken string.
Ah, I finally got it right!
I include the adjusted code if someone else has the same problem.
The trick was to set the connection string on the Database.DefaultConnectionFactory
private MyApplicationDataContext()
{ }
public static MyApplicationDataContext CreateInstance()
{
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
// Set connection string
var connectionString = string.Format("Data Source={0}", path);
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0", "", connectionString);
return new MyApplicationDataContext();
}
In EF 6 this can be done with DbConfiguration:
App.config:
<entityFramework codeConfigurationType="MyDbConfiguration, MyAssembly">
</entityFramework>
And inside your assembly create a class like:
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices(SqlCeProviderServices.ProviderInvariantName, SqlCeProviderServices.Instance);
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
var connectionString = string.Format(#"Data Source={0}",path);
SetDefaultConnectionFactory(new SqlCeConnectionFactory(SqlCeProviderServices.ProviderInvariantName, "", connectionString));
}
}
The SqlCeConnection instance is used to connect to Sql Ce database file. If I want to connect to MSSQL database, I will use SqlConnectionStringBuilder and SqlConnection instances to build my DbConnection instance.
// The ctor for `DbConnection`.
private MyApplicationDataContext(DbConnection conn) : base(conn, true) {}
public static MyApplicationDataContext CreateInstance()
{
var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
// Connection string builder for `Sql ce`
SqlCeConnectionStringBuilder sb = new SqlCeConnectionStringBuilder();
sb.DataSource = path;
// DbConnection for `Sql ce`
SqlCeConnection dbConn = new SqlCeConnection(sb.ToString());
return new MyApplicationDataContext(dbConn);
}
For EF 6
I arrived at this answer thanks to Matthias's post, but included some worthwhile info from Entity Framework Code-Based Configuration (EF6 onwards).
To specify a custom database location you will need to do some configuration. Configuration for an Entity Framework application can be specified in a config file (app.config/web.config) or through code. The latter is known as code-based configuration. Given that my project required the database location to be set dynamically, I went with the code-based configuration, which is described below.
(Note that the config file takes precedence over code-based configuration. In other words, if a configuration option is set in both code and in the config file, then the setting in the config file is used.)
According to EF documentation (above link) there are 4 approaches to implement your custom configuration,which include: Using DbConfiguration, Moving DbConfiguration, Setting DbConfiguration explicitly, and Overriding DbConfiguration.
Using DbConfiguration
Code-based configuration in EF6 and above is achieved by creating a subclass of System.Data.Entity.Config.DbConfiguration. The following guidelines should be followed when subclassing DbConfiguration:
Create only one DbConfiguration class for your application. This class specifies app-domain wide settings.
Place your DbConfiguration class in the same assembly as your DbContext class. (See the Moving DbConfiguration section if you want to change this.)
Give your DbConfiguration class a public parameterless constructor.
Set configuration options by calling protected DbConfiguration methods from within this constructor.
Following these guidelines allows EF to discover and use your configuration automatically by both tooling that needs to access your model and when your application is run.
Example (modified from Matthias's answer):
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetProviderServices(SqlCeProviderServices.ProviderInvariantName, SqlCeProviderServices.Instance);
//var directory Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var directory = #"C:\Users\Evan\Desktop\TestFolder"; // Directory may or may not already exist
Directory.CreateDirectory(directory); // create directory if not exists
var path = Path.Combine(directory, #"ApplicationName\MyDatabase.sdf");
var connectionString = string.Format(#"Data Source={0}",path);
SetDefaultConnectionFactory(new SqlCeConnectionFactory(SqlCeProviderServices.ProviderInvariantName, "", connectionString));
}
}
Note that the config file does not need to be altered unless there are existing configuration settings that override your custom configuration. Also, I changed the directory to illustrate that While EF is capable of creating a new database if it doesn't already exist, it will not create parent directories, which is why I included this line: Directory.CreateDirectory(directory). Given that this approach worked for my project I didn't explore the remaining 3 configuration methods, but you can find info on them in the link provided above and I will include the documentation here as a reference in case the link breaks.
Moving DbConfiguration
There are cases where it is not possible to place your DbConfiguration class in the same assembly as your DbContext class. For example, you may have two DbContext classes each in different assemblies. There are two options for handling this.
The first option is to use the config file to specify the DbConfiguration instance to use. To do this, set the codeConfigurationType attribute of the entityFramework section. For example:
<entityFramework codeConfigurationType="MyNamespace.MyDbConfiguration, MyAssembly">
...Your EF config...
</entityFramework>
The value of codeConfigurationType must be the assembly and namespace qualified name of your DbConfiguration class.
The second option is to place DbConfigurationTypeAttribute on your context class. For example:
[DbConfigurationType(typeof(MyDbConfiguration))]
public class MyContextContext : DbContext
{
}
The value passed to the attribute can either be your DbConfiguration type - as shown above - or the assembly and namespace qualified type name string. For example:
[DbConfigurationType("MyNamespace.MyDbConfiguration, MyAssembly")]
public class MyContextContext : DbContext
{
}
Setting DbConfiguration explicitly
There are some situations where configuration may be needed before any DbContext type has been used. Examples of this include:
Using DbModelBuilder to build a model without a context
Using some other framework/utility code that utilizes a DbContext
where that context is used before your application context is used
In such situations EF is unable to discover the configuration automatically and you must instead do one of the following:
Set the DbConfiguration type in the config file, as described in the
Moving DbConfiguration section above
Call the static DbConfiguration.SetConfiguration method during
application startup
Overriding DbConfiguration
There are some situations where you need to override the configuration set in the DbConfiguration. This is not typically done by application developers but rather by thrid party providers and plug-ins that cannot use a derived DbConfiguration class.
For this, EntityFramework allows an event handler to be registered that can modify existing configuration just before it is locked down. It also provides a sugar method specifically for replacing any service returned by the EF service locator. This is how it is intended to be used:
At app startup (before EF is used) the plug-in or provider should
register the event handler method for this event. (Note that this
must happen before the application uses EF.)
The event handler makes a call to ReplaceService for every service
that needs to be replaced.
For example, to repalce IDbConnectionFactory and DbProviderService you would register a handler something like this:
DbConfiguration.Loaded += (_, a) =>
{
a.ReplaceService<DbProviderServices>((s, k) => new MyProviderServices(s));
a.ReplaceService<IDbConnectionFactory>((s, k) => new MyConnectionFactory(s));
};
In the code above MyProviderServices and MyConnectionFactory represent your implementations of the service.
You can also add additional dependency handlers to get the same effect.
Note that you could also wrap DbProviderFactory in this way, but doing so will only effect EF and not uses of the DbProviderFactory outside of EF. For this reason you’ll probably want to continue to wrap DbProviderFactory as you have before.
You should also keep in mind the services that you run externally to your application - e.g. running migrations from Package Manager console. When you run migrate from the console it will attempt to find your DbConfiguration. However, whether or not it will get the wrapped service depends on where the event handler it registered. If it is registered as part of the construction of your DbConfiguration then the code should execute and the service should get wrapped. Usually this won’t be the case and this means that tooling won’t get the wrapped service.

How should I create a user preference attribute?

In my current asp.net mvc project a user should be able to log in. When logged in a set of preferences can optionally be set. Some of those preferences are general (e.g. prefered site language etc), but some are specific to this project only (pre-defined query filtering etc).
Since these preferences are present in a lot of different places of my site I would define this as cross-concern. Preferably I would have an attribute handle this rather than every action on it's own.
How can I design such an attribute that is generic enough to re-use in future projects yet knows enough about the current project to use all the project specific settings too?
--EDIT--
Getting and setting the preferences is not the problem. I connected a UserSettings class to the asp.net profile provider.
My problem is how to pull this cross concern out of my controllers into an attribute.
independently if you are storing that preferences in a text file, xml or database, wouldn't be easier to create a class (for example Utility.UserPreferences) that will load those preferences from the user and store them in a Session variable, then using an enum call them to retrieve / update
namespace Utility
{
public class UserPreferences
{
public UserPreferences(int userID)
{
// Load Preferences from text file, xml file or DB
string loadedPreferences = "us|20|yes|no";
HttpContext.Current.Session["userPreferences"] = loadedPreferences;
}
public void Savepreferences(string[] pref, int userID)
{
// Save preferences for that user
}
public static string GetPreferences(PreferencesType type)
{
string[] pref = HttpContext.Current.Session["userPreferences"].ToString().Split('|');
switch (type)
{
case PreferencesType.Language: return pref[0];
case PreferencesType.ShowHowManyResults: return pref[1];
case PreferencesType.ShowNavigation: return pref[2];
case PreferencesType.GetEmailAlerts: return pref[3];
}
}
public enum PreferencesType
{
Language, ShowHowManyResults, ShowNavigation, GetEmailAlerts
}
}
}
then ...
// Login sucessfully...
Utility.UserPreferences pref = new Utility.UserPreferences(CurrentUser.ID);
// to retrieve a preference
string language = Utility.UserPreferences.GetPreferences(
Utility.UserPreferences.PreferencesType.Language,
CurrentUser.ID);
it's just an idea... the way I would do it... it's simple and it's project(s) wide, cause you just need to change the class to hold more preferences...
For user preferences you should use the ASP.NET Profile Provider framework. Some resources:
ASP.NET Profile Providers
Implementing a Profile Provider
StackOverflow: Implementing Profile Provider in ASP.NET MVC
StackOverflow: Implementing a Custom Profile Provider in ASP.NET MVC
You could build attribute-based user preference handling on top of the Profile provider framework, but I imagine that sort of thing would be specific to your app.

Custom MembershipProvider Initialize method

When overriding the MembershipProvider and calling it directly, is there a way to fill the NameValueCollection config parameter of the Initialize method without manually looking through the config file for the settings?
Obviously this Initialize is being called by asp.net and the config is being filled somewhere. I have implemented my own MembershipProvider and it works fine through the build in controls.
I would like to create a new instance of my provider and make a call to it directly, but I don't really want to parse the .config for the MembershipProvider, it's connection string name and then the connection string if it's already being done somewhere.
tvanfosson- Thanks for the help. (if I had the 15 points necessary I would vote you up)
From your link I was able to figure it out. It turns out the second parameter to the Initialize proceedure was the list of parameters from the provider and could be reached in the following way:
string configPath = "~/web.config";
Configuration config = WebConfigurationManager.OpenWebConfiguration(configPath);
MembershipSection section = (MembershipSection)config.GetSection("system.web/membership");
ProviderSettingsCollection settings = section.Providers;
NameValueCollection membershipParams = settings[section.DefaultProvider].Parameters;
Initialize(section.DefaultProvider, membershipParams);
Not sure why you want to create a new one, but if you create it yourself, you'll need to read the web config and get the values yourself to pass to Initialize() as this is done outside the class. I'm sure, though, that there is already a section handler for this section so it should be just a matter of doing:
MembershipSection section = WebConfigurationManager.GetSection("membership");
Then find your provider and accessing its properties to construct the NameValueCollection. I don't think you will have to write any code to parse the configuration section.
Here is the MembershipSection documentation at MSDN. Drill down from there.
In any case you shouldn't create instance of MembershipProvider. It is creating and initializating by standard asp.net infrastructure. You can access to it by code like this one:
var customerMembership = Membership.Provider;

Categories