Is it possible to retain the logical layout of the project file?
If I have 2 embedded resources laid out like the following
When I call Assembly.GetManifestResourceNames(), the two of them get named:
MyLibrary._MyItems.SubItems.SubItem1.xml
MyLibrary._MyItems.SubItems.SubItem2.xml
However, this gives me no real insight into how they're logically ordered in my project. For all I know, they could both be in the same directory.
I worry that this may not be possible, because if I name the files like so, my app will not even compile:
I want to be able to distinguish between
MyLibrary._MyItems\SubItems\SubItem1.xml
and
MyLibrary._MyItems\SubItems.SubItem2.xml
Similar question, but less detail than I am looking for
Exact duplicate, no traction
One simple solution would be to come up with a convention to indicate where a file name starts. You could then "safely" assume the rest of the name indicates the folder structure.
However, since these are embedded resources you could create a T4 script (or some similar piece of code) that runs prior to compilation that examines the folder structure and builds a class detailing that structure.
Here is an example of what the generated class could look like.
public static class MyResources
{
private readonly Dictionary<String, String> ResourceNameToDirPathMappingSetSource = new Dictionary<String, String>();
static MyResources()
{
ResourceNameToPathMappingSetSource.Add("MyLibrary._MyItems.SubItems.SubItem1.xml", #"MyLibrary._MyItems\SubItems");
ResourceNameToPathMappingSetSource.Add("MyLibrary._MyItems.SubItems.SubItem2.xml", #"MyLibrary._MyItems");
}
public static IReadOnlyDictionary<String, String> ResourceNameToDirPathMappingSet
{
get
{
return ResourceNameToDirPathMappingSetSource;
}
}
}
My assumption being that you want to take the resource name and determine the folder path it was originally in. The dictionary of course could contain whatever values (read: custom class) you need to map to.
Related
I am creating an inf file parser (windows drivers, in theory similar to infs, in reality quite different).
I have a parent InfFile class which contains the structured but unparsed data in the form of Sections and Keys (I call it unparsed because it's not been converted into anything more useful than just Sections and Keys, as oppose to the rest of the data that comes after), but also gives access to some parsed data, such as SupportedOperatingSystems, Folders, Folder.Files etc.
The problem I'm facing is that in inf file can reference 4 types of Folders: default, X86, Ia64 and Amd64. So, it felt logical to me to try to shift all of the folder related code to a InfFolders class, with properties Default, X86, Ia64 and Amd64 (I can't simply merge them).
However, in order to figure out which folders are referenced in it, I need access to both the Sections and Keys properties of the InfFile class. That means either:
my InfFolders class contains a reference to its parent (which I think is probably not the best solution),
InfFile parses the data first and then passes it to the InfFolders as part of the constructor, but that means all of the Folder logic now lives in the InfFile class again,
I pass the needed Sections and Keys to InfFolders as part of the constructor, and have the logic to sort them into usable objects in there.
What is the best way to do this and what is best practice in situations like this?
An idea of what the InfFolders class looks like is this:
public class InfFolders
{
public IReadOnlyCollection<InfSourceFolder> Default()
{
return GetFolders("SourceDiskNames").ToList().AsReadOnly();
}
public IReadOnlyCollection<InfSourceFolder> X86()
{
return GetFolders("SourceDiskNames.X86").ToList().AsReadOnly();
}
public IReadOnlyCollection<InfSourceFolder> Ia64()
{
return GetFolders("SourceDisksNames.Ia64").ToList().AsReadOnly();
}
public IReadOnlyCollection<InfSourceFolder> Amd64()
{
return GetFolders("SourceDisksNames.Amd64").ToList().AsReadOnly();
}
private IEnumerable<InfSourceFolder> GetFolders(string sectionName)
{
return <InfFile reference>[sectionName]?.Keys?.Select(
x =>
new InfSourceFolder(Convert.ToInt32(x.Name),
new FileInfo(<InfFile reference>.FilePath).DirectoryName + x.Values.ElementAtOrDefault(3)));
}
}
As you can see in the method GetFolders() there are 2 places where a reference to the parent is needed.
I could shift all of this code to InfClass and remove the InfFolders class altogether, but don't think that makes for a very good model either. The way I see it is there 4 properties all relate to the same thing, so they should be grouped together.
I have made a simple localization of messages. All messages are stored in the static class Lng
public static partial class Lng
{
public static readonly string AppName = "My application";
public static class Category1
{
public static readonly string ConfirmDelete = "Are you sure want to delete?";
}
}
In code usage is as simple as referencing fields
MessageBox.Show(Lng.Category1.ConfirmDelete, ...
Then there is a manager, which does following:
language selection
load corresponding translation
updating fields via reflection
export currently selected language on application exit for an update (in case if default language is selected - to create first translation for any other language)
It's irrelevant of how language files looks likes, but here is a reflection part
TranslateLng("Lng.", typeof(Lng));
...
private static void TranslateLng(string parent, Type type)
{
foreach (Type nested in type.GetNestedTypes())
{
string child = string.Format("{0}{1}.", parent, nested.Name);
TranslateLng(child, nested);
foreach (var field in nested.GetFields())
{
string key = child + field.Name;
DefaultAdd(key, (string)field.GetValue(null)); // store value in default language dictionary (if not created yet)
field.SetValue(null, GetValue(key)); // get value for currently selected language
}
}
This system has one problem: all messages are defined in one class, which required manual management (deleting and updating messages when updating code which uses them).
And I was thinking to change manager to register strings dynamically and simplify usage to something like
MessageBox.Show(Lng.Text("Are you sure want to delete?"), ...
So that text is defined right where it used, duplicated text can be handled by manager and so on.
There are however 2 problems:
I will need a complete list of all messages at the end of application run to export complete list of messages (for currently selected language). What if some of Lng.Text() are never called at that run? Is there a way to register them as they are used in code (compile time?)? So that all calls will be registered somehow, even if peace of code is never used.
How to generate key. I could use CallerMemberName, but right key are more useful, as they are telling exact purpose. To example, Lng.Configuration.Appearance.CaptionText. I could call Lng.Text(key, message), but then I have to manage keys, ensure in their uniqueness, which doesn't appeals me.
I recently worked on a project with internationaliztion and we used Resources in con junction with the Sisulizer program with great success. Having the resources solves your key problem as you manually enter the key when you extract the resources. You also get great support from Resharper which makes the whole process a breeze.
Sisulizer is then used to extract resources as well as strings hard-coded in our Win Forms and WPF classes. It can export a CSV which you can give your translators and it also supports pseudo translation, which makes testing such apps very easy as well.
I have a SQL-table with three columns: Id, English and Norwegian. Id is the primary key. In my application I have a flag (EN/NO) to decide which language to use for labels, buttons ++ in the GUI.
The application is now doing a select * everytime the application loads, and the application is looking up all required values at runtime. But instead of loading the whole dataset for every instance, i want to export these values and create a dll so i can store these values locally.
Is there any possibility of creating this in-code so the dll will renew itself with every build? Or do I have to run some external program to dynamically create ex. a .cs code to copy/paste into my class? (I need to be able to re-run the process because rows will be added every time there is a need for a new label/text)
I have so far thought out three solutions on how to structure my export, but no clue on how to export the data:
Preserve the state of the DataTable in a static context and provide help-methods to standardize the way of getting the values out.
Create a class containing each unique ID as method-name, and a parameter to decide which value to return:
public static class Names
{
public static string 12345(string language)
{
switch (language)
{
case "EN":
return "Hello";
case "NO":
return "Hei";
default:
return "Hello";
}
}
}
Create a class containing a searchable list for each language with ID as key and the value (as value)
Why don't you create different resource files for different languages and load the appropriate one depending you the settings. You can do this by using System.Resources.ResourceManager. This article here explains this in detail.
EDIT: Following SO post also discuss this in detail Best practice to make a multi language application in C#/WinForms?
No, i don't like the idea to put internationalization strings into a class library, Why you don't just use the .NET internationalization feature already built in in the framework ?
Resource files are the best solution, not class library for this kind of work ...
I've seen many questions and answers about mapping strings to enums and vice-versa, but how can I map a series of localized strings to enums?
Should I just create an extension method like this that returns the proper string from a resource file? Is there a way to localize attributes (like "Description") that are used in solutions like this?
Which is the preferred solution - extension method or attributes. It seems to me that this isn't the intended purpose of attributes. In fact, now that I think about it, if I were to use an extension method an attribute seems like something I'd use to specify a key in a resource file for the localized string I want to use in place of the enum value.
EDIT - example:
Given the following enum,
public enum TransactionTypes {
Cheque = 1,
BankTransfer = 2,
CreditCard = 3
}
I would like a way to map each type to a localized string. I started off with an extension method for the enum that uses a switch statement and strongly typed references to the resource file.
However, an extension method for every enum doesn't seem like a great solution. I've started following this to create a custom attribute for each enumerated value. The attribute has a base name and key for the resource file containing localized strings. In the above enum, for example, I have this:
...
[EnumResourceAttribute("FinancialTransaction", "Cheque")]
Cheque = 1,
...
Where "FinanacialTransaction" is the resx file and "Cheque" is the string key. I'm trying to create a utility method to which I could pass any value from any enumeration and have it return the localized string representation of that value, assuming the custom attribute is specified. I just can't figure out how to dynamically access a resource file and a key within it.
I would definitely suggest using a resource file, probably with a method (extension or otherwise) to make it simple to get hold of the relevant resource. As the number of languages you support grows, you don't really want the code to be full of text, distracting you from the values themselves.
Likewise translation companies are likely to be geared up to handle resx files - they're not going to want to mess around in your source code, and you shouldn't let them do so anyway :)
Just use resources which are keyed on the name of the enum and the value within it. Straightforward, scales to multiple enums and multiple languages, doesn't clutter up your source code, works well with translation tools, and is basically going along with the flow of i18n within .NET.
EDIT: For mapping the enum values to the resource names, I'd just do something like:
public static string ToResourceName<T>(this T value) where T : struct
{
return typeof(T).Name + "." + value;
}
Then you could do:
string resource = MyEnum.SomeValue.ToResourceName();
Obviously that's performing string concatenation every time - you could cache that if you wanted to, but I wouldn't bother unless you had some indication that it was actually a problem.
That doesn't stop you using the extension method for non-enums, of course. If you want to do that, you need something like Unconstrained Melody.
I continued with the custom attributes and created this utility method:
public static string getEnumResourceString(Enum value)
{
System.Reflection.FieldInfo fi = value.GetType().GetField(value.ToString());
EnumResourceAttribute attr = (EnumResourceAttribute)System.Attribute.GetCustomAttribute(fi, typeof(EnumResourceAttribute));
return (string)HttpContext.GetGlobalResourceObject(attr.BaseName, attr.Key);
}
Our system complexity has risen to the point that we need to make permission names tied to the client from the database more specific. In the client, permissions are referenced from a static class since a lot of client functionality is dependent on the permissions each user has and the roles have a ton of variety. I've referenced this post as an example, but I'm looking for a more specific use case. Take for instance this reference, where PermissionAlpha would be a const string:
return HasPermission(PermissionNames.PermissionAlpha);
Which is great, except now that things are growing more complex the classes are being structured like this:
public static class PermissionNames
{
public static class PermissionAlpha
{
public const string SubPermission = "PermissionAlpha.SubPermission";
}
}
I'm trying to find an easy way to reference PermissionAlpha in this new setup that will act similar to the first declaration above. Would the only way to do this be to resort to pulling the value of the class name like in the example below? I'm trying to keep all the names in one place that can be reference anywhere in the application.
public static class PermissionAlpha
{
public static string Name { get { return typeof(PermissionAlpha).Name; } }
}
** Edit ** - Added missing permission name.
Maybe this would be too big of a change for you with the size of your project, but we have all of our business objects split into partial classes. One is for manual changes and one gets generated. During code-generation, we write the permission keys into the generated side of the partial classes from our "single source of truth". We're using a set of classes as our source of truth and CodeDom to generate, but you could also use a database as your source and use T4, CodeSmith, or others to generate.
Why not create reflectable attribute(s) on the classes in question? That way one can add all the extra information required. I provide a way of divining attributes on my blog article entitled:
C# Using Extended Attribute Information on Objects
HTH