Winform app: Compiled App.Config-like file? - c#

I would like to know if there is some kind of built-in compiled "App.Config" file?
The goal is to be able to have one of our library which can have some of its default values overriden when being used in some client application.
Thoses DLL are loaded dynamically, so I cannot just give a parameter in the constructor.
I don't want to use the App.config file because the user can edit those values(otherwise it would have been just fine).

There are several different ways to solve this.
If you like the idea of config-files, but do not want to have it accessible by end users in the compiled application, perhaps you can create your own settings-file in a format that suits your needs, and include it as an embedded resource?
An upside of this would be that you can access it as a regular XML or config file or whatever in Visual Studio, while it will be hidden from the end user. Personally I think I would prefer this to using special code / classes to store config-data.
To include a file as an embedded resource, include it into one of your Visual Studio projects, right click the included file and select Properties. Now under Build Action, select Embedded Resource. When you build your project now, the file will be included internally in the produced .dll-file.
I'm sure you'll be able to find lot's of info about how to access an embedded resource from code. As an example, there are some useful examples in this SO question. Note especially this answer, which also mentions an alternative way to include a resource.

Expanding on my comment... you could just make an interface for a settings class with hardcoded values, and then make different implementations of that interface. To actually change which one to use, all you'd need to do is comment/uncomment the line that instantiates an object into your settings variable before you build the dll:
public class MainDllProject
{
ISettings m_Settings;
public MainDllProject()
{
// Change this before compiling
this.m_Settings = new DebugSettings();
//this.m_Settings = new DeploySettings();
// use settings from the settings class
String setting1 = this.m_Settings.Setting1
Int32 setting2 = this.m_Settings.Setting2
//...
}
}
public interface ISettings
{
String Setting1 { get; }
Int32 Setting2 { get; }
}
public class DebugSettings: ISettings
{
public String Setting1
{ get { return "data_debug";} }
public Int32 Setting2
{ get { return 2;} }
}
public class DeploySettings: ISettings
{
public String Setting1
{ get { return "data_deploy";} }
public Int32 Setting2
{ get { return 1;} }
}
On finding "a built-in way of solving this", as you said, maybe this will be useful for you...
You can actually use the Visual Studio build configuration manager to build with different settings. Using the #If directives, you can automatically make it select which lines of code to use based on the configuration. A simple example based on the default debug configuration, which adds the "DEBUG=True" variable automatically:
public MainDllProject()
{
#If DEBUG Then
this.m_Settings = new DebugSettings();
#ElseIf
this.m_Settings = new DeploySettings();
#End if
}
You can actually make your own custom-named variables to check on just like that DEBUG one: after making a configuration, open the Project properties window, go to the Compile tab, select that specific configuration in the dropdown, and then at the bottom select "Advanced Compile Options". In there is a line "Custom constants" in which you can add such variables. For simple if-statements, you can just make a boolean like "CLIENTDEPLOY=True", and then you can use #If CLIENTDEPLOY Then in your code.

Related

How to get the .resx file strings in asp.net core

