I think I have a solution to this, but is there a better way, or is this going to break on me?
I am constructing a localized web site using global/local resx files. It is a requirement that non-technical users can edit the strings and add new languages through the web app.
This seems easy enough -- I have a form to display strings and the changes are saved with code like this snippet:
string filename = MapPath("App_GlobalResources/strings.hu.resx");
XmlDocument xDoc = new XmlDocument();
XmlNode xNode;
xDoc.Load(filename);
xNode = xDoc.SelectSingleNode("//root/data[#name='PageTitle']/value");
xNode.InnerText = txtNewTitle.Text;
xDoc.Save(filename);
Is this going to cause problems on a busy site? If it causes a momentary delay for recompilation, that's no big deal. And realistically, this form won't see constant, heavy use. What does the community think?
I've used a similar method before for a very basic "CMS". The site wasn't massively used but it didn't cause me any problems.
I don't think changing a resx will cause a recycle.
We did something similar, but used a database to store the user modified values. We then provided a fallback mechanism to serve the overridden value of a localized key.
That said, I think your method should work fine.
Have you considered creating a Resource object? You would need to wrap your settings into a single object that all the client code would use. Something like:
public class GuiResources
{
public string PageTitle
{
get return _pageTitle;
}
// Fired once when the class is first created.
void LoadConfiguration()
{
// Load settings from config section
_pageTitle = // Value from config
}
}
You could make it a singleton or a provider, that way the object is loaded only one time. Also you could make it smart to look at the current thread to get the culture info so you know what language to return.
Then in your web.config file you can create a custom section and set restartOnExternalChanges="true". That way, your app will get the changed when they are made.
Related
I have an old MVC 4 project that uses ASP.NET Resources in Razor views for localization.
I.e in a Razor view you may see
Views/Register.cshtml:
#Resource(() => Local.TermsAndConditions
This will go off and fine
Resources/Register.resx
And find the 'TermsAndConditions' parameter with the correct culture.
Now I don't actually know how all of this works, I've never worked on a multi-lingual application before, but I know that these resx files don't even have the correct information - what actually happens is somehow we fetch these from what looks like a complicated set of SQL tables in the database, using a built ResourceCache class.
I am trying to re-write the front-end using React, but am struggling with how to keep the localization - there are hundreds of entries and I am pulling my hair out a little here.
The only solution I can think of, is to manually find all the keys that would be required to lookup at the top of the view, and send these off to a custom API that will process these values for me, but feel like that may cause a large overhead on my application!
Any ideas?
So I solved this by creating a simple API method
List<string, string> LocaliseStrings (List<string> keys)
{
// get UI culture
// look up translated string
// add to return list in KVP
}
This would be called on every front-end page before anything else, and return the strings necessary
What I'm trying to do here is to allow my user to select a path in a data server on a network, so that I could generate a configuration file.
I hope to be able to replicate the function of OpenFileDialog() on my asp.net page. However this function does not exist on asp.net, and I do know that there is this control in asp.net call FileUpload. But what I required here, is just the path/directory for the folder. I do not require my files to be uploaded.
How can it be done?
Doing this in a web application is tricky. You would have to enumerate the folders on the server that you want to browse (presumably this is the same server that's running the web application), and then present that hierarchy to the user to select a folder. If it's not too big a hierarchy, you could just enumerate the whole bunch up front, and display it in a tree. If it's big for that, you could use an Ajax approach: select the top-level folder, then send an Ajax request to get the next level, and so on.
To enumerate the folders, you'll need to walk the filesystem yourself. See http://msdn.microsoft.com/en-us/library/dd997370(v=vs.100).aspx for one way.
No, there is no inbuilt control for this. It is not a normal requirement cause most site don't let their users see their file structures.
Building a user control that does this will be simple though.
I suggest using a TreeView asp.net control, attached to your datasource where you have listed the files.
This sample on binding a treeview should get you started.
You can populate your data using
var path = Server.MapPath("/");
var dirs = Directory.[EnumerateDirectories][2](path);
var files = Directory.[EnumerateFiles][3](path );
Finally to make it look like a dialog, you could use the jQuery UI dialog component.
The solution I have found is, this is just for anyone looking for answer:-
protected void browse_Click(object sender, EventArgs e)
{
Thread thdSyncRead = new Thread(new ThreadStart(openfolder));
thdSyncRead.SetApartmentState(ApartmentState.STA);
thdSyncRead.Start();
}
public void openfolder()
{
FolderBrowserDialog fbd = new FolderBrowserDialog();
DialogResult result = fbd.ShowDialog();
string selectedfolder = fbd.SelectedPath;
string[] files = Directory.GetFiles(fbd.SelectedPath);
System.Windows.Forms.MessageBox.Show("Files found: " + files.Length.ToString(), "Message");
}
The asp.net site is a completely disconnected environment to your server. As other people have mentioned, to replicate an OpenFileDialog() you will need to look at the folder structure and present this to the user in the web/disconnected environment. In this case the user is abstracted from the actual file system... since this abstraction already occurs, it would be a good time to consider the route you're taking. It might be worth considering that a direct replication of the file system is not required, you could manage the "virtual" folder structure in the database with links/paths to files on disk are maintained there?
The question's simple: how could one use embedded resources in asp.net applications? What are the steps to include a resource in the assembly, and how to reference it? What are the gotchas that could be encountered?
Edit: For a version without referencing Page and ClientScript, see What is the right way to handle Embedded Resources on a Razor View?
After spending a half of a day I've learned these:
to embed a resource one needs to set it's Build Action to Embedded Resource (in VS Solution Explorer rightclick the file -> Properties)
next AsssemblyInfo.vb must be modified to make this resources available for WebResource queries. Add [Assembly: System.Web.UI.WebResource("MyWebResourceProj.Test.css", "text/css")] to AssemblyInfo.vb located in MyProject folder of the project.
The name consists of root namespace/assembly name +'.'+filename. To be 100% sure of the name, use the following code snippet to look it up:
Dim resNames = Assembly.LoadFile("YourDll.dll").GetManifestResourceNames()
Note that the assembly's Root Namespace must be the same as the Assembly Name (this took me about 4 hours to realize. At least with .Net v4 that is the case)
If there are references inside the css ( <%=WebResource("NS.image.jpg")%> ) than pass PerformSubstitution:=true for that css's WebResource attribute.
Referencing the resource can be done with Page.ClientScript.GetWebResourceUrl(GetType(MyWebResourceProj.ConssumingPage), "MyWebResourceProj.Test.css")
Note that instead of GetType(Typename) one could use Me.GetType(), but again, that won't work if the class is inherited, so beware!
Resources:
Debugging ASP.NET 2.0 Web Resources: Decrypting the URL and Getting the Resource Name
Using embedded resources through WebResource.axd is a pain in the neck, as you can see from your own answer. You have to keep assemblyinfo.vb|cs in sync, and it always seems damn near impossible to get all the namespace & assembly names right in all the right places.
When you finally get it to work, your reward is an include script line that that looks like a core memory dump.
I suggest an alternative. Write yourself a very simple web handler (e.g. MyResourceLoader.ashx. Then add a method to your class that simply serves it's own embedded resources, in whatever way you think is meaningful. You can use reflection to get the classes, like WebResource does, or just hardcode whatever you need into your loader, if it's just for a specific purpose. A public method in your class might look like:
public static Stream GetResource(string resourceName) {
// get the resource from myself, which is easy and doesn't require
// anything in assemblyinfo, and return it as a stream. As a bonus,
// you can parse it dynamically or even return things that aren't
// just embedded, but generated completely in code!
}
Or if you decide to make something more general purpose, you can get all fancy and return more data using a class, e.g.
class ResourceInfo
{
public Stream Data;
public string MimeType;
public string FileName;
}
Now you have the ability to serve up your embedded resources any way you want, e.g.
<script language="javascript" src="/MyResourceLoader.ashx/MyControlScript.js">
I think MS made a mess of that WebResource business. Luckily its' pretty straightforward to do your own thing.
I have some HTML text files that I would like to dynamically include in an ASP.NET page.
What is the best way to do this?
My first guess was to create a Literal control on the page and output as follows:
litMyLiteral.Text = System.IO.File.ReadAllText("c:\path\to\file.htm");
Is this a good solution, or is there a better one? For me, performance is a primary goal since these snippet files are output on every page on my site and there's a lot of traffic.
Thanks
Depending on how many files you have or how often you have to read them, I would suggest dynamically loading into the asp.net cache AS you need them and having some type of expiry or the cache.
Write some type of class to wrap you file access and caching implementaton. This code will probably work but don't use it as is, it's just to give you an idea of what I am talking about. Please not all the comments for improvements.
public static class StaticFiles
{
public static string GetFile(string file)
{
// Note filename is the key
if (Cache[file] != null)
{
// Return the cached data, this will be fast.
return Cache[file].ToString();
}
else
{
// Make sure you do some exception checking / validation here for the
// file data and don't hard code the path and make it relative assuming
// it is in your application directory
// Do you file access and store it with some type of expiry
string output = System.IO.File.ReadAllText(string.Format("c:\path\to\{0}", file));
Cache[file] = output;
return output;
}
}
}
You should consider your cache expiry and see what would best suit your data. You could also implement some type of max check to allow a maximum number of cached files so the memory foot print doesn't get to big if you expiry is long. You really need to look at your total number of files and their size and figure out what would best suit your needs.
This would be a very bad solution as far as performance goes. Anything that initiates file IO on every single request is going to come back to haunt you later on if performance is a concern. A much better bet would be to create a cached dictionary that contains all of the content of all the text files that you want to include (assuming that there aren't overly many of them and they aren't overly huge) and storing all of the contents in memory so you don't have to open the file on every request. Other than that, the literal control approach isn't a bad idea, and is probably the simplest approach.
Consider a web application that resizes large tiff files on the fly. Each large tiff file is resized into a jpg thumbnail and larger jpg when the user invokes the operation. The dimensions of these converted files is always the same.
During a code review yesterday, one of the other developers asked me why I set those dimensions in my global.asax like so:
Application["resizedImageWidth"] = int.Parse(ConfigurationManager.AppSettings["ResizedImageWidth"]);
, instead of just looking up the value via a Configuration file during the actual method invocation. I store the widths in the configuration file in the event the end user's after testing the application would like to alter dimensions so I would not have to change code inline.
The reasoning I gave was to prevent the read from the configuration file each time an image was generated, but could not answer if there was similar overhead during a lookup to application level variables. This optimization probably doesn't affect performance to a large scale, but I wanted to know what the community thought the more efficient solution was, i.e. set them during Application start up, or read them on the fly during method invocation.
Generally you should read from the configuration on the fly as you need it. The framework will cache the configuration file, so it is fairly performant. And I believe (Don't quote me) that ASP.Net can monitor and bring in the changes to a configuration file without restarting the application.
I typically like to create a Configuration class which will hide the details of where the value is stored:
public static class Config
{
public static int GetXDimension()
{
...
}
}
This keeps your calling code clean from the configuration code, and if you find I'm wrong, you won't have to change your code everywhere.
You are correct to store this value in an Application object. Indeed, that is the express purpose behind the provision of Application: to hold values that are to be available across all pages and users of the application.
It will indeed be more performant as the application settings are held in memory. It should be pretty obvious that reading from memory is significantly faster than reading from your configuration file (not to mention the lookup via the ConfigurationManager.AppSettings class method and the integer parse).
Any changes to the web.config file will indeed result in a restart of the application. Indeed, this is how I restart my application (when needed) during testing. If you need to change this value "on the fly" you may even want to store this value in a "Configuration" table in your database rather than keeping it in the Web.config file so that changing it doesn't reboot the App.
One trick that I have used is similar to Josh's Config class: I have a "BusinessLogic" class and I place a small handful of settings in static variables of this class so that I can access them in a type-safe manner. It isn't hard and the payoff is pretty obvious in terms of readability.
I would recommend expanding Josh's answer and call it on demand, but then cache it into a private variable:
public static class Config
{
private static int? xDimension;
public static int GetXDimension()
{
if(xDimension == null)
xDimension = int.Parse(ConfigurationManager.AppSettings["ResizedImageWidth"]);
return xDimension.Value;
}
}
or into the Application:
public static class Config
{
public static int GetXDimension()
{
if(Application["xDimension"] == null)
Application["xDimension"] = int.Parse(ConfigurationManager.AppSettings["ResizedImageWidth"]);
return (int)Application["xDimension"];
}
}