How to get the resx file strings in asp.net core?
I get the strings using ResxResourceReader in mvc. But I can't get the same in asp.net core.
.NET Core changed how resource files work in a way I feel is sub-par and confusing (took me days to figure out), but this is what you need to do:
Add the following code to Startup.cs - Note: Change what languages you support and the ResourcePath of "Resources" will just be the folder you store the .resx files later.
As JustAMartin said in comments: If you are planning to put your SharedResource file inside Resources folder and set its namespace to end with Resources then do not set o.ResourcesPath = "Resources" just use services.AddLocalization(), otherwise it will start looking in Resources.Resources folder, which doesn't exist.
services.AddLocalization(o => o.ResourcesPath = "Resources");
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("en-GB"),
new CultureInfo("de-DE")
};
options.DefaultRequestCulture = new RequestCulture("en-US", "en-US");
// You must explicitly state which cultures your application supports.
// These are the cultures the app supports for formatting
// numbers, dates, etc.
options.SupportedCultures = supportedCultures;
// These are the cultures the app supports for UI strings,
// i.e. we have localized resources for.
options.SupportedUICultures = supportedCultures;
});
Create a folder in whatever project you want to store the resx files in - default, call it "Resources".
Create a new resx file with the specific culture and the file name you'll look up later: If you had a shared one, you could do: SharedResource.en-US.resx. Then turn off auto-code generation as it is useless now.
Create a class called "SharedResource" in the same location as your resx file. It can be blank, it just needs to be there so you can reference it later.
Wherever you want to use your resource, IoC inject (in this example) IStringLocalizer< SharedResource > with name "_localizer" or something.
Finally, you can reference an entry in the Resource file by doing _localizer["My_Resource_Name"]
Add another language by creating a new resx file named "SharedResource.de-DE.resx" or whatever, in that same folder.
The "Resource" folder will be used across all assemblies to look all of them up. Thus, this folder could end up pretty cluttered, especially if you start getting view specific stuff in here.
I see what the devs were trying to do here, but they gave up too much to get there. People can code and add translation stuff without actually translating anything. They made it easier for devs to have translation in mind from the start, but they end up making it way more work for the devs that actually use translations. Now we can't auto generate anything. We have to IoC inject a reference to the translations in order to access them (no more static unless you want to use the ServiceLocater anti-pattern). All the names are hard-coded strings, so now if you spell a translation wrong it'll just spit back the string you gave it, defeating the purpose of having a translation in the first place, meaning you'll probably need a wrapper around this so you don't rely on constants everywhere.
I can't believe anyone thought this was a good idea, to be honest. Why bend over backwards for devs that don't care about translations, anyway?
I ended up creating a wrapper around this style. The only good thing about this is that if you decide you want to get resources from the database, no code change above will be necessary, but now you have to add the resource entry, add it to the interface, and then implement it to pull it back out again. I used nameof() so I didn't need to use constants, but this is still brittle as if the property name or resx file name changes, it'll break the translation without any sort of crash - I will probably need an integration test to ensure I don't get the same value I send in:
public interface ICommonResource
{
string ErrorUnexpectedNumberOfRowsSaved { get; }
string ErrorNoRecordsSaved { get; }
string ErrorConcurrency { get; }
string ErrorGeneric { get; }
string RuleAlreadyInUse { get; }
string RuleDoesNotExist { get; }
string RuleInvalid { get; }
string RuleMaxLength { get; }
string RuleRequired { get; }
}
public class CommonResource : ICommonResource
{
private readonly IStringLocalizer<CommonResource> _localizer;
public CommonResource(IStringLocalizer<CommonResource> localizer) =>
_localizer = localizer;
public string ErrorUnexpectedNumberOfRowsSaved => GetString(nameof(ErrorUnexpectedNumberOfRowsSaved));
public string ErrorNoRecordsSaved => GetString(nameof(ErrorNoRecordsSaved));
public string ErrorConcurrency => GetString(nameof(ErrorConcurrency));
public string ErrorGeneric => GetString(nameof(ErrorGeneric));
public string RuleAlreadyInUse => GetString(nameof(RuleAlreadyInUse));
public string RuleDoesNotExist => GetString(nameof(RuleDoesNotExist));
public string RuleInvalid => GetString(nameof(RuleInvalid));
public string RuleMaxLength => GetString(nameof(RuleMaxLength));
public string RuleRequired => GetString(nameof(RuleRequired));
private string GetString(string name) =>
_localizer[name];
}
The old way version (like in asp.net) is to create a default resource file like MyResources.resx and other files for different cultures MyResources.fr.resx, ... and retrieve the values from it with MyResources.MyValue1 . Creating MyResources.resx will generate a .cs file with all your resource values as static properties.
.Net Core recommends to work with IStringLocalizer<T> where T is a class created by you that match with the name of your resource files. You can start development without any resource file and add them later. You have to inject (IStringLocalizer< MyResources > localizer) on your controller and than get the value with _localizer["MyValue1"];
You can take a look over the .net core official documentation here
For a more direct replacement, I have created ResXResourceReader.NetStandard, which repackages ResXResourceReader and ResXResourceWriter for .NET Standard (which means .NET Core too).

Save Settings in Dependent Dll

I have a Windows Service and a dependent Dll, I chose this way so that Dll can be invoked by any application, Winforms, WPF etc.
Now I am stumped on what is the best practice to Save settings in the Dll, I used User Settings in the dll and Saved them from calling App.
public class Preferences
{
public static string IPAddress
{
get { return Settings.Default.IPAddress; }
set { Settings.Default.IPAddress = value; }
}
}
and then in my service/Winforms Code, I wrote
Preferences.IPAddress = "XX.XX.XX.XX"
Someone told me this is not how you should do it . I wanted to know is this not the correct approach

Where does OnAfterInstall event go?

I've had serious problems on how to solve this: I don't know where the OnAfterInstall event goes.
Let me explain myself. I created a C# project which compiles perfectly and built in Release mode. After that, I've created a Setup Project using the wizard. I have added an extra dialog, which lets the user choose between two languages. Now, my problem is that I want to store that language into the registry (or app.config file, the easier the better), and I've read that you need to detect it within the OnAfterInstall method in an inherited class of Installer.
Now, where should I put that class? Logic tells me it goes in the C# project, but it complains that neither Context nor Installer class exist. When I add this class to the Setup Project, it doesn't complain, but it doesn't work after that. Here's the class.
using System;
using System.Configuration.Install;
public class Install : Installer
{
public Install()
{
}
protected override void OnAfterInstall(IDictionary savedState)
{
string lang = Context.Parameters["lang"];
RegistryKey key = Registry.LocalMachine;
using (key = key.CreateSubKey(#"SOFTWARE\MyCompany\MyApp"))
{
key.SetValue("lang", lang);
key.Close();
}
base.OnAfterInstall(savedState);
}
}
PS: I'm already passing lang as CustomActionData using /lang=[LANG] (where LANG is the radio value)
First, you should add the RunInstallerAttribute to you class.
[RunInstaller(true)]
public class Install : Installer
...
Next, put the installer in a separate project (class library), e.g. MyCustomInstaller.
Finally, add the primary output of this project to a custom action in the custom actions editor of the setup project.
It's up to you in which custom action you want to use.

App.config multi-project access strategies

My current solution has 3 project with 2 app.config (one for common settings and another for service settings). As of now I'm simply creating static classes to act as a mediator to access values. I do this so I don't have to write ConfigurationManager.AppSettings["SomeKey"] everywhere. This works fine until you want to access an app.config file from a different project.
Here is what I'm currently doing (all properties omitted for brevity).
public class ServiceConfiguration
{
public static readonly string SyncEvery = ConfigurationManager.AppSettings["SyncEveryMinutes"];
}
How can I access an app.config file located in another project? I thought perhaps setting VS to copy the file to the output directory would do the trick however my configuration object is still null.
I can't imaging many good reasons to read another app's configuration in the first place, it just opens a can of worms that isn't worth dealing with.
Expose a class that exposes the project's configured values as properties, and access them from a consuming class.
public class FirstProjectClass
{
public static int SyncEveryMinutes
{
get { return (int)ConfigurationManager.AppSetting["SyncEveryMinutes"] };
}
}
public class SecondProjectClass
{
public void ShowConfigedValue()
{
Console.Writeline("Syncing every {0} minutes", FirstProjectClass.SyncEveryMinutes);
}
}
if you've got complex configuration requirements you can also look into custom configuration sections
ConfigurationManager.OpenExeConfiguration can be helpfull:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.openexeconfiguration.aspx
Also: what Jason said - it is usually a bad idea.

Sharing settings between applications

I have multiple .NET assemblies that all need to share common user settings, such as preferences, user names, etc. One is a WPF application, another is a console application, and the third is an Office Add-in. All of these settings are user-scope.
Only the WPF application needs to be able to change settings. The rest just read them.
Ideally, I'd like to use the .NET configuration framework. I'm not sure how to do this though. If I add Settings to the WPF application, how can the other applications find the user.config file?
Is it just easier to create a class library and use IsolatedFileStorage and serialize my settings?
Any advice would be greatly appreciated.
You can implement your custom settings class, inheriting ApplicationSettingsBase. As a good start, you can add the default User Settings file to a sample project (Right click on the project -> Properties -> Settings -> This project does not contain a default settings file. Click here to create one.). Add a user-scoped setting and investigate the structure of the designer-generated Settings.Designer.cs file:
namespace ConsoleApplication1.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("John Doe")]
public string Name {
get {
return ((string)(this["Name"]));
}
set {
this["Name"] = value;
}
}
}
}
In your custom implementation, you will not be limited to the designer-generated access modifiers, so you can implement the Settings class as internal with internal setters, visible only to the needed assemblies, or whatever fits your needs.
Of course, you can always implement your custom serialize/deserialize mechanism, but you will lose the funcionality provided by ApplicationSettingsBase's Updgrade, Reload, and Reset methods. If you don't need any of these, this could be the cleaner approach.
I would recommend you to create service to provide and update user info and or preferences. It will be better architecture, cleaner solution and it will be easier to maintain and extend.

Categories

Resources